Merge commit 'remotes/korg/cupcake'

Conflicts:
	build/sdk.atree
	ide/eclipse/.classpath
	tools/activitycreator/src/com/android/activitycreator/ActivityCreator.java
	tools/eclipse/scripts/create_adt_symlinks.sh
	tools/eclipse/scripts/create_all_symlinks.sh
	tools/eclipse/scripts/create_bridge_symlinks.sh
	tools/eclipse/scripts/create_common_symlinks.sh
	tools/eclipse/scripts/create_ddms_symlinks.sh
	tools/eclipse/scripts/create_editors_symlinks.sh
	tools/eclipse/scripts/create_test_symlinks.sh
	tools/scripts/build.template
This commit is contained in:
Jean-Baptiste Queru
2008-12-19 08:04:59 -08:00
656 changed files with 47129 additions and 9826 deletions

View File

@@ -1,15 +0,0 @@
<?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.pde.core.requiredPlugins">
<accessrules>
<accessrule kind="accessible" pattern="org/eclipse/wst/**/internal/**"/>
</accessrules>
</classpathentry>
<classpathentry kind="lib" path="kxml2-2.3.0.jar"/>
<classpathentry kind="lib" path="layoutlib_api.jar"/>
<classpathentry kind="lib" path="ninepatch.jar"/>
<classpathentry kind="lib" path="layoutlib_utils.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>editors</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@@ -1,56 +0,0 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Android Editors
Bundle-SymbolicName: com.android.ide.eclipse.editors;singleton:=true
Bundle-Version: 0.8.1.qualifier
Bundle-Activator: com.android.ide.eclipse.editors.EditorsPlugin
Bundle-Vendor: The Android Open Source Project
Require-Bundle: com.android.ide.eclipse.common,
org.eclipse.ui,
org.eclipse.core.runtime,
org.eclipse.core.resources,
org.eclipse.ui.editors,
org.eclipse.jface.text,
org.eclipse.ui.ide,
org.eclipse.wst.sse.ui,
org.eclipse.wst.xml.ui,
org.eclipse.wst.xml.core,
org.eclipse.wst.sse.core,
org.eclipse.ui.forms,
org.eclipse.jdt.core,
org.eclipse.ui.browser,
org.eclipse.jdt.ui,
org.eclipse.gef,
org.eclipse.ui.views,
org.eclipse.ui.console
Eclipse-LazyStart: true
Export-Package: com.android.ide.eclipse.editors;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.descriptors;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.layout;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.layout.descriptors;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.layout.parts;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.layout.uimodel;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.manifest;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.manifest.descriptors;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.manifest.model;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.manifest.pages;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.menu;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.menu.descriptors;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.resources;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.resources.configurations;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.resources.descriptors;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.resources.explorer;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.resources.manager;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.resources.manager.files;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.resources.uimodel;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.ui;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.ui.tree;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.uimodel;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.wizards;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.xml;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.xml.descriptors;x-friends:="com.android.ide.eclipse.tests"
Bundle-ClassPath: kxml2-2.3.0.jar,
.,
layoutlib_api.jar,
ninepatch.jar,
layoutlib_utils.jar

View File

@@ -1,224 +0,0 @@
*Eclipse Public License - v 1.0*
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF
THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
*1. DEFINITIONS*
"Contribution" means:
a) in the case of the initial Contributor, the initial code and
documentation distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program originate from and
are distributed by that particular Contributor. A Contribution
'originates' from a Contributor if it was added to the Program by such
Contributor itself or anyone acting on such Contributor's behalf.
Contributions do not include additions to the Program which: (i) are
separate modules of software distributed in conjunction with the Program
under their own license agreement, and (ii) are not derivative works of
the Program.
"Contributor" means any person or entity that distributes the Program.
"Licensed Patents " mean patent claims licensable by a Contributor which
are necessarily infringed by the use or sale of its Contribution alone
or when combined with the Program.
"Program" means the Contributions distributed in accordance with this
Agreement.
"Recipient" means anyone who receives the Program under this Agreement,
including all Contributors.
*2. GRANT OF RIGHTS*
a) Subject to the terms of this Agreement, each Contributor hereby
grants Recipient a non-exclusive, worldwide, royalty-free copyright
license to reproduce, prepare derivative works of, publicly display,
publicly perform, distribute and sublicense the Contribution of such
Contributor, if any, and such derivative works, in source code and
object code form.
b) Subject to the terms of this Agreement, each Contributor hereby
grants Recipient a non-exclusive, worldwide, royalty-free patent license
under Licensed Patents to make, use, sell, offer to sell, import and
otherwise transfer the Contribution of such Contributor, if any, in
source code and object code form. This patent license shall apply to the
combination of the Contribution and the Program if, at the time the
Contribution is added by the Contributor, such addition of the
Contribution causes such combination to be covered by the Licensed
Patents. The patent license shall not apply to any other combinations
which include the Contribution. No hardware per se is licensed hereunder.
c) Recipient understands that although each Contributor grants the
licenses to its Contributions set forth herein, no assurances are
provided by any Contributor that the Program does not infringe the
patent or other intellectual property rights of any other entity. Each
Contributor disclaims any liability to Recipient for claims brought by
any other entity based on infringement of intellectual property rights
or otherwise. As a condition to exercising the rights and licenses
granted hereunder, each Recipient hereby assumes sole responsibility to
secure any other intellectual property rights needed, if any. For
example, if a third party patent license is required to allow Recipient
to distribute the Program, it is Recipient's responsibility to acquire
that license before distributing the Program.
d) Each Contributor represents that to its knowledge it has sufficient
copyright rights in its Contribution, if any, to grant the copyright
license set forth in this Agreement.
*3. REQUIREMENTS*
A Contributor may choose to distribute the Program in object code form
under its own license agreement, provided that:
a) it complies with the terms and conditions of this Agreement; and
b) its license agreement:
i) effectively disclaims on behalf of all Contributors all warranties
and conditions, express and implied, including warranties or conditions
of title and non-infringement, and implied warranties or conditions of
merchantability and fitness for a particular purpose;
ii) effectively excludes on behalf of all Contributors all liability for
damages, including direct, indirect, special, incidental and
consequential damages, such as lost profits;
iii) states that any provisions which differ from this Agreement are
offered by that Contributor alone and not by any other party; and
iv) states that source code for the Program is available from such
Contributor, and informs licensees how to obtain it in a reasonable
manner on or through a medium customarily used for software exchange.
When the Program is made available in source code form:
a) it must be made available under this Agreement; and
b) a copy of this Agreement must be included with each copy of the Program.
Contributors may not remove or alter any copyright notices contained
within the Program.
Each Contributor must identify itself as the originator of its
Contribution, if any, in a manner that reasonably allows subsequent
Recipients to identify the originator of the Contribution.
*4. COMMERCIAL DISTRIBUTION*
Commercial distributors of software may accept certain responsibilities
with respect to end users, business partners and the like. While this
license is intended to facilitate the commercial use of the Program, the
Contributor who includes the Program in a commercial product offering
should do so in a manner which does not create potential liability for
other Contributors. Therefore, if a Contributor includes the Program in
a commercial product offering, such Contributor ("Commercial
Contributor") hereby agrees to defend and indemnify every other
Contributor ("Indemnified Contributor") against any losses, damages and
costs (collectively "Losses") arising from claims, lawsuits and other
legal actions brought by a third party against the Indemnified
Contributor to the extent caused by the acts or omissions of such
Commercial Contributor in connection with its distribution of the
Program in a commercial product offering. The obligations in this
section do not apply to any claims or Losses relating to any actual or
alleged intellectual property infringement. In order to qualify, an
Indemnified Contributor must: a) promptly notify the Commercial
Contributor in writing of such claim, and b) allow the Commercial
Contributor to control, and cooperate with the Commercial Contributor
in, the defense and any related settlement negotiations. The Indemnified
Contributor may participate in any such claim at its own expense.
For example, a Contributor might include the Program in a commercial
product offering, Product X. That Contributor is then a Commercial
Contributor. If that Commercial Contributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Contributor's responsibility
alone. Under this section, the Commercial Contributor would have to
defend claims against the other Contributors related to those
performance claims and warranties, and if a court requires any other
Contributor to pay any damages as a result, the Commercial Contributor
must pay those damages.
*5. NO WARRANTY*
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED
ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES
OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR
A PARTICULAR PURPOSE. Each Recipient is solely responsible for
determining the appropriateness of using and distributing the Program
and assumes all risks associated with its exercise of rights under this
Agreement , including but not limited to the risks and costs of program
errors, compliance with applicable laws, damage to or loss of data,
programs or equipment, and unavailability or interruption of operations.
*6. DISCLAIMER OF LIABILITY*
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*7. GENERAL*
If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further action
by the parties hereto, such provision shall be reformed to the minimum
extent necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against any entity (including
a cross-claim or counterclaim in a lawsuit) alleging that the Program
itself (excluding combinations of the Program with other software or
hardware) infringes such Recipient's patent(s), then such Recipient's
rights granted under Section 2(b) shall terminate as of the date such
litigation is filed.
All Recipient's rights under this Agreement shall terminate if it fails
to comply with any of the material terms or conditions of this Agreement
and does not cure such failure in a reasonable period of time after
becoming aware of such noncompliance. If all Recipient's rights under
this Agreement terminate, Recipient agrees to cease use and distribution
of the Program as soon as reasonably practicable. However, Recipient's
obligations under this Agreement and any licenses granted by Recipient
relating to the Program shall continue and survive.
Everyone is permitted to copy and distribute copies of this Agreement,
but in order to avoid inconsistency the Agreement is copyrighted and may
only be modified in the following manner. The Agreement Steward reserves
the right to publish new versions (including revisions) of this
Agreement from time to time. No one other than the Agreement Steward has
the right to modify this Agreement. The Eclipse Foundation is the
initial Agreement Steward. The Eclipse Foundation may assign the
responsibility to serve as the Agreement Steward to a suitable separate
entity. Each new version of the Agreement will be given a distinguishing
version number. The Program (including Contributions) may always be
distributed subject to the version of the Agreement under which it was
received. In addition, after a new version of the Agreement is
published, Contributor may elect to distribute the Program (including
its Contributions) under the new version. Except as expressly stated in
Sections 2(a) and 2(b) above, Recipient receives no rights or licenses
to the intellectual property of any Contributor under this Agreement,
whether expressly, by implication, estoppel or otherwise. All rights in
the Program not expressly granted under this Agreement are reserved.
This Agreement is governed by the laws of the State of New York and the
intellectual property laws of the United States of America. No party to
this Agreement will bring a legal action under this Agreement more than
one year after the cause of action arose. Each party waives its rights
to a jury trial in any resulting litigation.

View File

@@ -1,10 +0,0 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
plugin.xml,\
icons/,\
layoutlib_api.jar,\
kxml2-2.3.0.jar,\
ninepatch.jar,\
layoutlib_utils.jar

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 B

View File

@@ -1,107 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<plugin>
<extension
point="org.eclipse.ui.editors">
<editor
class="com.android.ide.eclipse.editors.manifest.ManifestEditor"
default="true"
filenames="AndroidManifest.xml"
icon="icons/android.png"
id="com.android.ide.eclipse.editors.manifest.ManifestEditor"
name="Android Manifest Editor">
</editor>
<editor
class="com.android.ide.eclipse.editors.resources.ResourcesEditor"
default="false"
extensions="xml"
icon="icons/android.png"
id="com.android.ide.eclipse.editors.resources.ResourcesEditor"
name="Android Resource Editor">
</editor>
<editor
class="com.android.ide.eclipse.editors.layout.LayoutEditor"
default="false"
extensions="xml"
icon="icons/android.png"
id="com.android.ide.eclipse.editors.layout.LayoutEditor"
matchingStrategy="com.android.ide.eclipse.editors.layout.MatchingStrategy"
name="Android Layout Editor">
</editor>
<editor
class="com.android.ide.eclipse.editors.menu.MenuEditor"
default="false"
extensions="xml"
icon="icons/android.png"
id="com.android.ide.eclipse.editors.menu.MenuEditor"
name="Android Menu Editor">
</editor>
<editor
class="com.android.ide.eclipse.editors.xml.XmlEditor"
default="false"
extensions="xml"
icon="icons/android.png"
id="com.android.ide.eclipse.editors.xml.XmlEditor"
name="Android Xml Resources Editor">
</editor>
</extension>
<extension
point="org.eclipse.wst.sse.ui.editorConfiguration">
<sourceViewerConfiguration
class="com.android.ide.eclipse.editors.manifest.ManifestSourceViewerConfig"
target="com.android.ide.eclipse.editors.manifest.ManifestEditor">
</sourceViewerConfiguration>
<sourceViewerConfiguration
class="com.android.ide.eclipse.editors.resources.ResourcesSourceViewerConfig"
target="com.android.ide.eclipse.editors.resources.ResourcesEditor">
</sourceViewerConfiguration>
<sourceViewerConfiguration
class="com.android.ide.eclipse.editors.layout.LayoutSourceViewerConfig"
target="com.android.ide.eclipse.editors.layout.LayoutEditor">
</sourceViewerConfiguration>
<sourceViewerConfiguration
class="com.android.ide.eclipse.editors.menu.MenuSourceViewerConfig"
target="com.android.ide.eclipse.editors.menu.MenuEditor">
</sourceViewerConfiguration>
<sourceViewerConfiguration
class="com.android.ide.eclipse.editors.xml.XmlSourceViewerConfig"
target="com.android.ide.eclipse.editors.xml.XmlEditor">
</sourceViewerConfiguration>
</extension>
<extension
point="org.eclipse.ui.views">
<view
allowMultiple="false"
category="com.android.ide.eclipse.ddms.views.category"
class="com.android.ide.eclipse.editors.resources.explorer.ResourceExplorerView"
icon="icons/android.png"
id="com.android.ide.eclipse.editors.resources.explorer.ResourceExplorerView"
name="Resource Explorer">
</view>
</extension>
<extension
point="org.eclipse.ui.newWizards">
<wizard
canFinishEarly="false"
category="com.android.ide.eclipse.wizards.category"
class="com.android.ide.eclipse.editors.wizards.NewXmlFileWizard"
finalPerspective="org.eclipse.jdt.ui.JavaPerspective"
hasPages="true"
icon="icons/android.png"
id="com.android.ide.eclipse.editors.wizards.NewXmlFileWizard"
name="Android XML File"
preferredPerspectives="org.eclipse.jdt.ui.JavaPerspective"
project="false">
</wizard>
</extension>
<extension
point="org.eclipse.ui.perspectiveExtensions">
<perspectiveExtension
targetID="org.eclipse.jdt.ui.JavaPerspective">
<newWizardShortcut
id="com.android.ide.eclipse.editors.wizards.NewXmlFileWizard">
</newWizardShortcut>
</perspectiveExtension>
</extension>
</plugin>

View File

@@ -1,733 +0,0 @@
/*
* Copyright (C) 2007 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.editors;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.resources.FrameworkResourceManager;
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.descriptors.SeparatorAttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.TextValueDescriptor;
import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.editors.uimodel.UiFlagAttributeNode;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.regex.Pattern;
/**
* Content Assist Processor for Android XML files
*/
public abstract class AndroidContentAssist implements IContentAssistProcessor {
/** Regexp to detect a full attribute after an element tag.
* <pre>Syntax:
* name = "..." quoted string with all but < and "
* or:
* name = '...' quoted string with all but < and '
* </pre>
*/
private static Pattern sFirstAttribute = Pattern.compile(
"^ *[a-zA-Z_:]+ *= *(?:\"[^<\"]*\"|'[^<']*')"); //$NON-NLS-1$
/** Regexp to detect an element tag name */
private static Pattern sFirstElementWord = Pattern.compile("^[a-zA-Z0-9_:]+"); //$NON-NLS-1$
/** Regexp to detect whitespace */
private static Pattern sWhitespace = Pattern.compile("\\s+"); //$NON-NLS-1$
protected final static String ROOT_ELEMENT = "";
/** Descriptor of the root of the XML hierarchy. This a "fake" ElementDescriptor which
* is used to list all the possible roots given by actual implementations. */
private ElementDescriptor mRootDescriptor;
/**
* Constructor for AndroidContentAssist
* @param rootElementDescriptors The valid root elements of the XML hierarchy
*/
public AndroidContentAssist(ElementDescriptor[] rootElementDescriptors) {
mRootDescriptor = new ElementDescriptor("", rootElementDescriptors);
}
/**
* Returns a list of completion proposals based on the
* specified location within the document that corresponds
* to the current cursor position within the text viewer.
*
* @param viewer the viewer whose document is used to compute the proposals
* @param offset an offset within the document for which completions should be computed
* @return an array of completion proposals or <code>null</code> if no proposals are possible
*
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
*/
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
AndroidEditor editor = getAndroidEditor(viewer);
UiElementNode rootUiNode = editor.getUiRootNode();
Object[] choices = null; /* An array of ElementDescriptor, or AttributeDescriptor
or String or null */
String parent = ""; //$NON-NLS-1$
String wordPrefix = extractElementPrefix(viewer, offset);
char needTag = 0;
boolean isElement = false;
boolean isAttribute = false;
Node currentNode = getNode(viewer, offset);
if (currentNode == null)
return null;
// check to see if we can find a UiElementNode matching this XML node
UiElementNode currentUiNode =
rootUiNode == null ? null : rootUiNode.findXmlNode(currentNode);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
parent = currentNode.getNodeName();
if (wordPrefix.equals(parent)) {
// We are still editing the element's tag name, not the attributes
// (the element's tag name may not even be complete)
isElement = true;
choices = getChoicesForElement(parent, currentNode);
} else {
// We're not editing the current node name, so we might be editing its
// attributes instead...
isAttribute = true;
AttribInfo info = parseAttributeInfo(viewer, offset);
if (info != null) {
// We're editing attributes in an element node (either the attributes' names
// or their values).
choices = getChoicesForAttribute(parent, currentNode, currentUiNode, info);
if (info.correctedPrefix != null) {
wordPrefix = info.correctedPrefix;
}
needTag = info.needTag;
}
}
} else if (currentNode.getNodeType() == Node.TEXT_NODE) {
isElement = true;
// Examine the parent of the text node.
choices = getChoicesForTextNode(currentNode);
}
// Abort if we can't recognize the context or there are no completion choices
if (choices == null || choices.length == 0) return null;
if (isElement) {
// If we found some suggestions, do we need to add an opening "<" bracket
// for the element? We don't if the cursor is right after "<" or "</".
// Per XML Spec, there's no whitespace between "<" or "</" and the tag name.
int offset2 = offset - wordPrefix.length() - 1;
int c1 = extractChar(viewer, offset2);
if (!((c1 == '<') || (c1 == '/' && extractChar(viewer, offset2 - 1) == '<'))) {
needTag = '<';
}
}
// get the selection length
int selectionLength = 0;
ISelection selection = viewer.getSelectionProvider().getSelection();
if (selection instanceof TextSelection) {
TextSelection textSelection = (TextSelection)selection;
selectionLength = textSelection.getLength();
}
return computeProposals(offset, currentNode, choices, wordPrefix, needTag,
isAttribute, selectionLength);
}
/**
* Returns the namespace prefix matching the Android Resource URI.
* If no such declaration is found, returns the default "android" prefix.
*
* @param node The current node. Must not be null.
* @param nsUri The namespace URI of which the prefix is to be found,
* e.g. AndroidConstants.NS_RESOURCES
* @return The first prefix declared or the default "android" prefix.
*/
private String lookupNamespacePrefix(Node node, String nsUri) {
// Note: Node.lookupPrefix is not implemented in wst/xml/core NodeImpl.java
// The following emulates this:
// String prefix = node.lookupPrefix(AndroidConstants.NS_RESOURCES);
if (XmlnsAttributeDescriptor.XMLNS_URI.equals(nsUri)) {
return "xmlns"; //$NON-NLS-1$
}
HashSet<String> visited = new HashSet<String>();
String prefix = null;
for (; prefix == null &&
node != null &&
node.getNodeType() == Node.ELEMENT_NODE;
node = node.getParentNode()) {
NamedNodeMap attrs = node.getAttributes();
for (int n = attrs.getLength() - 1; n >= 0; --n) {
Node attr = attrs.item(n);
if ("xmlns".equals(attr.getPrefix())) { //$NON-NLS-1$
String uri = attr.getNodeValue();
if (AndroidConstants.NS_RESOURCES.equals(uri)) {
return attr.getLocalName();
}
visited.add(uri);
}
}
}
// Use a sensible default prefix if we can't find one.
// We need to make sure the prefix is not one that was declared in the scope
// visited above.
prefix = AndroidConstants.NS_RESOURCES.equals(nsUri) ? "android" : "ns"; //$NON-NLS-1$ //$NON-NLS-2$
String base = prefix;
for (int i = 1; visited.contains(prefix); i++) {
prefix = base + Integer.toString(i);
}
return prefix;
}
/**
* Gets the choices when the user is editing the name of an XML element.
* <p/>
* The user is editing the name of an element (the "parent").
* Find the grand-parent and if one is found, return its children element list.
* The name which is being edited should be one of those.
* <p/>
* Example: <manifest><applic*cursor* => returns the list of all elements that
* can be found under <manifest>, of which <application> is one of the choices.
*
* @return an ElementDescriptor[] or null if no valid element was found.
*/
private Object[] getChoicesForElement(String parent, Node current_node) {
ElementDescriptor grandparent = null;
if (current_node.getParentNode().getNodeType() == Node.ELEMENT_NODE) {
grandparent = getDescriptor(current_node.getParentNode().getNodeName());
} else if (current_node.getParentNode().getNodeType() == Node.DOCUMENT_NODE) {
grandparent = mRootDescriptor;
}
if (grandparent != null) {
for (ElementDescriptor e : grandparent.getChildren()) {
if (e.getXmlName().startsWith(parent)) {
return grandparent.getChildren();
}
}
}
return null;
}
/**
* Gets the choices when the user is editing an XML attribute.
* <p/>
* In input, attrInfo contains details on the analyzed context, namely whether the
* user is editing an attribute value (isInValue) or an attribute name.
* <p/>
* In output, attrInfo also contains two possible new values (this is a hack to circumvent
* the lack of out-parameters in Java):
* - AttribInfo.correctedPrefix if the user has been editing an attribute value and it has
* been detected that what the user typed is different from what extractElementPrefix()
* predicted. This happens because extractElementPrefix() stops when a character that
* cannot be an element name appears whereas parseAttributeInfo() uses a grammar more
* lenient as suitable for attribute values.
* - AttribInfo.needTag will be non-zero if we find that the attribute completion proposal
* must be double-quoted.
* @param currentUiNode
*
* @return an AttributeDescriptor[] if the user is editing an attribute name.
* a String[] if the user is editing an attribute value with some known values,
* or null if nothing is known about the context.
*/
private Object[] getChoicesForAttribute(String parent,
Node currentNode, UiElementNode currentUiNode, AttribInfo attrInfo) {
Object[] choices = null;
if (attrInfo.isInValue) {
// Editing an attribute's value... Get the attribute name and then the
// possible choice for the tuple(parent,attribute)
String value = attrInfo.value;
if (value.startsWith("'") || value.startsWith("\"")) { //$NON-NLS-1$ //$NON-NLS-2$
value = value.substring(1);
// The prefix that was found at the beginning only scan for characters
// valid of tag name. We now know the real prefix for this attribute's
// value, which is needed to generate the completion choices below.
attrInfo.correctedPrefix = value;
} else {
attrInfo.needTag = '"';
}
if (currentUiNode != null) {
// look for an UI attribute matching the current attribute name
String attrName = attrInfo.name;
UiAttributeNode currAttrNode = null;
for (UiAttributeNode attrNode : currentUiNode.getUiAttributes()) {
if (attrNode.getDescriptor().getXmlLocalName().equals(attrName)) {
currAttrNode = attrNode;
break;
}
}
if (currAttrNode != null) {
choices = currAttrNode.getPossibleValues();
if (currAttrNode instanceof UiFlagAttributeNode) {
// A "flag" can consist of several values separated by "or" (|).
// If the correct prefix contains such a pipe character, we change
// it so that only the currently edited value is completed.
int pos = value.indexOf('|');
if (pos >= 0) {
attrInfo.correctedPrefix = value = value.substring(pos + 1);
attrInfo.needTag = 0;
}
}
}
}
if (choices == null) {
// fallback on the older descriptor-only based lookup.
// in order to properly handle the special case of the name attribute in
// the action tag, we need the grandparent of the action node, to know
// what type of actions we need.
// e.g. activity -> intent-filter -> action[@name]
String greatGrandParentName = null;
Node grandParent = currentNode.getParentNode();
if (grandParent != null) {
Node greatGrandParent = grandParent.getParentNode();
if (greatGrandParent != null) {
greatGrandParentName = greatGrandParent.getLocalName();
}
}
choices = FrameworkResourceManager.getInstance().getValues(
parent, attrInfo.name, greatGrandParentName);
}
} else {
// Editing an attribute's name... Get attributes valid for the parent node.
if (currentUiNode != null) {
choices = currentUiNode.getAttributeDescriptors();
} else {
ElementDescriptor parent_desc = getDescriptor(parent);
choices = parent_desc.getAttributes();
}
}
return choices;
}
/**
* Gets the choices when the user is editing an XML text node.
* <p/>
* This means the user is editing outside of any XML element or attribute.
* Simply return the list of XML elements that can be present there, based on the
* parent of the current node.
*
* @return An ElementDescriptor[] or null.
*/
private Object[] getChoicesForTextNode(Node currentNode) {
Object[] choices = null;
String parent;
Node parent_node = currentNode.getParentNode();
if (parent_node.getNodeType() == Node.ELEMENT_NODE) {
// We're editing a text node which parent is an element node. Limit
// content assist to elements valid for the parent.
parent = parent_node.getNodeName();
ElementDescriptor desc = getDescriptor(parent);
if (desc != null) {
choices = desc.getChildren();
}
} else if (parent_node.getNodeType() == Node.DOCUMENT_NODE) {
// We're editing a text node at the first level (i.e. root node).
// Limit content assist to the only valid root elements.
choices = mRootDescriptor.getChildren();
}
return choices;
}
/**
* Given a list of choices found, generates the proposals to be displayed to the user.
* <p/>
* Choices is an object array. Items of the array can be:
* - ElementDescriptor: a possible element descriptor which XML name should be completed.
* - AttributeDescriptor: a possible attribute descriptor which XML name should be completed.
* - String: string values to display as-is to the user. Typically those are possible
* values for a given attribute.
*
* @return The ICompletionProposal[] to display to the user.
*/
private ICompletionProposal[] computeProposals(int offset, Node currentNode,
Object[] choices, String wordPrefix, char need_tag,
boolean is_attribute, int selectionLength) {
ArrayList<CompletionProposal> proposals = new ArrayList<CompletionProposal>();
HashMap<String, String> nsUriMap = new HashMap<String, String>();
for (Object choice : choices) {
String keyword = null;
String nsPrefix = null;
Image icon = null;
String tooltip = null;
if (choice instanceof ElementDescriptor) {
keyword = ((ElementDescriptor)choice).getXmlName();
icon = ((ElementDescriptor)choice).getIcon();
tooltip = DescriptorsUtils.formatTooltip(((ElementDescriptor)choice).getTooltip());
} else if (choice instanceof TextValueDescriptor) {
continue; // Value nodes are not part of the completion choices
} else if (choice instanceof SeparatorAttributeDescriptor) {
continue; // not real attribute descriptors
} else if (choice instanceof AttributeDescriptor) {
keyword = ((AttributeDescriptor)choice).getXmlLocalName();
icon = ((AttributeDescriptor)choice).getIcon();
if (choice instanceof TextAttributeDescriptor) {
tooltip = ((TextAttributeDescriptor) choice).getTooltip();
}
String nsUri = ((AttributeDescriptor)choice).getNamespaceUri();
nsPrefix = nsUriMap.get(nsUri);
if (nsPrefix == null) {
nsPrefix = lookupNamespacePrefix(currentNode, nsUri);
nsUriMap.put(nsUri, nsPrefix);
}
if (nsPrefix != null) {
nsPrefix += ":"; //$NON-NLS-1$
}
} else if (choice instanceof String) {
keyword = (String) choice;
} else {
continue; // discard unknown choice
}
if (keyword.startsWith(wordPrefix) ||
(nsPrefix != null && keyword.startsWith(nsPrefix))) {
if (nsPrefix != null) {
keyword = nsPrefix + keyword;
}
String end_tag = ""; //$NON-NLS-1$
if (need_tag != 0) {
if (need_tag == '"') {
keyword = need_tag + keyword;
end_tag = String.valueOf(need_tag);
} else if (need_tag == '<') {
if (elementCanHaveChildren(choice)) {
end_tag = String.format("></%1$s>", keyword); //$NON-NLS-1$
keyword = need_tag + keyword;
} else {
keyword = need_tag + keyword;
end_tag = "/>"; //$NON-NLS-1$
}
}
}
CompletionProposal proposal = new CompletionProposal(
keyword + end_tag, // String replacementString
offset - wordPrefix.length(), // int replacementOffset
wordPrefix.length() + selectionLength, // int replacementLength
keyword.length(), // int cursorPosition (rel. to rplcmntOffset)
icon, // Image image
null, // String displayString
null, // IContextInformation contextInformation
tooltip // String additionalProposalInfo
);
proposals.add(proposal);
}
}
return proposals.toArray(new ICompletionProposal[proposals.size()]);
}
/**
* Indicates whether this descriptor describes an element that can potentially
* have children (either sub-elements or text value). If an element can have children,
* we want to explicitly write an opening and a separate closing tag.
* <p/>
* Elements can have children if the descriptor has children element descriptors
* or if one of the attributes is a TextValueDescriptor.
*
* @param descriptor An ElementDescriptor or an AttributeDescriptor
* @return True if the descriptor is an ElementDescriptor that can have children or a text value
*/
private boolean elementCanHaveChildren(Object descriptor) {
if (descriptor instanceof ElementDescriptor) {
ElementDescriptor desc = (ElementDescriptor) descriptor;
if (desc.hasChildren()) {
return true;
}
for (AttributeDescriptor attr_desc : desc.getAttributes()) {
if (attr_desc instanceof TextValueDescriptor) {
return true;
}
}
}
return false;
}
/**
* Returns the element descriptor matching a given XML node name or null if it can't be
* found.
* <p/>
* This is simplistic; ideally we should consider the parent's chain to make sure we
* can differentiate between different hierarchy trees. Right now the first match found
* is returned.
*/
private ElementDescriptor getDescriptor(String nodeName) {
return mRootDescriptor.findChildrenDescriptor(nodeName, true /* recursive */);
}
public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
return null;
}
/**
* Returns the characters which when entered by the user should
* automatically trigger the presentation of possible completions.
*
* In our case, we auto-activate on opening tags and attributes namespace.
*
* @return the auto activation characters for completion proposal or <code>null</code>
* if no auto activation is desired
*/
public char[] getCompletionProposalAutoActivationCharacters() {
return new char[]{ '<', ':', '=' };
}
public char[] getContextInformationAutoActivationCharacters() {
return null;
}
public IContextInformationValidator getContextInformationValidator() {
return null;
}
public String getErrorMessage() {
return null;
}
/**
* Heuristically extracts the prefix used for determining template relevance
* from the viewer's document. The default implementation returns the String from
* offset backwards that forms a potential XML element name.
*
* Code extracted from org.eclipse.jface.text.templatesTemplateCompletionProcessor
* and adapted to our needs.
*
* @param viewer the viewer
* @param offset offset into document
* @return the prefix to consider
*/
protected String extractElementPrefix(ITextViewer viewer, int offset) {
int i = offset;
IDocument document = viewer.getDocument();
if (i > document.getLength()) return ""; //$NON-NLS-1$
try {
for (; i > 0; --i) {
char ch = document.getChar(i - 1);
// accepted characters are a-z and : (for attributes' namespace)
if (ch != ':' && (ch < 'a' || ch > 'z')) break;
}
return document.get(i, offset - i);
} catch (BadLocationException e) {
return ""; //$NON-NLS-1$
}
}
/**
* Extracts the character at the given offset.
* Returns 0 if the offset is invalid.
*/
protected char extractChar(ITextViewer viewer, int offset) {
IDocument document = viewer.getDocument();
if (offset > document.getLength()) return 0;
try {
return document.getChar(offset);
} catch (BadLocationException e) {
return 0;
}
}
/**
* Information about the current edit of an attribute as reported by parseAttributeInfo.
*/
private class AttribInfo {
/** True if the cursor is located in an attribute's value, false if in an attribute name */
public boolean isInValue = false;
/** The attribute name. Null when not set. */
public String name = null;
/** The attribute value. Null when not set. The value *may* start with a quote
* (' or "), in which case we know we don't need to quote the string for the user */
public String value = null;
/** String typed by the user so far (i.e. right before requesting code completion),
* which will be corrected if we find a possible completion for an attribute value.
* See the long comment in getChoicesForAttribute(). */
public String correctedPrefix = null;
/** Non-zero if an attribute value need a start/end tag (i.e. quotes or brackets) */
public char needTag = 0;
}
/**
* Try to guess if the cursor is editing an element's name or an attribute following an
* element. If it's an attribute, try to find if an attribute name is being defined or
* its value.
* <br/>
* This is currently *only* called when we know the cursor is after a complete element
* tag name, so it should never return null.
* <br/>
* Reference for XML syntax: http://www.w3.org/TR/2006/REC-xml-20060816/#sec-starttags
* <br/>
* @return An AttribInfo describing which attribute is being edited or null if the cursor is
* not editing an attribute (in which case it must be an element's name).
*/
private AttribInfo parseAttributeInfo(ITextViewer viewer, int offset) {
AttribInfo info = new AttribInfo();
IDocument document = viewer.getDocument();
int n = document.getLength();
if (offset <= n) {
try {
n = offset;
for (;offset > 0; --offset) {
char ch = document.getChar(offset - 1);
if (ch == '<') break;
}
// text will contain the full string of the current element,
// i.e. whatever is after the "<" to the current cursor
String text = document.get(offset, n - offset);
// Normalize whitespace to single spaces
text = sWhitespace.matcher(text).replaceAll(" "); //$NON-NLS-1$
// Remove the leading element name. By spec, it must be after the < without
// any whitespace. If there's nothing left, no attribute has been defined yet.
// Be sure to keep any whitespace after the initial word if any, as it matters.
text = sFirstElementWord.matcher(text).replaceFirst(""); //$NON-NLS-1$
// There MUST be space after the element name. If not, the cursor is still
// defining the element name.
if (!text.startsWith(" ")) { //$NON-NLS-1$
return null;
}
// Remove full attributes:
// Syntax:
// name = "..." quoted string with all but < and "
// or:
// name = '...' quoted string with all but < and '
String temp;
do {
temp = text;
text = sFirstAttribute.matcher(temp).replaceFirst(""); //$NON-NLS-1$
} while(!temp.equals(text));
// Now we're left with 3 cases:
// - nothing: either there is no attribute definition or the cursor located after
// a completed attribute definition.
// - a string with no =: the user is writing an attribute name. This case can be
// merged with the previous one.
// - string with an = sign, optionally followed by a quote (' or "): the user is
// writing the value of the attribute.
int pos_equal = text.indexOf('=');
if (pos_equal == -1) {
info.isInValue = false;
info.name = text.trim();
} else {
info.isInValue = true;
info.name = text.substring(0, pos_equal).trim();
info.value = text.substring(pos_equal + 1).trim();
}
return info;
} catch (BadLocationException e) {
// pass
}
}
return null;
}
/**
* Returns the XML DOM node corresponding to the given offset of the given document.
*/
protected Node getNode(ITextViewer viewer, int offset) {
Node node = null;
try {
IModelManager mm = StructuredModelManager.getModelManager();
if (mm != null) {
IStructuredModel model = mm.getExistingModelForRead(viewer.getDocument());
if (model != null) {
for(; offset >= 0 && node == null; --offset) {
node = (Node) model.getIndexedRegion(offset);
}
}
}
} catch (Exception e) {
// Ignore exceptions.
}
return node;
}
/**
* Returns the active {@link AndroidEditor} matching this source viewer.
*/
private AndroidEditor getAndroidEditor(ITextViewer viewer) {
IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (wwin != null) {
IWorkbenchPage page = wwin.getActivePage();
if (page != null) {
IEditorPart editor = page.getActiveEditor();
if (editor instanceof AndroidEditor) {
ISourceViewer ssviewer = ((AndroidEditor) editor).getStructuredSourceViewer();
if (ssviewer == viewer) {
return (AndroidEditor) editor;
}
}
}
}
return null;
}
}

View File

@@ -1,655 +0,0 @@
/*
* Copyright (C) 2007 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.editors;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.forms.editor.IFormPage;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.events.IHyperlinkListener;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.internal.browser.WorkbenchBrowserSupport;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.MultiPageEditorPart;
import org.eclipse.ui.part.WorkbenchPart;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IModelStateListener;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.ui.StructuredTextEditor;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.w3c.dom.Document;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Multi-page form editor for Android XML files.
* <p/>
* It is designed to work with a {@link StructuredTextEditor} that will display an XML file.
* <br/>
* Derived classes must implement createFormPages to create the forms before the
* source editor. This can be a no-op if desired.
*/
public abstract class AndroidEditor extends FormEditor implements IResourceChangeListener {
/** Preference name for the current page of this file */
private static final String PREF_CURRENT_PAGE = "_current_page";
/** Id string used to create the Android SDK browser */
private static String BROWSER_ID = "android"; // $NON-NLS-1$
/** Page id of the XML source editor, used for switching tabs programmatically */
public final static String TEXT_EDITOR_ID = "editor_part"; //$NON-NLS-1$
/** Width hint for text fields. Helps the grid layout resize properly on smaller screens */
public static final int TEXT_WIDTH_HINT = 50;
/** Page index of the text editor (always the last page) */
private int mTextPageIndex;
/** The text editor */
private StructuredTextEditor mEditor;
/** Listener for the XML model from the StructuredEditor */
private XmlModelStateListener mXmlModelStateListener;
/**
* Creates a form editor.
*/
public AndroidEditor() {
super();
ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
}
// ---- Abstract Methods ----
/**
* Returns the root node of the UI element hierarchy manipulated by the current
* UI node editor.
*/
abstract public UiElementNode getUiRootNode();
/**
* Creates the various form pages.
* <p/>
* Derived classes must implement this to add their own specific tabs.
*/
abstract protected void createFormPages();
/**
* Subclasses should override this method to process the new XML Model, which XML
* root node is given.
*
* The base implementation is empty.
*
* @param xml_doc The XML document, if available, or null if none exists.
*/
protected void xmlModelChanged(Document xml_doc) {
// pass
}
// ---- Base Class Overrides, Interfaces Implemented ----
/**
* Creates the pages of the multi-page editor.
*/
@Override
protected void addPages() {
createAndroidPages();
selectDefaultPage(null /* defaultPageId */);
}
/**
* Creates the page for the Android Editors
*/
protected void createAndroidPages() {
createFormPages();
createTextEditor();
}
/**
* Selects the default active page.
* @param defaultPageId the id of the page to show. If <code>null</code> the editor attempts to
* find the default page in the properties of the {@link IResource} object being edited.
*/
protected void selectDefaultPage(String defaultPageId) {
if (defaultPageId == null) {
if (getEditorInput() instanceof IFileEditorInput) {
IFile file = ((IFileEditorInput) getEditorInput()).getFile();
QualifiedName qname = new QualifiedName(AndroidConstants.EDITORS_PLUGIN_ID,
getClass().getSimpleName() + PREF_CURRENT_PAGE);
String pageId;
try {
pageId = file.getPersistentProperty(qname);
if (pageId != null) {
defaultPageId = pageId;
}
} catch (CoreException e) {
// ignored
}
}
}
if (defaultPageId != null) {
try {
setActivePage(Integer.parseInt(defaultPageId));
} catch (Exception e) {
// We can get NumberFormatException from parseInt but also
// AssertionError from setActivePage when the index is out of bounds.
// Generally speaking we just want to ignore any exception and fall back on the
// first page rather than crash the editor load. Logging the error is enough.
EditorsPlugin.log(e, "Selecting page '%s' in AndroidEditor failed", defaultPageId);
}
}
}
/**
* Removes all the pages from the editor.
*/
protected void removePages() {
int count = getPageCount();
for (int i = count - 1 ; i >= 0 ; i--) {
removePage(i);
}
}
/**
* Overrides the parent's setActivePage to be able to switch to the xml editor.
*
* If the special pageId TEXT_EDITOR_ID is given, switches to the mTextPageIndex page.
* This is needed because the editor doesn't actually derive from IFormPage and thus
* doesn't have the get-by-page-id method. In this case, the method returns null since
* IEditorPart does not implement IFormPage.
*/
@Override
public IFormPage setActivePage(String pageId) {
if (pageId.equals(TEXT_EDITOR_ID)) {
super.setActivePage(mTextPageIndex);
return null;
} else {
return super.setActivePage(pageId);
}
}
/**
* Notifies this multi-page editor that the page with the given id has been
* activated. This method is called when the user selects a different tab.
*
* @see MultiPageEditorPart#pageChange(int)
*/
@Override
protected void pageChange(int newPageIndex) {
super.pageChange(newPageIndex);
if (getEditorInput() instanceof IFileEditorInput) {
IFile file = ((IFileEditorInput) getEditorInput()).getFile();
QualifiedName qname = new QualifiedName(AndroidConstants.EDITORS_PLUGIN_ID,
getClass().getSimpleName() + PREF_CURRENT_PAGE);
try {
file.setPersistentProperty(qname, Integer.toString(newPageIndex));
} catch (CoreException e) {
// ignore
}
}
}
/**
* Notifies this listener that some resource changes
* are happening, or have already happened.
*
* Closes all project files on project close.
* @see IResourceChangeListener
*/
public void resourceChanged(final IResourceChangeEvent event) {
if (event.getType() == IResourceChangeEvent.PRE_CLOSE) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
IWorkbenchPage[] pages = getSite().getWorkbenchWindow()
.getPages();
for (int i = 0; i < pages.length; i++) {
if (((FileEditorInput)mEditor.getEditorInput())
.getFile().getProject().equals(
event.getResource())) {
IEditorPart editorPart = pages[i].findEditor(mEditor
.getEditorInput());
pages[i].closeEditor(editorPart, true);
}
}
}
});
}
}
/**
* Initializes the editor part with a site and input.
* <p/>
* Checks that the input is an instance of {@link IFileEditorInput}.
*
* @see FormEditor
*/
@Override
public void init(IEditorSite site, IEditorInput editorInput) throws PartInitException {
if (!(editorInput instanceof IFileEditorInput))
throw new PartInitException("Invalid Input: Must be IFileEditorInput");
super.init(site, editorInput);
}
/**
* Removes attached listeners.
*
* @see WorkbenchPart
*/
@Override
public void dispose() {
IStructuredModel xml_model = getModelForRead();
if (xml_model != null) {
try {
if (mXmlModelStateListener != null) {
xml_model.removeModelStateListener(mXmlModelStateListener);
}
} finally {
xml_model.releaseFromRead();
}
}
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
super.dispose();
}
/**
* Commit all dirty pages then saves the contents of the text editor.
* <p/>
* This works by committing all data to the XML model and then
* asking the Structured XML Editor to save the XML.
*
* @see IEditorPart
*/
@Override
public void doSave(IProgressMonitor monitor) {
commitPages(true /* onSave */);
// The actual "save" operation is done by the Structured XML Editor
getEditor(mTextPageIndex).doSave(monitor);
}
/* (non-Javadoc)
* Saves the contents of this editor to another object.
* <p>
* Subclasses must override this method to implement the open-save-close lifecycle
* for an editor. For greater details, see <code>IEditorPart</code>
* </p>
*
* @see IEditorPart
*/
@Override
public void doSaveAs() {
commitPages(true /* onSave */);
IEditorPart editor = getEditor(mTextPageIndex);
editor.doSaveAs();
setPageText(mTextPageIndex, editor.getTitle());
setInput(editor.getEditorInput());
}
/**
* Commits all dirty pages in the editor. This method should
* be called as a first step of a 'save' operation.
* <p/>
* This is the same implementation as in {@link FormEditor}
* except it fixes two bugs: a cast to IFormPage is done
* from page.get(i) <em>before</em> being tested with instanceof.
* Another bug is that the last page might be a null pointer.
* <p/>
* The incorrect casting makes the original implementation crash due
* to our {@link StructuredTextEditor} not being an {@link IFormPage}
* so we have to override and duplicate to fix it.
*
* @param onSave <code>true</code> if commit is performed as part
* of the 'save' operation, <code>false</code> otherwise.
* @since 3.3
*/
@Override
public void commitPages(boolean onSave) {
if (pages != null) {
for (int i = 0; i < pages.size(); i++) {
Object page = pages.get(i);
if (page != null && page instanceof IFormPage) {
IFormPage form_page = (IFormPage) page;
IManagedForm managed_form = form_page.getManagedForm();
if (managed_form != null && managed_form.isDirty()) {
managed_form.commit(onSave);
}
}
}
}
}
/* (non-Javadoc)
* Returns whether the "save as" operation is supported by this editor.
* <p>
* Subclasses must override this method to implement the open-save-close lifecycle
* for an editor. For greater details, see <code>IEditorPart</code>
* </p>
*
* @see IEditorPart
*/
@Override
public boolean isSaveAsAllowed() {
return false;
}
// ---- Local methods ----
/**
* Helper method that creates a new hyper-link Listener.
* Used by derived classes which need active links in {@link FormText}.
* <p/>
* This link listener handles two kinds of URLs:
* <ul>
* <li> Links starting with "http" are simply sent to a local browser.
* <li> Links starting with "file:/" are simply sent to a local browser.
* <li> Links starting with "page:" are expected to be an editor page id to switch to.
* <li> Other links are ignored.
* </ul>
*
* @return A new hyper-link listener for FormText to use.
*/
public final IHyperlinkListener createHyperlinkListener() {
return new HyperlinkAdapter() {
/**
* Switch to the page corresponding to the link that has just been clicked.
* For this purpose, the HREF of the &lt;a&gt; tags above is the page ID to switch to.
*/
@Override
public void linkActivated(HyperlinkEvent e) {
super.linkActivated(e);
String link = e.data.toString();
if (link.startsWith("http") || //$NON-NLS-1$
link.startsWith("file:/")) { //$NON-NLS-1$
openLinkInBrowser(link);
} else if (link.startsWith("page:")) { //$NON-NLS-1$
// Switch to an internal page
setActivePage(link.substring(5 /* strlen("page:") */));
}
}
};
}
/**
* Open the http link into a browser
*
* @param link The URL to open in a browser
*/
private void openLinkInBrowser(String link) {
try {
IWorkbenchBrowserSupport wbs = WorkbenchBrowserSupport.getInstance();
wbs.createBrowser(BROWSER_ID).openURL(new URL(link));
} catch (PartInitException e1) {
// pass
} catch (MalformedURLException e1) {
// pass
}
}
/**
* Creates the XML source editor.
* <p/>
* Memorizes the index page of the source editor (it's always the last page, but the number
* of pages before can change.)
* <br/>
* Retrieves the underlying XML model from the StructuredEditor and attaches a listener to it.
* Finally triggers modelChanged() on the model listener -- derived classes can use this
* to initialize the model the first time.
* <p/>
* Called only once <em>after</em> createFormPages.
*/
private void createTextEditor() {
try {
mEditor = new StructuredTextEditor();
int index = addPage(mEditor, getEditorInput());
mTextPageIndex = index;
setPageText(index, mEditor.getTitle());
if (!(mEditor.getTextViewer().getDocument() instanceof IStructuredDocument)) {
Status status = new Status(IStatus.ERROR, AndroidConstants.EDITORS_PLUGIN_ID,
"Error opening the Android XML editor. Is the document an XML file?");
throw new RuntimeException("Android XML Editor Error", new CoreException(status));
}
IStructuredModel xml_model = getModelForRead();
if (xml_model != null) {
try {
mXmlModelStateListener = new XmlModelStateListener();
xml_model.addModelStateListener(mXmlModelStateListener);
mXmlModelStateListener.modelChanged(xml_model);
} finally {
xml_model.releaseFromRead();
}
}
} catch (PartInitException e) {
ErrorDialog.openError(getSite().getShell(),
"Android XML Editor Error", null, e.getStatus());
}
}
/**
* Returns the ISourceViewer associated with the Structured Text editor.
*/
public final ISourceViewer getStructuredSourceViewer() {
if (mEditor != null) {
// We can't access mEditor.getSourceViewer() because it is protected,
// however getTextViewer simply returns the SourceViewer casted, so we
// can use it instead.
return mEditor.getTextViewer();
}
return null;
}
/**
* Returns the {@link IStructuredDocument} used by the StructuredTextEditor (aka Source
* Editor) or null if not available.
*/
public final IStructuredDocument getStructuredDocument() {
if (mEditor != null && mEditor.getTextViewer() != null) {
return (IStructuredDocument) mEditor.getTextViewer().getDocument();
}
return null;
}
/**
* Returns a version of the model that has been shared for read.
* <p/>
* Callers <em>must</em> call model.releaseFromRead() when done, typically
* in a try..finally clause.
*
* @return The model for the XML document or null if cannot be obtained from the editor
*/
public final IStructuredModel getModelForRead() {
IStructuredDocument document = getStructuredDocument();
if (document != null) {
IModelManager mm = StructuredModelManager.getModelManager();
if (mm != null) {
return mm.getModelForRead(document);
}
}
return null;
}
/**
* Returns a version of the model that has been shared for edit.
* <p/>
* Callers <em>must</em> call model.releaseFromEdit() when done, typically
* in a try..finally clause.
*
* @return The model for the XML document or null if cannot be obtained from the editor
*/
public final IStructuredModel getModelForEdit() {
IStructuredDocument document = getStructuredDocument();
if (document != null) {
IModelManager mm = StructuredModelManager.getModelManager();
if (mm != null) {
return mm.getModelForEdit(document);
}
}
return null;
}
/**
* Helper class to perform edits on the XML model whilst making sure the
* model has been prepared to be changed.
*
* It first gets a model for edition, then calls aboutToChangeModel, then performs the
* requested action and finally calls changedModel and releaseFromEdit.
*
* The method is synchronous. As soon as the changedModel method is called, XML model
* listeners will be triggered.
*
* @param edit_action Something that will change
*/
public final void editXmlModel(Runnable edit_action) {
IStructuredModel model = getModelForEdit();
try {
model.aboutToChangeModel();
edit_action.run();
} finally {
// Notify the model we're done modifying it. This must *always* be executed.
model.changedModel();
model.releaseFromEdit();
}
}
/**
* Returns the XML {@link Document} or null if we can't get it
*/
protected final Document getXmlDocument(IStructuredModel model) {
if (model == null) {
EditorsPlugin.log(IStatus.WARNING, "Android Editor: No XML model for root node."); //$NON-NLS-1$
return null;
}
if (model instanceof IDOMModel) {
IDOMModel dom_model = (IDOMModel) model;
return dom_model.getDocument();
}
return null;
}
/**
* Listen to changes in the underlying XML model in the structured editor.
*/
private class XmlModelStateListener implements IModelStateListener {
/**
* A model is about to be changed. This typically is initiated by one
* client of the model, to signal a large change and/or a change to the
* model's ID or base Location. A typical use might be if a client might
* want to suspend processing until all changes have been made.
* <p/>
* This AndroidEditor implementation of IModelChangedListener is empty.
*/
public void modelAboutToBeChanged(IStructuredModel model) {
// pass
}
/**
* Signals that the changes foretold by modelAboutToBeChanged have been
* made. A typical use might be to refresh, or to resume processing that
* was suspended as a result of modelAboutToBeChanged.
* <p/>
* This AndroidEditor implementation calls the xmlModelChanged callback.
*/
public void modelChanged(IStructuredModel model) {
xmlModelChanged(getXmlDocument(model));
}
/**
* Notifies that a model's dirty state has changed, and passes that state
* in isDirty. A model becomes dirty when any change is made, and becomes
* not-dirty when the model is saved.
* <p/>
* This AndroidEditor implementation of IModelChangedListener is empty.
*/
public void modelDirtyStateChanged(IStructuredModel model, boolean isDirty) {
// pass
}
/**
* A modelDeleted means the underlying resource has been deleted. The
* model itself is not removed from model management until all have
* released it. Note: baseLocation is not (necessarily) changed in this
* event, but may not be accurate.
* <p/>
* This AndroidEditor implementation of IModelChangedListener is empty.
*/
public void modelResourceDeleted(IStructuredModel model) {
// pass
}
/**
* A model has been renamed or copied (as in saveAs..). In the renamed
* case, the two paramenters are the same instance, and only contain the
* new info for id and base location.
* <p/>
* This AndroidEditor implementation of IModelChangedListener is empty.
*/
public void modelResourceMoved(IStructuredModel oldModel, IStructuredModel newModel) {
// pass
}
/**
* This AndroidEditor implementation of IModelChangedListener is empty.
*/
public void modelAboutToBeReinitialized(IStructuredModel structuredModel) {
// pass
}
/**
* This AndroidEditor implementation of IModelChangedListener is empty.
*/
public void modelReinitialized(IStructuredModel structuredModel) {
// pass
}
}
}

View File

@@ -1,116 +0,0 @@
/*
* Copyright (C) 2007 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.editors;
import org.eclipse.jface.text.IAutoEditStrategy;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContentAssistant;
import org.eclipse.jface.text.formatter.IContentFormatter;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.viewers.IInputProvider;
import org.eclipse.wst.sse.core.text.IStructuredPartitions;
import org.eclipse.wst.xml.core.text.IXMLPartitions;
import org.eclipse.wst.xml.ui.StructuredTextViewerConfigurationXML;
import java.util.ArrayList;
/**
* Base Source Viewer Configuration for Android resources.
*/
public class AndroidSourceViewerConfig extends StructuredTextViewerConfigurationXML {
/** Content Assist Processor to use for all handled partitions. */
private IContentAssistProcessor mProcessor;
public AndroidSourceViewerConfig(IContentAssistProcessor processor) {
super();
mProcessor = processor;
}
@Override
public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) {
return super.getContentAssistant(sourceViewer);
}
/**
* Returns the content assist processors that will be used for content
* assist in the given source viewer and for the given partition type.
*
* @param sourceViewer the source viewer to be configured by this
* configuration
* @param partitionType the partition type for which the content assist
* processors are applicable
* @return IContentAssistProcessors or null if should not be supported
*/
@Override
protected IContentAssistProcessor[] getContentAssistProcessors(
ISourceViewer sourceViewer, String partitionType) {
ArrayList<IContentAssistProcessor> processors = new ArrayList<IContentAssistProcessor>();
if (partitionType == IStructuredPartitions.UNKNOWN_PARTITION ||
partitionType == IStructuredPartitions.DEFAULT_PARTITION ||
partitionType == IXMLPartitions.XML_DEFAULT) {
if (sourceViewer instanceof IInputProvider) {
IInputProvider input = (IInputProvider) sourceViewer;
Object a = input.getInput();
if (a != null)
a.toString();
}
IDocument doc = sourceViewer.getDocument();
if (doc != null)
doc.toString();
processors.add(mProcessor);
}
IContentAssistProcessor[] others = super.getContentAssistProcessors(sourceViewer,
partitionType);
if (others != null && others.length > 0) {
for (IContentAssistProcessor p : others) {
processors.add(p);
}
}
if (processors.size() > 0) {
return processors.toArray(new IContentAssistProcessor[processors.size()]);
} else {
return null;
}
}
@Override
public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) {
// TODO text hover for android xml
return super.getTextHover(sourceViewer, contentType);
}
@Override
public IAutoEditStrategy[] getAutoEditStrategies(
ISourceViewer sourceViewer, String contentType) {
// TODO auto edit strategies for android xml
return super.getAutoEditStrategies(sourceViewer, contentType);
}
@Override
public IContentFormatter getContentFormatter(ISourceViewer sourceViewer) {
// TODO content formatter for android xml
return super.getContentFormatter(sourceViewer);
}
}

View File

@@ -1,672 +0,0 @@
/*
* Copyright (C) 2007 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.editors;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.CommonPlugin;
import com.android.ide.eclipse.common.SdkStatsHelper;
import com.android.ide.eclipse.common.StreamHelper;
import com.android.ide.eclipse.common.resources.FrameworkResourceManager;
import com.android.ide.eclipse.editors.EditorsPlugin.LayoutBridge.LoadStatus;
import com.android.ide.eclipse.editors.layout.LayoutEditor;
import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors;
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
import com.android.ide.eclipse.editors.menu.MenuEditor;
import com.android.ide.eclipse.editors.menu.descriptors.MenuDescriptors;
import com.android.ide.eclipse.editors.resources.ResourcesEditor;
import com.android.ide.eclipse.editors.resources.manager.ProjectResources;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolder;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor;
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IFileListener;
import com.android.ide.eclipse.editors.xml.XmlEditor;
import com.android.ide.eclipse.editors.xml.descriptors.XmlDescriptors;
import com.android.layoutlib.api.ILayoutBridge;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.console.MessageConsole;
import org.eclipse.ui.console.MessageConsoleStream;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
import java.io.File;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
/**
* The activator class controls the plug-in life cycle
*/
public class EditorsPlugin extends AbstractUIPlugin {
// The shared instance
private static EditorsPlugin sPlugin;
private static Image sAndroidLogo;
private static ImageDescriptor sAndroidLogoDesc;
private ResourceMonitor mResourceMonitor;
private SdkPathChangedListener mSdkPathChangedListener;
private ArrayList<Runnable> mResourceRefreshListener = new ArrayList<Runnable>();
private MessageConsoleStream mAndroidConsoleStream;
/** Stream to write error messages to the android console */
private MessageConsoleStream mAndroidConsoleErrorStream;
public final static class LayoutBridge {
public enum LoadStatus { LOADING, LOADED, FAILED }
/** Link to the layout bridge */
public ILayoutBridge bridge;
public LoadStatus status = LoadStatus.LOADING;
}
private final LayoutBridge mLayoutBridge = new LayoutBridge();
private boolean mLayoutBridgeInit;
private ClassLoader mBridgeClassLoader;
private Color mRed;
/**
* The constructor
*/
public EditorsPlugin() {
}
/**
* The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code>
* method refreshes the plug-in actions. Subclasses may extend this method,
* but must send super <b>first</b>.
* {@inheritDoc}
*
* @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
*/
@Override
public void start(BundleContext context) throws Exception {
super.start(context);
sPlugin = this;
sAndroidLogoDesc = imageDescriptorFromPlugin(AndroidConstants.EDITORS_PLUGIN_ID,
"/icons/android.png"); //$NON-NLS-1$
sAndroidLogo = sAndroidLogoDesc.createImage();
// get the stream to write in the android console.
MessageConsole androidConsole = CommonPlugin.getDefault().getAndroidConsole();
mAndroidConsoleStream = androidConsole.newMessageStream();
mAndroidConsoleErrorStream = androidConsole.newMessageStream();
mRed = new Color(getDisplay(), 0xFF, 0x00, 0x00);
// because this can be run, in some cases, by a non ui thread, and beccause
// changing the console properties update the ui, we need to make this change
// in the ui thread.
getDisplay().asyncExec(new Runnable() {
public void run() {
mAndroidConsoleErrorStream.setColor(mRed);
}
});
// Add a resource listener to handle compiled resources.
IWorkspace ws = ResourcesPlugin.getWorkspace();
mResourceMonitor = ResourceMonitor.startMonitoring(ws);
if (mResourceMonitor != null) {
setupDefaultEditor(mResourceMonitor);
ResourceManager.setup(mResourceMonitor);
}
// Setup the sdk location changed listener and invoke it the first time
mSdkPathChangedListener = new SdkPathChangedListener();
FrameworkResourceManager.getInstance().addFrameworkResourcesChangeListener(
mSdkPathChangedListener);
// ping the usage server
pingUsageServer();
}
/**
* The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code>
* method saves this plug-in's preference and dialog stores and shuts down
* its image registry (if they are in use). Subclasses may extend this
* method, but must send super <b>last</b>. A try-finally statement should
* be used where necessary to ensure that <code>super.shutdown()</code> is
* always done.
*
* @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
*/
@Override
public void stop(BundleContext context) throws Exception {
sPlugin = null;
sAndroidLogo.dispose();
IconFactory.getInstance().Dispose();
// Remove the resource listener that handles compiled resources.
IWorkspace ws = ResourcesPlugin.getWorkspace();
ResourceMonitor.stopMonitoring(ws);
FrameworkResourceManager.getInstance().removeFrameworkResourcesChangeListener(
mSdkPathChangedListener);
mSdkPathChangedListener = null;
mRed.dispose();
super.stop(context);
}
/**
* Returns the shared instance
*
* @return the shared instance
*/
public static EditorsPlugin getDefault() {
return sPlugin;
}
/**
* Returns an Image for the small Android logo.
*
* Callers should not dispose it.
*/
public static Image getAndroidLogo() {
return sAndroidLogo;
}
/**
* Returns an {@link ImageDescriptor} for the small Android logo.
*
* Callers should not dispose it.
*/
public static ImageDescriptor getAndroidLogoDesc() {
return sAndroidLogoDesc;
}
/**
* Logs a message to the default Eclipse log.
*
* @param severity One of IStatus' severity codes: OK, ERROR, INFO, WARNING or CANCEL.
* @param format The format string, like for String.format().
* @param args The arguments for the format string, like for String.format().
*/
public static void log(int severity, String format, Object ... args) {
String message = String.format(format, args);
Status status = new Status(severity, AndroidConstants.EDITORS_PLUGIN_ID, message);
getDefault().getLog().log(status);
}
/**
* Logs an exception to the default Eclipse log.
* <p/>
* The status severity is always set to ERROR.
*
* @param exception The exception to log. Its call trace will be recorded.
* @param format The format string, like for String.format().
* @param args The arguments for the format string, like for String.format().
*/
public static void log(Throwable exception, String format, Object ... args) {
String message = String.format(format, args);
Status status = new Status(IStatus.ERROR, AndroidConstants.EDITORS_PLUGIN_ID,
message, exception);
getDefault().getLog().log(status);
}
/**
* Returns the ResourceMonitor object.
*/
public ResourceMonitor getResourceMonitor() {
return mResourceMonitor;
}
/**
* Sets up the editor to register default editors for resource files when needed.
*
* This is called by the {@link EditorsPlugin} during initialization.
*
* @param monitor The main Resource Monitor object.
*/
public void setupDefaultEditor(ResourceMonitor monitor) {
monitor.addFileListener(new IFileListener() {
private static final String UNKNOWN_EDITOR = "unknown-editor"; //$NON-NLS-1$
/* (non-Javadoc)
* Sent when a file changed.
* @param file The file that changed.
* @param markerDeltas The marker deltas for the file.
* @param kind The change kind. This is equivalent to
* {@link IResourceDelta#accept(IResourceDeltaVisitor)}
*
* @see IFileListener#fileChanged
*/
public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
if (AndroidConstants.EXT_XML.equals(file.getFileExtension())) {
// The resources files must have a file path similar to
// project/res/.../*.xml
// There is no support for sub folders, so the segment count must be 4
if (file.getFullPath().segmentCount() == 4) {
// check if we are inside the res folder.
String segment = file.getFullPath().segment(1);
if (segment.equalsIgnoreCase(AndroidConstants.FD_RESOURCES)) {
// we are inside a res/ folder, get the actual ResourceFolder
ProjectResources resources = ResourceManager.getInstance().
getProjectResources(file.getProject());
// This happens when importing old Android projects in Eclipse
// that lack the container (probably because resources fail to build
// properly.)
if (resources == null) {
log(IStatus.INFO,
"getProjectResources failed for path %1$s in project %2$s", //$NON-NLS-1$
file.getFullPath().toOSString(),
file.getProject().getName());
return;
}
ResourceFolder resFolder = resources.getResourceFolder(
(IFolder)file.getParent());
if (resFolder != null) {
if (kind == IResourceDelta.ADDED) {
resourceAdded(file, resFolder.getType());
} else if (kind == IResourceDelta.CHANGED) {
resourceChanged(file, resFolder.getType());
}
} else {
// if the res folder is null, this means the name is invalid,
// in this case we remove whatever android editors that was set
// as the default editor.
IEditorDescriptor desc = IDE.getDefaultEditor(file);
String editorId = desc.getId();
if (editorId.startsWith(AndroidConstants.EDITORS_PLUGIN_ID)) {
// reset the default editor.
IDE.setDefaultEditor(file, null);
}
}
}
}
}
}
private void resourceAdded(IFile file, ResourceFolderType type) {
// set the default editor based on the type.
if (type == ResourceFolderType.LAYOUT) {
IDE.setDefaultEditor(file, LayoutEditor.ID);
} else if (type == ResourceFolderType.DRAWABLE
|| type == ResourceFolderType.VALUES) {
IDE.setDefaultEditor(file, ResourcesEditor.ID);
} else if (type == ResourceFolderType.MENU) {
IDE.setDefaultEditor(file, MenuEditor.ID);
} else if (type == ResourceFolderType.XML) {
if (XmlEditor.canHandleFile(file)) {
IDE.setDefaultEditor(file, XmlEditor.ID);
} else {
// set a property to determine later if the XML can be handled
QualifiedName qname = new QualifiedName(
AndroidConstants.EDITORS_PLUGIN_ID,
UNKNOWN_EDITOR);
try {
file.setPersistentProperty(qname, "1");
} catch (CoreException e) {
// pass
}
}
}
}
private void resourceChanged(IFile file, ResourceFolderType type) {
if (type == ResourceFolderType.XML) {
IEditorDescriptor ed = IDE.getDefaultEditor(file);
if (ed == null || ed.getId() != XmlEditor.ID) {
QualifiedName qname = new QualifiedName(
AndroidConstants.EDITORS_PLUGIN_ID,
UNKNOWN_EDITOR);
String prop = null;
try {
prop = file.getPersistentProperty(qname);
} catch (CoreException e) {
// pass
}
if (prop != null && XmlEditor.canHandleFile(file)) {
try {
// remove the property & set editor
file.setPersistentProperty(qname, null);
IWorkbenchPage page = PlatformUI.getWorkbench().
getActiveWorkbenchWindow().getActivePage();
IEditorPart oldEditor = page.findEditor(new FileEditorInput(file));
if (oldEditor != null &&
CommonPlugin.displayPrompt("Android XML Editor",
String.format("The file you just saved as been recognized as a file that could be better handled using the Android XML Editor. Do you want to edit '%1$s' using the Android XML editor instead?",
file.getFullPath()))) {
IDE.setDefaultEditor(file, XmlEditor.ID);
IEditorPart newEditor = page.openEditor(
new FileEditorInput(file),
XmlEditor.ID,
true, /* activate */
IWorkbenchPage.MATCH_NONE);
if (newEditor != null) {
page.closeEditor(oldEditor, true /* save */);
}
}
} catch (CoreException e) {
// setPersistentProperty or page.openEditor may have failed
}
}
}
}
}
}, IResourceDelta.ADDED | IResourceDelta.CHANGED);
}
/**
* Respond to notifications from the resource manager than the SDK resources have been updated.
* It gets the new resources from the {@link FrameworkResourceManager} and then try to update
* the layout descriptors from the new layout data.
*/
private class SdkPathChangedListener implements Runnable {
public void run() {
// Perform the update in a thread (here an Eclipse runtime job)
// since this should never block the caller (especially the start method)
new Job("Editors: Load Framework Resource Parser") {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
SubMonitor progress = SubMonitor.convert(monitor, "Update Description",
60);
FrameworkResourceManager resourceManager = FrameworkResourceManager.getInstance();
AndroidManifestDescriptors.updateDescriptors(
resourceManager.getManifestDefinitions());
progress.worked(10);
if (progress.isCanceled()) {
return Status.CANCEL_STATUS;
}
LayoutDescriptors.getInstance().updateDescriptors(
resourceManager.getLayoutViewsInfo(),
resourceManager.getLayoutGroupsInfo());
progress.worked(10);
if (progress.isCanceled()) {
return Status.CANCEL_STATUS;
}
MenuDescriptors.getInstance().updateDescriptors(
resourceManager.getXmlMenuDefinitions());
progress.worked(10);
if (progress.isCanceled()) {
return Status.CANCEL_STATUS;
}
XmlDescriptors.getInstance().updateDescriptors(
resourceManager.getXmlSearchableDefinitions(),
resourceManager.getPreferencesInfo(),
resourceManager.getPreferenceGroupsInfo());
progress.worked(10);
// load the layout lib bridge.
if (System.getenv("ANDROID_DISABLE_LAYOUT") == null) {
loadLayoutBridge();
FrameworkResourceManager frMgr = FrameworkResourceManager.getInstance();
ResourceManager rMgr = ResourceManager.getInstance();
rMgr.loadFrameworkResources(frMgr.getFrameworkResourcesLocation());
}
progress.worked(10);
// Notify resource changed listeners
progress.subTask("Refresh UI");
progress.setWorkRemaining(mResourceRefreshListener.size());
// Clone the list before iterating, to avoid Concurrent Modification
// exceptions
@SuppressWarnings("unchecked")
ArrayList<Runnable> listeners = (ArrayList<Runnable>)
mResourceRefreshListener.clone();
for (Runnable listener : listeners) {
try {
getDisplay().syncExec(listener);
} catch (Exception e) {
log(e, "ResourceRefreshListener Failed"); //$NON-NLS-1$
} finally {
progress.worked(1);
}
}
} catch (Throwable e) {
EditorsPlugin.log(e, "Load Framework Resource Parser failed"); //$NON-NLS-1$
EditorsPlugin.printToConsole("Framework Resource Parser",
"Failed to parse");
} finally {
if (monitor != null) {
monitor.done();
}
}
return Status.OK_STATUS;
}
}.schedule();
}
}
public void addResourceChangedListener(Runnable resourceRefreshListener) {
mResourceRefreshListener.add(resourceRefreshListener);
}
public void removeResourceChangedListener(Runnable resourceRefreshListener) {
mResourceRefreshListener.remove(resourceRefreshListener);
}
public static Display getDisplay() {
IWorkbench bench = sPlugin.getWorkbench();
if (bench != null) {
return bench.getDisplay();
}
return null;
}
/**
* Pings the usage start server.
*/
private void pingUsageServer() {
// In order to not block the plugin loading, so we spawn another thread.
new Thread("Ping!") { //$NON-NLS-1$
@Override
public void run() {
// get the version of the plugin
String versionString = (String) getBundle().getHeaders().get(
Constants.BUNDLE_VERSION);
Version version = new Version(versionString);
SdkStatsHelper.pingUsageServer("editors", version); //$NON-NLS-1$
}
}.start();
}
/**
* Prints one or more message to the android console.
* @param tag The tag to be associated with the message. Can be null.
* @param objects the objects to print through their <code>toString</code> method.
*/
public static synchronized void printToConsole(String tag, Object... objects) {
StreamHelper.printToStream(sPlugin.mAndroidConsoleStream, tag, objects);
}
/**
* Prints one or more error messages to the android console.
* @param tag The tag to be associated with the message. Can be null.
* @param objects the objects to print through their <code>toString</code> method.
*/
public static synchronized void printErrorToConsole(String tag, Object... objects) {
StreamHelper.printToStream(sPlugin.mAndroidConsoleErrorStream, tag, objects);
}
public static synchronized OutputStream getErrorStream() {
return sPlugin.mAndroidConsoleErrorStream;
}
/**
* Displays an error dialog box. This dialog box is ran asynchronously in the ui thread,
* therefore this method can be called from any thread.
* @param title The title of the dialog box
* @param message The error message
*/
public final static void displayError(final String title, final String message) {
// get the current Display
final Display display = getDisplay();
// dialog box only run in ui thread..
display.asyncExec(new Runnable() {
public void run() {
Shell shell = display.getActiveShell();
MessageDialog.openError(shell, title, message);
}
});
}
/**
* Display a yes/no question dialog box. This dialog is opened synchronously in the ui thread,
* therefore this message can be called from any thread.
* @param title The title of the dialog box
* @param message The error message
* @return true if OK was clicked.
*/
public final static boolean displayPrompt(final String title, final String message) {
// get the current Display and Shell
final Display display = getDisplay();
// we need to ask the user what he wants to do.
final boolean[] wrapper = new boolean[] { false };
display.syncExec(new Runnable() {
public void run() {
Shell shell = display.getActiveShell();
wrapper[0] = MessageDialog.openQuestion(shell, title, message);
}
});
return wrapper[0];
}
/**
* Returns a {@link LayoutBridge} object possibly containing a {@link ILayoutBridge} object.
* <p/>If {@link LayoutBridge#bridge} is <code>null</code>, {@link LayoutBridge#status} will
* contain the reason (either {@link LoadStatus#LOADING} or {@link LoadStatus#FAILED}).
* <p/>Valid {@link ILayoutBridge} objects are always initialized before being returned.
*/
public synchronized LayoutBridge getLayoutBridge() {
if (mLayoutBridgeInit == false && mLayoutBridge.bridge != null) {
FrameworkResourceManager manager = FrameworkResourceManager.getInstance();
mLayoutBridge.bridge.init(
manager.getFrameworkFontLocation() + AndroidConstants.FD_DEFAULT_RES,
manager.getEnumValueMap());
mLayoutBridgeInit = true;
}
return mLayoutBridge;
}
/**
* Loads the layout bridge from the dynamically loaded layoutlib.jar
*/
private void loadLayoutBridge() {
try {
// reset to be sure we won't be using an obsolete version if we
// get an exception somewhere.
mLayoutBridge.bridge = null;
mLayoutBridge.status = LayoutBridge.LoadStatus.LOADING;
// get the URL for the file.
File f = new File(
FrameworkResourceManager.getInstance().getLayoutLibLocation());
if (f.isFile() == false) {
log(IStatus.ERROR, "layoutlib.jar is missing!"); //$NON-NLS-1$
} else {
URL url = f.toURL();
// create a class loader. Because this jar reference interfaces
// that are in the editors plugin, it's important to provide
// a parent class loader.
mBridgeClassLoader = new URLClassLoader(new URL[] { url },
this.getClass().getClassLoader());
// load the class
Class<?> clazz = mBridgeClassLoader.loadClass(AndroidConstants.CLASS_BRIDGE);
if (clazz != null) {
// instantiate an object of the class.
Constructor<?> constructor = clazz.getConstructor();
if (constructor != null) {
Object bridge = constructor.newInstance();
if (bridge instanceof ILayoutBridge) {
mLayoutBridge.bridge = (ILayoutBridge)bridge;
}
}
}
if (mLayoutBridge.bridge == null) {
mLayoutBridge.status = LayoutBridge.LoadStatus.FAILED;
log(IStatus.ERROR, "Failed to load " + AndroidConstants.CLASS_BRIDGE); //$NON-NLS-1$
} else {
mLayoutBridge.status = LayoutBridge.LoadStatus.LOADED;
}
}
} catch (Throwable t) {
mLayoutBridge.status = LayoutBridge.LoadStatus.FAILED;
// log the error.
log(t, "Failed to load the LayoutLib");
}
}
public ClassLoader getLayoutlibBridgeClassLoader() {
return mBridgeClassLoader;
}
}

View File

@@ -1,164 +0,0 @@
/*
* 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.ide.eclipse.editors;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
/**
* Quickly parses a (potential) XML file to extract its first element (i.e. the root element)
* and namespace, if any.
* <p/>
* This is used to determine if a file is an XML document that the XmlEditor can process.
* <p/>
* TODO use this to remove the hardcoded "android" namespace prefix limitation.
*/
public final class FirstElementParser {
private static SAXParserFactory sSaxfactory;
/**
* Result from the XML parsing. <br/>
* Contains the name of the root XML element. <br/>
* If an XMLNS URI was specified and found, the XMLNS prefix is recorded. Otherwise it is null.
*/
public static final class Result {
private String mElement;
private String mXmlnsPrefix;
private String mXmlnsUri;
public String getElement() {
return mElement;
}
public String getXmlnsPrefix() {
return mXmlnsPrefix;
}
public String getXmlnsUri() {
return mXmlnsUri;
}
void setElement(String element) {
mElement = element;
}
void setXmlnsPrefix(String xmlnsPrefix) {
mXmlnsPrefix = xmlnsPrefix;
}
void setXmlnsUri(String xmlnsUri) {
mXmlnsUri = xmlnsUri;
}
}
private static class ResultFoundException extends SAXException { }
/**
* Parses the given filename.
*
* @param osFilename The file to parse.
* @param xmlnsUri An optional URL of which we want to know the prefix.
* @return The element details found or null if not found.
*/
public static Result parse(String osFilename, String xmlnsUri) {
if (sSaxfactory == null) {
// TODO just create a single factory in CommonPlugin and reuse it
sSaxfactory = SAXParserFactory.newInstance();
sSaxfactory.setNamespaceAware(true);
}
Result result = new Result();
if (xmlnsUri != null && xmlnsUri.length() > 0) {
result.setXmlnsUri(xmlnsUri);
}
try {
SAXParser parser = sSaxfactory.newSAXParser();
XmlHandler handler = new XmlHandler(result);
parser.parse(new InputSource(new FileReader(osFilename)), handler);
} catch(ResultFoundException e) {
// XML handling was aborted because the required element was found.
// Simply return the result.
return result;
} catch (ParserConfigurationException e) {
} catch (SAXException e) {
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
return null;
}
/**
* Private constructor. Use the static parse() method instead.
*/
private FirstElementParser() {
// pass
}
/**
* A specialized SAX handler that captures the arguments of the very first element
* (i.e. the root element)
*/
private static class XmlHandler extends DefaultHandler {
private final Result mResult;
public XmlHandler(Result result) {
mResult = result;
}
/**
* Processes a namespace prefix mapping.
* I.e. for xmlns:android="some-uri", this received prefix="android" and uri="some-uri".
* <p/>
* The prefix is recorded in the result structure if the URI is the one searched for.
* <p/>
* This event happens <em>before</em> the corresponding startElement event.
*/
@Override
public void startPrefixMapping(String prefix, String uri) {
if (uri.equals(mResult.getXmlnsUri())) {
mResult.setXmlnsPrefix(prefix);
}
}
/**
* Processes a new element start.
* <p/>
* This simply records the element name and abort processing by throwing an exception.
*/
@Override
public void startElement(String uri, String localName, String name, Attributes attributes)
throws SAXException {
mResult.setElement(localName);
throw new ResultFoundException();
}
}
}

View File

@@ -1,258 +0,0 @@
/*
* Copyright (C) 2008 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.ide.eclipse.editors;
import com.android.ide.eclipse.common.AndroidConstants;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
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.widgets.Display;
import java.util.HashMap;
/**
* Factory to generate icons for Android Editors.
* <p/>
* Icons are kept here and reused.
*/
public class IconFactory {
public static final int COLOR_RED = SWT.COLOR_DARK_RED;
public static final int COLOR_GREEN = SWT.COLOR_DARK_GREEN;
public static final int COLOR_BLUE = SWT.COLOR_DARK_BLUE;
public static final int COLOR_DEFAULT = SWT.COLOR_BLACK;
public static final int SHAPE_CIRCLE = 'C';
public static final int SHAPE_RECT = 'R';
public static final int SHAPE_DEFAULT = SHAPE_CIRCLE;
private static IconFactory sInstance;
private HashMap<String, Image> mIconMap = new HashMap<String, Image>();
private HashMap<String, ImageDescriptor> mImageDescMap = new HashMap<String, ImageDescriptor>();
private IconFactory() {
}
public static synchronized IconFactory getInstance() {
if (sInstance == null) {
sInstance = new IconFactory();
}
return sInstance;
}
public void Dispose() {
// Dispose icons
for (Image icon : mIconMap.values()) {
// The map can contain null values
if (icon != null) {
icon.dispose();
}
}
mIconMap.clear();
}
/**
* Returns an Image for a given icon name.
* <p/>
* Callers should not dispose it.
*
* @param osName The leaf name, without the extension, of an existing icon in the
* editor's "icons" directory. If it doesn't exists, a default icon will be
* generated automatically based on the name.
*/
public Image getIcon(String osName) {
return getIcon(osName, COLOR_DEFAULT, SHAPE_DEFAULT);
}
/**
* Returns an Image for a given icon name.
* <p/>
* Callers should not dispose it.
*
* @param osName The leaf name, without the extension, of an existing icon in the
* editor's "icons" directory. If it doesn't exists, a default icon will be
* generated automatically based on the name.
* @param color The color of the text in the automatically generated icons,
* one of COLOR_DEFAULT, COLOR_RED, COLOR_BLUE or COLOR_RED.
* @param shape The shape of the icon in the automatically generated icons,
* one of SHAPE_DEFAULT, SHAPE_CIRCLE or SHAPE_RECT.
*/
public Image getIcon(String osName, int color, int shape) {
EditorsPlugin plugin = EditorsPlugin.getDefault();
String key = Character.toString((char) shape) + Integer.toString(color) + osName;
Image icon = mIconMap.get(key);
if (icon == null && !mIconMap.containsKey(key)) {
ImageDescriptor id = getImageDescriptor(osName, color, shape);
if (id != null) {
icon = id.createImage();
}
// Note that we store null references in the icon map, to avoid looking them
// up every time. If it didn't exist once, it will not exist later.
mIconMap.put(key, icon);
}
return icon;
}
/**
* Returns an ImageDescriptor for a given icon name.
* <p/>
* Callers should not dispose it.
*
* @param osName The leaf name, without the extension, of an existing icon in the
* editor's "icons" directory. If it doesn't exists, a default icon will be
* generated automatically based on the name.
*/
public ImageDescriptor getImageDescriptor(String osName) {
return getImageDescriptor(osName, COLOR_DEFAULT, SHAPE_DEFAULT);
}
/**
* Returns an ImageDescriptor for a given icon name.
* <p/>
* Callers should not dispose it.
*
* @param osName The leaf name, without the extension, of an existing icon in the
* editor's "icons" directory. If it doesn't exists, a default icon will be
* generated automatically based on the name.
* @param color The color of the text in the automatically generated icons.
* one of COLOR_DEFAULT, COLOR_RED, COLOR_BLUE or COLOR_RED.
* @param shape The shape of the icon in the automatically generated icons,
* one of SHAPE_DEFAULT, SHAPE_CIRCLE or SHAPE_RECT.
*/
public ImageDescriptor getImageDescriptor(String osName, int color, int shape) {
EditorsPlugin plugin = EditorsPlugin.getDefault();
String key = Character.toString((char) shape) + Integer.toString(color) + osName;
ImageDescriptor id = mImageDescMap.get(key);
if (id == null && !mImageDescMap.containsKey(key)) {
id = plugin.imageDescriptorFromPlugin(
AndroidConstants.EDITORS_PLUGIN_ID,
String.format("/icons/%1$s.png", osName)); //$NON-NLS-1$
if (id == null) {
id = new LetterImageDescriptor(osName.charAt(0), color, shape);
}
// Note that we store null references in the icon map, to avoid looking them
// up every time. If it didn't exist once, it will not exist later.
mImageDescMap.put(key, id);
}
return id;
}
/**
* A simple image description that generates a 16x16 image which consists
* of a colored letter inside a black & white circle.
*/
private static class LetterImageDescriptor extends ImageDescriptor {
private final char mLetter;
private final int mColor;
private final int mShape;
public LetterImageDescriptor(char letter, int color, int shape) {
mLetter = letter;
mColor = color;
mShape = shape;
}
@Override
public ImageData getImageData() {
final int SX = 15;
final int SY = 15;
final int RX = 4;
final int RY = 4;
Display display = Display.getCurrent();
if (display == null) {
return null;
}
Image image = new Image(display, SX, SY);
image.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
GC gc = new GC(image);
gc.setAdvanced(true);
gc.setAntialias(SWT.ON);
gc.setTextAntialias(SWT.ON);
gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
if (mShape == SHAPE_CIRCLE) {
gc.fillOval(0, 0, SX - 1, SY - 1);
} else if (mShape == SHAPE_RECT) {
gc.fillRoundRectangle(0, 0, SX - 1, SY - 1, RX, RY);
}
gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
gc.setLineWidth(1);
if (mShape == SHAPE_CIRCLE) {
gc.drawOval(0, 0, SX - 1, SY - 1);
} else if (mShape == SHAPE_RECT) {
gc.drawRoundRectangle(0, 0, SX - 1, SY - 1, RX, RY);
}
// Get a bold version of the default system font, if possible.
Font font = display.getSystemFont();
FontData[] fds = font.getFontData();
fds[0].setStyle(SWT.BOLD);
// use 3/4th of the circle diameter for the font size (in pixels)
// and convert it to "font points" (font points in SWT are hardcoded in an
// arbitrary 72 dpi and then converted in real pixels using whatever is
// indicated by getDPI -- at least that's how it works under Win32).
fds[0].setHeight((int) ((SY + 1) * 3./4. * 72./display.getDPI().y));
// Note: win32 implementation always uses fds[0] so we change just that one.
// getFontData indicates that the array of fd is really an unusual thing for X11.
font = new Font(display, fds);
gc.setFont(font);
gc.setForeground(display.getSystemColor(mColor));
// Text measurement varies so slightly depending on the platform
int ofx = 0;
int ofy = 0;
if (AndroidConstants.CURRENT_PLATFORM == AndroidConstants.PLATFORM_WINDOWS) {
ofx = +1;
ofy = -1;
}
String s = Character.toString(mLetter).toUpperCase();
Point p = gc.textExtent(s);
int tx = (SX + ofx - p.x) / 2;
int ty = (SY + ofy - p.y) / 2;
gc.drawText(s, tx, ty, true /* isTransparent */);
font.dispose();
gc.dispose();
ImageData data = image.getImageData();
image.dispose();
return data;
}
}
}

View File

@@ -1,119 +0,0 @@
/*
* Copyright (C) 2007 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.editors.descriptors;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.IconFactory;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.swt.graphics.Image;
/**
* {@link AttributeDescriptor} describes an XML attribute with its XML attribute name.
* <p/>
* An attribute descriptor also knows which UI node should be instantiated to represent
* this particular attribute (e.g. text field, icon chooser, class selector, etc.)
* Some attributes may be hidden and have no user interface at all.
* <p/>
* This is an abstract class. Derived classes must implement data description and return
* the correct UiAttributeNode-derived class.
*/
public abstract class AttributeDescriptor {
private String mXmlLocalName;
private ElementDescriptor mParent;
private final String mNsUri;
/**
* Creates a new {@link AttributeDescriptor}
*
* @param xmlLocalName The XML name of the attribute (case sensitive)
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
*/
public AttributeDescriptor(String xmlLocalName, String nsUri) {
mXmlLocalName = xmlLocalName;
mNsUri = nsUri;
}
/**
* Returns the XML local name of the attribute (case sensitive)
*/
public final String getXmlLocalName() {
return mXmlLocalName;
}
public final String getNamespaceUri() {
return mNsUri;
}
/**
* Returns the XML qualified name of the attribute (case sensitive, with namespace prefix
* if present)
*
* @deprecated
*/
private final String getXmlName() {
return mXmlLocalName;
}
/**
* Returns the namespace of the attribute.
*
* @deprecated
*/
private final String getNamespace() {
// For now we hard-code the prefix as being "android"
if (mXmlLocalName.startsWith("android:")) { //$NON-NLs-1$
return AndroidConstants.NS_RESOURCES;
}
return ""; //$NON-NLs-1$
}
final void setParent(ElementDescriptor parent) {
mParent = parent;
}
public final ElementDescriptor getParent() {
return mParent;
}
/**
* Returns an optional icon for the attribute.
* <p/>
* By default this tries to return an icon based on the XML name of the attribute.
* If this fails, it tries to return the default Android logo as defined in the
* plugin. If all fails, it returns null.
*
* @return An icon for this element or null.
*/
public Image getIcon() {
IconFactory factory = IconFactory.getInstance();
Image icon;
icon = factory.getIcon(getXmlLocalName(), IconFactory.COLOR_RED, IconFactory.SHAPE_CIRCLE);
return icon != null ? icon : EditorsPlugin.getAndroidLogo();
}
/**
* @param uiParent The {@link UiElementNode} parent of this UI attribute.
* @return A new {@link UiAttributeNode} linked to this descriptor or null if this
* attribute has no user interface.
*/
public abstract UiAttributeNode createUiNode(UiElementNode uiParent);
}

View File

@@ -1,69 +0,0 @@
/*
* 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.ide.eclipse.editors.descriptors;
import com.android.ide.eclipse.editors.uimodel.UiAbstractTextAttributeNode;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.swt.graphics.Image;
/**
* Label provider for {@link UiAbstractTextAttributeNode}.
*/
public class AttributeDescriptorLabelProvider implements ILabelProvider {
private final static AttributeDescriptorLabelProvider sThis =
new AttributeDescriptorLabelProvider();
public static ILabelProvider getProvider() {
return sThis;
}
public Image getImage(Object element) {
return null;
}
public String getText(Object element) {
if (element instanceof UiAbstractTextAttributeNode) {
return ((UiAbstractTextAttributeNode)element).getCurrentValue();
}
return null;
}
public void addListener(ILabelProviderListener listener) {
// TODO Auto-generated method stub
}
public void dispose() {
// TODO Auto-generated method stub
}
public boolean isLabelProperty(Object element, String property) {
// TODO Auto-generated method stub
return false;
}
public void removeListener(ILabelProviderListener listener) {
// TODO Auto-generated method stub
}
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright (C) 2007 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.editors.descriptors;
import com.android.ide.eclipse.editors.uimodel.UiListAttributeNode;
/**
* Describes a text attribute that can only contain boolean values.
* It is displayed by a {@link UiListAttributeNode}.
*/
public class BooleanAttributeDescriptor extends ListAttributeDescriptor {
public BooleanAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
String tooltip) {
super(xmlLocalName, uiName, nsUri, tooltip,
new String[] { "true", "false" } );
}
}

View File

@@ -1,751 +0,0 @@
/*
* 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.ide.eclipse.editors.descriptors;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.resources.ResourceType;
import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo;
import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo.Format;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.swt.graphics.Image;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility methods related to descriptors handling.
*/
public final class DescriptorsUtils {
private static final int JAVADOC_BREAK_LENGTH = 60;
/**
* The path in the online documentation for the manifest description.
* <p/>
* This is NOT a complete URL. To be used, it needs to be appended
* to {@link AndroidConstants#CODESITE_BASE_URL} or to the local SDK
* documentation.
*/
public static final String MANIFEST_SDK_URL = "/reference/android/R.styleable.html#"; //$NON-NLS-1$
public static final String IMAGE_KEY = "image"; //$NON-NLS-1$
private static final String CODE = "$code"; //$NON-NLS-1$
private static final String LINK = "$link"; //$NON-NLS-1$
private static final String ELEM = "$elem"; //$NON-NLS-1$
private static final String BREAK = "$break"; //$NON-NLS-1$
/**
* The {@link ITextAttributeCreator} interface is used by the appendAttribute() method
* to provide a way for caller to override the kind of {@link TextAttributeDescriptor}
* created for a give XML attribute name.
*/
public interface ITextAttributeCreator {
/**
* Creates a new {@link TextAttributeDescriptor} instance for the given XML name,
* UI name and tooltip.
*
* @param xmlName The XML attribute name.
* @param uiName The UI attribute name.
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
* @param tooltip An optional tooltip.
* @return A new {@link TextAttributeDescriptor} (or derived) instance.
*/
public TextAttributeDescriptor create(String xmlName, String uiName, String nsUri,
String tooltip);
}
/**
* Add all {@link AttributeInfo} to the the array of {@link AttributeDescriptor}.
*
* @param attributes The list of {@link AttributeDescriptor} to append to
* @param elementXmlName Optional XML local name of the element to which attributes are
* being added. When not null, this is used to filter overrides.
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
* @param infos The array of {@link AttributeInfo} to read and append to attributes
* @param requiredAttributes An optional list of attributes to mark as "required" (i.e. append
* a "*" to their UI name as a hint for the user.)
* @param overrides A map [attribute name => TextAttributeDescriptor creator]. A creator
* can either by a Class<? extends TextAttributeDescriptor> or an instance of
* {@link ITextAttributeCreator} that instantiates the right TextAttributeDescriptor.
*/
public static void appendAttributes(ArrayList<AttributeDescriptor> attributes,
String elementXmlName,
String nsUri, AttributeInfo[] infos,
String[] requiredAttributes,
Map<String, Object> overrides) {
for (AttributeInfo info : infos) {
boolean required = false;
if (requiredAttributes != null) {
for(String attr_name : requiredAttributes) {
if (attr_name.equals(info.getName())) {
required = true;
break;
}
}
}
appendAttribute(attributes, elementXmlName, nsUri, info, required, overrides);
}
}
/**
* Add an {@link AttributeInfo} to the the array of {@link AttributeDescriptor}.
*
* @param attributes The list of {@link AttributeDescriptor} to append to
* @param elementXmlName Optional XML local name of the element to which attributes are
* being added. When not null, this is used to filter overrides.
* @param info The {@link AttributeInfo} to append to attributes
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
* @param required True if the attribute is to be marked as "required" (i.e. append
* a "*" to its UI name as a hint for the user.)
* @param overrides A map [attribute name => TextAttributeDescriptor creator]. A creator
* can either by a Class<? extends TextAttributeDescriptor> or an instance of
* {@link ITextAttributeCreator} that instantiates the right TextAttributeDescriptor.
*/
public static void appendAttribute(ArrayList<AttributeDescriptor> attributes,
String elementXmlName,
String nsUri,
AttributeInfo info, boolean required,
Map<String, Object> overrides) {
AttributeDescriptor attr = null;
String xmlLocalName = info.getName();
String uiName = prettyAttributeUiName(info.getName()); // ui_name
if (required) {
uiName += "*"; //$NON-NLS-1$
}
String tooltip = formatTooltip(info.getJavaDoc()); // tooltip
// Add the known types to the tooltip
Format[] formats_list = info.getFormats();
int flen = formats_list.length;
if (flen > 0) {
// Fill the formats in a set for faster access
HashSet<Format> formats_set = new HashSet<Format>();
StringBuilder sb = new StringBuilder();
if (tooltip != null) {
sb.append(tooltip);
sb.append(" "); //$NON-NLS-1$
}
sb.append("["); //$NON-NLS-1$
for (int i = 0; i < flen; i++) {
Format f = formats_list[i];
formats_set.add(f);
sb.append(f.toString().toLowerCase());
if (i < flen - 1) {
sb.append(", "); //$NON-NLS-1$
}
}
// The extra space at the end makes the tooltip more readable on Windows.
sb.append("]"); //$NON-NLS-1$
if (required) {
sb.append(". Required.");
}
// The extra space at the end makes the tooltip more readable on Windows.
sb.append(" "); //$NON-NLS-1$
tooltip = sb.toString();
// Create a specialized attribute if we can
if (overrides != null) {
for (Entry<String, Object> entry: overrides.entrySet()) {
String key = entry.getKey();
String elements[] = key.split("/");
String overrideAttrLocalName = null;
if (elements.length < 1) {
continue;
} else if (elements.length == 1) {
overrideAttrLocalName = elements[0];
elements = null;
} else {
overrideAttrLocalName = elements[elements.length - 1];
elements = elements[0].split(",");
}
if (overrideAttrLocalName == null ||
!overrideAttrLocalName.equals(xmlLocalName)) {
continue;
}
boolean ok_element = elements.length < 1;
if (!ok_element) {
for (String element : elements) {
if (element.equals("*") || element.equals(elementXmlName)) {
ok_element = true;
break;
}
}
}
if (!ok_element) {
continue;
}
Object override = entry.getValue();
if (override instanceof Class) {
try {
// The override is instance of the class to create, which must
// have a constructor compatible with TextAttributeDescriptor.
@SuppressWarnings("unchecked") //$NON-NLS-1$
Class<? extends TextAttributeDescriptor> clazz =
(Class<? extends TextAttributeDescriptor>) override;
Constructor<? extends TextAttributeDescriptor> cons;
cons = clazz.getConstructor(new Class<?>[] {
String.class, String.class, String.class, String.class } );
attr = cons.newInstance(
new Object[] { xmlLocalName, uiName, nsUri, tooltip });
} catch (SecurityException e) {
// ignore
} catch (NoSuchMethodException e) {
// ignore
} catch (IllegalArgumentException e) {
// ignore
} catch (InstantiationException e) {
// ignore
} catch (IllegalAccessException e) {
// ignore
} catch (InvocationTargetException e) {
// ignore
}
} else if (override instanceof ITextAttributeCreator) {
attr = ((ITextAttributeCreator) override).create(
xmlLocalName, uiName, nsUri, tooltip);
}
}
} // if overrides
// Create a specialized descriptor if we can, based on type
if (attr == null) {
if (formats_set.contains(Format.REFERENCE)) {
// This is either a multi-type reference or a generic reference.
attr = new ReferenceAttributeDescriptor(xmlLocalName, uiName, nsUri, tooltip);
} else if (formats_set.contains(Format.ENUM)) {
attr = new ListAttributeDescriptor(xmlLocalName, uiName, nsUri, tooltip,
info.getEnumValues());
} else if (formats_set.contains(Format.FLAG)) {
attr = new FlagAttributeDescriptor(xmlLocalName, uiName, nsUri, tooltip,
info.getFlagValues());
} else if (formats_set.contains(Format.BOOLEAN)) {
attr = new BooleanAttributeDescriptor(xmlLocalName, uiName, nsUri, tooltip);
} else if (formats_set.contains(Format.STRING)) {
attr = new ReferenceAttributeDescriptor(ResourceType.STRING,
xmlLocalName, uiName, nsUri,
tooltip);
}
}
}
// By default a simple text field is used
if (attr == null) {
attr = new TextAttributeDescriptor(xmlLocalName, uiName, nsUri, tooltip);
}
attributes.add(attr);
}
/**
* Indicates the the given {@link AttributeInfo} already exists in the ArrayList of
* {@link AttributeDescriptor}. This test for the presence of a descriptor with the same
* XML name.
*
* @param attributes The list of {@link AttributeDescriptor} to compare to.
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
* @param info The {@link AttributeInfo} to know whether it is included in the above list.
* @return True if this {@link AttributeInfo} is already present in
* the {@link AttributeDescriptor} list.
*/
public static boolean containsAttribute(ArrayList<AttributeDescriptor> attributes,
String nsUri,
AttributeInfo info) {
String xmlLocalName = info.getName();
for (AttributeDescriptor desc : attributes) {
if (desc.getXmlLocalName().equals(xmlLocalName)) {
if (nsUri == desc.getNamespaceUri() ||
(nsUri != null && nsUri.equals(desc.getNamespaceUri()))) {
return true;
}
}
}
return false;
}
/**
* Create a pretty attribute UI name from an XML name.
* <p/>
* The original xml name starts with a lower case and is camel-case,
* e.g. "maxWidthForView". The pretty name starts with an upper case
* and has space separators, e.g. "Max width for view".
*/
public static String prettyAttributeUiName(String name) {
if (name.length() < 1) {
return name;
}
StringBuffer buf = new StringBuffer();
char c = name.charAt(0);
// Use upper case initial letter
buf.append((char)(c >= 'a' && c <= 'z' ? c + 'A' - 'a' : c));
int len = name.length();
for (int i = 1; i < len; i++) {
c = name.charAt(i);
if (c >= 'A' && c <= 'Z') {
// 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.
if (c >= 'X' && c <= 'Z' &&
(i == len-1 ||
(i < len-1 && name.charAt(i+1) >= 'A' && name.charAt(i+1) <= 'Z'))) {
buf.append(c);
} else {
buf.append((char)(c - 'A' + 'a'));
}
} else if (c == '_') {
buf.append(' ');
} 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"
name = name.replaceAll("(?<=^| )sdk(?=$| )", "SDK");
name = name.replaceAll("(?<=^| )uri(?=$| )", "URI");
return name;
}
/**
* Capitalizes the string, i.e. transforms the initial [a-z] into [A-Z].
* Returns the string unmodified if the first character is not [a-z].
*
* @param str The string to capitalize.
* @return The capitalized string
*/
public static String capitalize(String str) {
if (str == null || str.length() < 1 || str.charAt(0) < 'a' || str.charAt(0) > 'z') {
return str;
}
StringBuilder sb = new StringBuilder();
sb.append((char)(str.charAt(0) + 'A' - 'a'));
sb.append(str.substring(1));
return sb.toString();
}
/**
* Formats the javadoc tooltip to be usable in a tooltip.
*/
public static String formatTooltip(String javadoc) {
ArrayList<String> spans = scanJavadoc(javadoc);
StringBuilder sb = new StringBuilder();
boolean needBreak = false;
for (int n = spans.size(), i = 0; i < n; ++i) {
String s = spans.get(i);
if (CODE.equals(s)) {
s = spans.get(++i);
if (s != null) {
sb.append('"').append(s).append('"');
}
} else if (LINK.equals(s)) {
String base = spans.get(++i);
String anchor = spans.get(++i);
String text = spans.get(++i);
if (base != null) {
base = base.trim();
}
if (anchor != null) {
anchor = anchor.trim();
}
if (text != null) {
text = text.trim();
}
// If there's no text, use the anchor if there's one
if (text == null || text.length() == 0) {
text = anchor;
}
if (base != null && base.length() > 0) {
if (text == null || text.length() == 0) {
// If we still have no text, use the base as text
text = base;
}
}
if (text != null) {
sb.append(text);
}
} else if (ELEM.equals(s)) {
s = spans.get(++i);
if (s != null) {
sb.append(s);
}
} else if (BREAK.equals(s)) {
needBreak = true;
} else if (s != null) {
if (needBreak && s.trim().length() > 0) {
sb.append('\r');
}
sb.append(s);
needBreak = false;
}
}
return sb.toString();
}
/**
* Formats the javadoc tooltip to be usable in a FormText.
* <p/>
* If the descriptor can provide an icon, the caller should provide
* elementsDescriptor.getIcon() as "image" to FormText, e.g.:
* <code>formText.setImage(IMAGE_KEY, elementsDescriptor.getIcon());</code>
*
* @param javadoc The javadoc to format. Cannot be null.
* @param elementDescriptor The element descriptor parent of the javadoc. Cannot be null.
* @param androidDocBaseUrl The base URL for the documentation. Cannot be null. Should be
* <code>FrameworkResourceManager.getInstance().getDocumentationBaseUrl()</code>
*/
public static String formatFormText(String javadoc,
ElementDescriptor elementDescriptor,
String androidDocBaseUrl) {
ArrayList<String> spans = scanJavadoc(javadoc);
String fullSdkUrl = androidDocBaseUrl + MANIFEST_SDK_URL;
String sdkUrl = elementDescriptor.getSdkUrl();
if (sdkUrl != null && sdkUrl.startsWith(MANIFEST_SDK_URL)) {
fullSdkUrl = androidDocBaseUrl + sdkUrl;
}
StringBuilder sb = new StringBuilder();
Image icon = elementDescriptor.getIcon();
if (icon != null) {
sb.append("<form><li style=\"image\" value=\"" + IMAGE_KEY + "\">"); //$NON-NLS-1$ //$NON-NLS-2$
} else {
sb.append("<form><p>"); //$NON-NLS-1$
}
for (int n = spans.size(), i = 0; i < n; ++i) {
String s = spans.get(i);
if (CODE.equals(s)) {
s = spans.get(++i);
if (elementDescriptor.getXmlName().equals(s) && fullSdkUrl != null) {
sb.append("<a href=\""); //$NON-NLS-1$
sb.append(fullSdkUrl);
sb.append("\">"); //$NON-NLS-1$
sb.append(s);
sb.append("</a>"); //$NON-NLS-1$
} else if (s != null) {
sb.append('"').append(s).append('"');
}
} else if (LINK.equals(s)) {
String base = spans.get(++i);
String anchor = spans.get(++i);
String text = spans.get(++i);
if (base != null) {
base = base.trim();
}
if (anchor != null) {
anchor = anchor.trim();
}
if (text != null) {
text = text.trim();
}
// If there's no text, use the anchor if there's one
if (text == null || text.length() == 0) {
text = anchor;
}
// TODO specialize with a base URL for views, menus & other resources
// Base is empty for a local page anchor, in which case we'll replace it
// by the element SDK URL if it exists.
if ((base == null || base.length() == 0) && fullSdkUrl != null) {
base = fullSdkUrl;
}
String url = null;
if (base != null && base.length() > 0) {
if (base.startsWith("http")) { //$NON-NLS-1$
// If base looks an URL, use it, with the optional anchor
url = base;
if (anchor != null && anchor.length() > 0) {
// If the base URL already has an anchor, it needs to be
// removed first. If there's no anchor, we need to add "#"
int pos = url.lastIndexOf('#');
if (pos < 0) {
url += "#"; //$NON-NLS-1$
} else if (pos < url.length() - 1) {
url = url.substring(0, pos + 1);
}
url += anchor;
}
} else if (text == null || text.length() == 0) {
// If we still have no text, use the base as text
text = base;
}
}
if (url != null && text != null) {
sb.append("<a href=\""); //$NON-NLS-1$
sb.append(url);
sb.append("\">"); //$NON-NLS-1$
sb.append(text);
sb.append("</a>"); //$NON-NLS-1$
} else if (text != null) {
sb.append("<b>").append(text).append("</b>"); //$NON-NLS-1$ //$NON-NLS-2$
}
} else if (ELEM.equals(s)) {
s = spans.get(++i);
if (sdkUrl != null && s != null) {
sb.append("<a href=\""); //$NON-NLS-1$
sb.append(sdkUrl);
sb.append("\">"); //$NON-NLS-1$
sb.append(s);
sb.append("</a>"); //$NON-NLS-1$
} else if (s != null) {
sb.append("<b>").append(s).append("</b>"); //$NON-NLS-1$ //$NON-NLS-2$
}
} else if (BREAK.equals(s)) {
// ignore line breaks in pseudo-HTML rendering
} else if (s != null) {
sb.append(s);
}
}
if (icon != null) {
sb.append("</li></form>"); //$NON-NLS-1$
} else {
sb.append("</p></form>"); //$NON-NLS-1$
}
return sb.toString();
}
private static ArrayList<String> scanJavadoc(String javadoc) {
ArrayList<String> spans = new ArrayList<String>();
// Standardize all whitespace in the javadoc to single spaces.
if (javadoc != null) {
javadoc = javadoc.replaceAll("[ \t\f\r\n]+", " "); //$NON-NLS-1$ //$NON-NLS-2$
}
// Detects {@link <base>#<name> <text>} where all 3 are optional
Pattern p_link = Pattern.compile("\\{@link\\s+([^#\\}\\s]*)(?:#([^\\s\\}]*))?(?:\\s*([^\\}]*))?\\}(.*)"); //$NON-NLS-1$
// Detects <code>blah</code>
Pattern p_code = Pattern.compile("<code>(.+?)</code>(.*)"); //$NON-NLS-1$
// Detects @blah@, used in hard-coded tooltip descriptors
Pattern p_elem = Pattern.compile("@([\\w -]+)@(.*)"); //$NON-NLS-1$
// Detects a buffer that starts by @ < or { (one that was not matched above)
Pattern p_open = Pattern.compile("([@<\\{])(.*)"); //$NON-NLS-1$
// Detects everything till the next potential separator, i.e. @ < or {
Pattern p_text = Pattern.compile("([^@<\\{]+)(.*)"); //$NON-NLS-1$
int currentLength = 0;
String text = null;
while(javadoc != null && javadoc.length() > 0) {
Matcher m;
String s = null;
if ((m = p_code.matcher(javadoc)).matches()) {
spans.add(CODE);
spans.add(text = cleanupJavadocHtml(m.group(1))); // <code> text
javadoc = m.group(2);
if (text != null) {
currentLength += text.length();
}
} else if ((m = p_link.matcher(javadoc)).matches()) {
spans.add(LINK);
spans.add(m.group(1)); // @link base
spans.add(m.group(2)); // @link anchor
spans.add(text = cleanupJavadocHtml(m.group(3))); // @link text
javadoc = m.group(4);
if (text != null) {
currentLength += text.length();
}
} else if ((m = p_elem.matcher(javadoc)).matches()) {
spans.add(ELEM);
spans.add(text = cleanupJavadocHtml(m.group(1))); // @text@
javadoc = m.group(2);
if (text != null) {
currentLength += text.length() - 2;
}
} else if ((m = p_open.matcher(javadoc)).matches()) {
s = m.group(1);
javadoc = m.group(2);
} else if ((m = p_text.matcher(javadoc)).matches()) {
s = m.group(1);
javadoc = m.group(2);
} else {
// This is not supposed to happen. In case of, just use everything.
s = javadoc;
javadoc = null;
}
if (s != null && s.length() > 0) {
s = cleanupJavadocHtml(s);
if (currentLength >= JAVADOC_BREAK_LENGTH) {
spans.add(BREAK);
currentLength = 0;
}
while (currentLength + s.length() > JAVADOC_BREAK_LENGTH) {
int pos = s.indexOf(' ', JAVADOC_BREAK_LENGTH - currentLength);
if (pos <= 0) {
break;
}
spans.add(s.substring(0, pos + 1));
spans.add(BREAK);
currentLength = 0;
s = s.substring(pos + 1);
}
spans.add(s);
currentLength += s.length();
}
}
return spans;
}
/**
* Remove anything that looks like HTML from a javadoc snippet, as it is supported
* neither by FormText nor a standard text tooltip.
*/
private static String cleanupJavadocHtml(String s) {
if (s != null) {
s = s.replaceAll("&lt;", "\""); //$NON-NLS-1$ $NON-NLS-2$
s = s.replaceAll("&gt;", "\""); //$NON-NLS-1$ $NON-NLS-2$
s = s.replaceAll("<[^>]+>", ""); //$NON-NLS-1$ $NON-NLS-2$
}
return s;
}
/**
* Sets the default layout attributes for the a new UiElementNode.
* <p/>
* Note that ideally the node should already be part of a hierarchy so that its
* parent layout and previous sibling can be determined, if any.
* <p/>
* This does not override attributes which are not empty.
*/
public static void setDefaultLayoutAttributes(UiElementNode ui_node) {
ui_node.setAttributeValue("layout_width", "wrap_content", false /* override */); //$NON-NLS-1$ $NON-NLS-2$
ui_node.setAttributeValue("layout_height", "wrap_content", false /* override */); //$NON-NLS-1$ $NON-NLS-2$
String widget_id = getFreeWidgetId(ui_node.getUiRoot(),
new Object[] {ui_node.getDescriptor().getXmlLocalName(), 1, null });
if (widget_id != null) {
ui_node.setAttributeValue("id", "@+id/" + widget_id, false /* override */); //$NON-NLS-1$ $NON-NLS-2$
}
UiElementNode ui_parent = ui_node.getUiParent();
if (ui_parent != null && ui_parent.getDescriptor().getXmlLocalName().equals("RelativeLayout")) { //$NON-NLS-1$
UiElementNode ui_previous = ui_node.getUiPreviousSibling();
if (ui_previous != null) {
String id = ui_previous.getAttributeValue("id"); //$NON-NLS-1$
if (id != null && id.length() > 0) {
id = id.replace("@+", "@");
ui_node.setAttributeValue("layout_below", id, false /* override */);
}
}
}
}
/**
* Given a UI root node, returns the first available id that matches the
* pattern "prefix%02d".
*
* For recursion purposes, a "context" is given. Since Java doesn't have in-out parameters
* in methods and we're not going to do a dedicated type, we just use an object array which
* must contain the following items:
* - prefix(String): The prefix of the generated id, i.e. "widget"
* - index(Integer): The minimum index of the generated id
* - generated(String) The generated widget currently being searched. Must start with null.
*
* @param uiRoot The Ui root node where to start searching recusrively.
* @param params An in-out context of parameters used during recursion, as explaine above.
* @return A suitable generated id
*/
private static String getFreeWidgetId(UiElementNode uiRoot, Object[] params) {
String generated = (String) params[2];
if (generated == null) {
String prefix = (String) params[0];
int pos = prefix.indexOf('.');
if (pos >= 0) {
prefix = prefix.substring(pos + 1);
}
pos = prefix.indexOf('$');
if (pos >= 0) {
prefix = prefix.substring(pos + 1);
}
prefix = prefix.replaceAll("[^a-zA-Z]", ""); //$NON-NLS-1$ $NON-NLS-2$
if (prefix.length() == 0) {
prefix = "widget";
}
generated = String.format("%1$s%2$02d", prefix, ((Integer)params[1]).intValue());
params[0] = prefix;
params[2] = generated;
}
String id = uiRoot.getAttributeValue("id"); //$NON-NLS-1$
if (id != null) {
id = id.replace("@+id/", ""); //$NON-NLS-1$ $NON-NLS-2$
id = id.replace("@id/", ""); //$NON-NLS-1$ $NON-NLS-2$
if (id.equals(generated)) {
// switch to next value
int num = ((Integer)params[1]).intValue() + 1;
generated = String.format("%1$s%2$02d", params[0], num);
params[1] = num;
params[2] = generated;
}
}
for (UiElementNode uiChild : uiRoot.getUiChildren()) {
getFreeWidgetId(uiChild, params);
}
return (String) params[2];
}
}

View File

@@ -1,57 +0,0 @@
/*
* 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.ide.eclipse.editors.descriptors;
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
/**
* {@link DocumentDescriptor} describes the properties expected for an XML document node.
*
* Compared to ElementDescriptor, {@link DocumentDescriptor} does not have XML name nor UI name,
* tooltip, SDK url and attributes list.
* <p/>
* It has a children list which represent all the possible roots of the document.
* <p/>
* The document nodes are "mandatory", meaning the UI node is never deleted and it may lack
* an actual XML node attached.
*/
public class DocumentDescriptor extends ElementDescriptor {
/**
* Constructs a new {@link DocumentDescriptor} based on its XML name and children list.
* The UI name is build by capitalizing the XML name.
* The UI nodes will be non-mandatory.
* <p/>
* The XML name is never shown in the UI directly. It is however used when an icon
* needs to be found for the node.
*
* @param xml_name The XML element node name. Case sensitive.
* @param children The list of allowed children. Can be null or empty.
*/
public DocumentDescriptor(String xml_name, ElementDescriptor[] children) {
super(xml_name, children, true /* mandatory */);
}
/**
* @return A new {@link UiElementNode} linked to this descriptor.
*/
@Override
public UiElementNode createUiNode() {
return new UiDocumentNode(this);
}
}

View File

@@ -1,318 +0,0 @@
/*
* Copyright (C) 2007 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.editors.descriptors;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.IconFactory;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.Image;
import java.util.HashSet;
import java.util.Set;
/**
* {@link ElementDescriptor} describes the properties expected for a given XML element node.
*
* {@link ElementDescriptor} have an XML name, UI name, a tooltip, an SDK url,
* an attributes list and a children list.
*
* An UI node can be "mandatory", meaning the UI node is never deleted and it may lack
* an actual XML node attached. A non-mandatory UI node MUST have an XML node attached
* and it will cease to exist when the XML node ceases to exist.
*/
public class ElementDescriptor {
/** The XML element node name. Case sensitive. */
private String mXmlName;
/** The XML element name for the user interface, typically capitalized. */
private String mUiName;
/** The list of allowed attributes. */
private AttributeDescriptor[] mAttributes;
/** The list of allowed children */
private ElementDescriptor[] mChildren;
/* An optional tooltip. Can be empty. */
private String mTooltip;
/** An optional SKD URL. Can be empty. */
private String mSdkUrl;
/** Whether this UI node must always exist (even for empty models). */
private boolean mMandatory;
/**
* Constructs a new {@link ElementDescriptor} based on its XML name, UI name,
* tooltip, SDK url, attributes list, children list and mandatory.
*
* @param xml_name The XML element node name. Case sensitive.
* @param ui_name The XML element name for the user interface, typically capitalized.
* @param tooltip An optional tooltip. Can be null or empty.
* @param sdk_url An optional SKD URL. Can be null or empty.
* @param attributes The list of allowed attributes. Can be null or empty.
* @param children The list of allowed children. Can be null or empty.
* @param mandatory Whether this node must always exist (even for empty models). A mandatory
* UI node is never deleted and it may lack an actual XML node attached. A non-mandatory
* UI node MUST have an XML node attached and it will cease to exist when the XML node
* ceases to exist.
*/
public ElementDescriptor(String xml_name, String ui_name, String tooltip, String sdk_url,
AttributeDescriptor[] attributes,
ElementDescriptor[] children,
boolean mandatory) {
mMandatory = mandatory;
mXmlName = xml_name;
mUiName = ui_name;
mTooltip = (tooltip != null && tooltip.length() > 0) ? tooltip : null;
mSdkUrl = (sdk_url != null && sdk_url.length() > 0) ? sdk_url : null;
setAttributes(attributes != null ? attributes : new AttributeDescriptor[]{});
mChildren = children != null ? children : new ElementDescriptor[]{};
}
/**
* Constructs a new {@link ElementDescriptor} based on its XML name and children list.
* The UI name is build by capitalizing the XML name.
* The UI nodes will be non-mandatory.
*
* @param xml_name The XML element node name. Case sensitive.
* @param children The list of allowed children. Can be null or empty.
* @param mandatory Whether this node must always exist (even for empty models). A mandatory
* UI node is never deleted and it may lack an actual XML node attached. A non-mandatory
* UI node MUST have an XML node attached and it will cease to exist when the XML node
* ceases to exist.
*/
public ElementDescriptor(String xml_name, ElementDescriptor[] children, boolean mandatory) {
this(xml_name, prettyName(xml_name), null, null, null, children, mandatory);
}
/**
* Constructs a new {@link ElementDescriptor} based on its XML name and children list.
* The UI name is build by capitalizing the XML name.
* The UI nodes will be non-mandatory.
*
* @param xml_name The XML element node name. Case sensitive.
* @param children The list of allowed children. Can be null or empty.
*/
public ElementDescriptor(String xml_name, ElementDescriptor[] children) {
this(xml_name, prettyName(xml_name), null, null, null, children, false);
}
/**
* Constructs a new {@link ElementDescriptor} based on its XML name.
* The UI name is build by capitalizing the XML name.
* The UI nodes will be non-mandatory.
*
* @param xml_name The XML element node name. Case sensitive.
*/
public ElementDescriptor(String xml_name) {
this(xml_name, prettyName(xml_name), null, null, null, null, false);
}
/** Returns whether this node must always exist (even for empty models) */
public boolean isMandatory() {
return mMandatory;
}
/**
* Returns the XML element node local name (case sensitive)
*/
public final String getXmlLocalName() {
int pos = mXmlName.indexOf(':');
if (pos != -1) {
return mXmlName.substring(pos+1);
}
return mXmlName;
}
/** Returns the XML element node name. Case sensitive. */
public String getXmlName() {
return mXmlName;
}
/**
* Returns the namespace of the attribute.
*/
public final String getNamespace() {
// For now we hard-code the prefix as being "android"
if (mXmlName.startsWith("android:")) { //$NON-NLs-1$
return AndroidConstants.NS_RESOURCES;
}
return ""; //$NON-NLs-1$
}
/** Returns the XML element name for the user interface, typically capitalized. */
public String getUiName() {
return mUiName;
}
/**
* Returns an optional icon for the element.
* <p/>
* By default this tries to return an icon based on the XML name of the element.
* If this fails, it tries to return the default Android logo as defined in the
* plugin. If all fails, it returns null.
*
* @return An icon for this element or null.
*/
public Image getIcon() {
IconFactory factory = IconFactory.getInstance();
int color = hasChildren() ? IconFactory.COLOR_BLUE : IconFactory.COLOR_GREEN;
int shape = hasChildren() ? IconFactory.SHAPE_RECT : IconFactory.SHAPE_CIRCLE;
Image icon = factory.getIcon(mXmlName, color, shape);
return icon != null ? icon : EditorsPlugin.getAndroidLogo();
}
/**
* Returns an optional ImageDescriptor for the element.
* <p/>
* By default this tries to return an image based on the XML name of the element.
* If this fails, it tries to return the default Android logo as defined in the
* plugin. If all fails, it returns null.
*
* @return An ImageDescriptor for this element or null.
*/
public ImageDescriptor getImageDescriptor() {
IconFactory factory = IconFactory.getInstance();
int color = hasChildren() ? IconFactory.COLOR_BLUE : IconFactory.COLOR_GREEN;
int shape = hasChildren() ? IconFactory.SHAPE_RECT : IconFactory.SHAPE_CIRCLE;
ImageDescriptor id = factory.getImageDescriptor(mXmlName, color, shape);
return id != null ? id : EditorsPlugin.getAndroidLogoDesc();
}
/* Returns the list of allowed attributes. */
public AttributeDescriptor[] getAttributes() {
return mAttributes;
}
/* Sets the list of allowed attributes. */
public void setAttributes(AttributeDescriptor[] attributes) {
mAttributes = attributes;
for (AttributeDescriptor attribute : attributes) {
attribute.setParent(this);
}
}
/** Returns the list of allowed children */
public ElementDescriptor[] getChildren() {
return mChildren;
}
/** @return True if this descriptor has children available */
public boolean hasChildren() {
return mChildren.length > 0;
}
/** Sets the list of allowed children. */
public void setChildren(ElementDescriptor[] newChildren) {
mChildren = newChildren;
}
/**
* Returns an optional tooltip. Will be null if not present.
* <p/>
* The tooltip is based on the Javadoc of the element and already processed via
* {@link DescriptorsUtils#formatTooltip(String)} to be displayed right away as
* a UI tooltip.
*/
public String getTooltip() {
return mTooltip;
}
/** Returns an optional SKD URL. Will be null if not present. */
public String getSdkUrl() {
return mSdkUrl;
}
/** Sets the optional tooltip. Can be null or empty. */
public void setTooltip(String tooltip) {
mTooltip = tooltip;
}
/** Sets the optional SDK URL. Can be null or empty. */
public void setSdkUrl(String sdkUrl) {
mSdkUrl = sdkUrl;
}
/**
* @return A new {@link UiElementNode} linked to this descriptor.
*/
public UiElementNode createUiNode() {
return new UiElementNode(this);
}
/**
* Returns the first children of this descriptor that describes the given XML element name.
* <p/>
* In recursive mode, searches the direct children first before descending in the hierarchy.
*
* @return The ElementDescriptor matching the requested XML node element name or null.
*/
public ElementDescriptor findChildrenDescriptor(String element_name, boolean recursive) {
return findChildrenDescriptorInternal(element_name, recursive, null);
}
private ElementDescriptor findChildrenDescriptorInternal(String element_name,
boolean recursive,
Set<ElementDescriptor> visited) {
if (recursive && visited == null) {
visited = new HashSet<ElementDescriptor>();
}
for (ElementDescriptor e : getChildren()) {
if (e.getXmlName().equals(element_name)) {
return e;
}
}
if (visited != null) {
visited.add(this);
}
if (recursive) {
for (ElementDescriptor e : getChildren()) {
if (visited != null) {
if (!visited.add(e)) { // Set.add() returns false if element is already present
continue;
}
}
ElementDescriptor f = e.findChildrenDescriptorInternal(element_name,
recursive, visited);
if (f != null) {
return f;
}
}
}
return null;
}
/**
* Utility helper than pretty-formats an XML Name for the UI.
* This is used by the simplified constructor that takes only an XML element name.
*
* @param xml_name The XML name to convert.
* @return The XML name with dashes replaced by spaces and capitalized.
*/
private static String prettyName(String xml_name) {
char c[] = xml_name.toCharArray();
if (c.length > 0) {
c[0] = Character.toUpperCase(c[0]);
}
return new String(c).replace("-", " "); //$NON-NLS-1$ //$NON-NLS-2$
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright (C) 2007 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.editors.descriptors;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.editors.uimodel.UiListAttributeNode;
/**
* Describes a text attribute that can only contains some predefined values.
* It is displayed by a {@link UiListAttributeNode}.
*/
public class EnumAttributeDescriptor extends ListAttributeDescriptor {
public EnumAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
String tooltip) {
super(xmlLocalName, uiName, nsUri, tooltip);
}
/**
* @return A new {@link UiListAttributeNode} linked to this descriptor.
*/
@Override
public UiAttributeNode createUiNode(UiElementNode uiParent) {
return new UiListAttributeNode(this, uiParent);
}
}

View File

@@ -1,85 +0,0 @@
/*
* 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.ide.eclipse.editors.descriptors;
import com.android.ide.eclipse.editors.ui.FlagValueCellEditor;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.editors.uimodel.UiFlagAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiListAttributeNode;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.swt.widgets.Composite;
/**
* Describes a text attribute that can only contains some predefined values.
* It is displayed by a {@link UiListAttributeNode}.
*
* Note: in Android resources, a "flag" is a list of fixed values where one or
* more values can be selected using an "or", e.g. "align='left|top'".
* By contrast, an "enum" is a list of fixed values of which only one can be
* selected at a given time, e.g. "gravity='right'".
* <p/>
* This class handles the "flag" case.
* The "enum" case is done using {@link ListAttributeDescriptor}.
*/
public class FlagAttributeDescriptor extends TextAttributeDescriptor {
private String[] mNames;
/**
* Creates a new {@link FlagAttributeDescriptor} which automatically gets its
* values from the FrameworkResourceManager.
*/
public FlagAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
String tooltip) {
super(xmlLocalName, uiName, nsUri, tooltip);
}
/**
* Creates a new {@link FlagAttributeDescriptor} which uses the provided values.
*/
public FlagAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
String tooltip, String[] names) {
super(xmlLocalName, uiName, nsUri, tooltip);
mNames = names;
}
/**
* @return The initial names of the flags. Can be null, in which case the Framework
* resource parser should be checked.
*/
public String[] getNames() {
return mNames;
}
/**
* @return A new {@link UiListAttributeNode} linked to this descriptor.
*/
@Override
public UiAttributeNode createUiNode(UiElementNode uiParent) {
return new UiFlagAttributeNode(this, uiParent);
}
// ------- IPropertyDescriptor Methods
@Override
public CellEditor createPropertyEditor(Composite parent) {
return new FlagValueCellEditor(parent);
}
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright (C) 2007 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.editors.descriptors;
import com.android.ide.eclipse.editors.ui.ListValueCellEditor;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.editors.uimodel.UiListAttributeNode;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.swt.widgets.Composite;
/**
* Describes a text attribute that can contains some predefined values.
* It is displayed by a {@link UiListAttributeNode}.
*/
public class ListAttributeDescriptor extends TextAttributeDescriptor {
private String[] mValues = null;
/**
* Creates a new {@link ListAttributeDescriptor} which automatically gets its
* values from the FrameworkResourceManager.
*/
public ListAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
String tooltip) {
super(xmlLocalName, uiName, nsUri, tooltip);
}
/**
* Creates a new {@link ListAttributeDescriptor} which uses the provided values.
*/
public ListAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
String tooltip, String[] values) {
super(xmlLocalName, uiName, nsUri, tooltip);
mValues = values;
}
public String[] getValues() {
return mValues;
}
/**
* @return A new {@link UiListAttributeNode} linked to this descriptor.
*/
@Override
public UiAttributeNode createUiNode(UiElementNode uiParent) {
return new UiListAttributeNode(this, uiParent);
}
// ------- IPropertyDescriptor Methods
@Override
public CellEditor createPropertyEditor(Composite parent) {
return new ListValueCellEditor(parent);
}
}

View File

@@ -1,84 +0,0 @@
/*
* 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.ide.eclipse.editors.descriptors;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.resources.ResourceType;
import com.android.ide.eclipse.editors.ui.ResourceValueCellEditor;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.editors.uimodel.UiResourceAttributeNode;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.swt.widgets.Composite;
/**
* Describes an XML attribute displayed containing a value or a reference to a resource.
* It is displayed by a {@link UiResourceAttributeNode}.
*/
public final class ReferenceAttributeDescriptor extends TextAttributeDescriptor {
private ResourceType mResourceType;
/**
* Creates a reference attributes that can contain any type of resources.
* @param xmlLocalName The XML name of the attribute (case sensitive)
* @param uiName The UI name of the attribute. Cannot be an empty string and cannot be null.
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
* @param tooltip A non-empty tooltip string or null
*/
public ReferenceAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
String tooltip) {
super(xmlLocalName, uiName, nsUri, tooltip);
}
/**
* Creates a reference attributes that can contain a reference to a specific
* {@link ResourceType}.
* @param resourceType The specific {@link ResourceType} that this reference attribute supports.
* It can be <code>null</code>, in which case, all resource types are supported.
* @param xmlLocalName The XML name of the attribute (case sensitive)
* @param uiName The UI name of the attribute. Cannot be an empty string and cannot be null.
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
* @param tooltip A non-empty tooltip string or null
*/
public ReferenceAttributeDescriptor(ResourceType resourceType,
String xmlLocalName, String uiName, String nsUri,
String tooltip) {
super(xmlLocalName, uiName, nsUri, tooltip);
mResourceType = resourceType;
}
/**
* @return A new {@link UiResourceAttributeNode} linked to this reference descriptor.
*/
@Override
public UiAttributeNode createUiNode(UiElementNode uiParent) {
return new UiResourceAttributeNode(mResourceType, this, uiParent);
}
// ------- IPropertyDescriptor Methods
@Override
public CellEditor createPropertyEditor(Composite parent) {
return new ResourceValueCellEditor(parent);
}
}

View File

@@ -1,45 +0,0 @@
/*
* 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.ide.eclipse.editors.descriptors;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.editors.uimodel.UiSeparatorAttributeNode;
/**
* {@link SeparatorAttributeDescriptor} does not represent any real attribute.
* <p/>
* It is used to separate groups of attributes visually.
*/
public class SeparatorAttributeDescriptor extends AttributeDescriptor {
/**
* Creates a new {@link SeparatorAttributeDescriptor}
*/
public SeparatorAttributeDescriptor(String label) {
super(label /* xmlLocalName */, null /* nsUri */);
}
/**
* @return A new {@link UiAttributeNode} linked to this descriptor or null if this
* attribute has no user interface.
*/
@Override
public UiAttributeNode createUiNode(UiElementNode uiParent) {
return new UiSeparatorAttributeNode(this, uiParent);
}
}

View File

@@ -1,133 +0,0 @@
/*
* Copyright (C) 2007 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.editors.descriptors;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.ui.TextValueCellEditor;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.editors.uimodel.UiTextAttributeNode;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.views.properties.IPropertyDescriptor;
/**
* Describes a textual XML attribute.
* <p/>
* Such an attribute has a tooltip and would typically be displayed by
* {@link UiTextAttributeNode} using a label widget and text field.
* <p/>
* This is the "default" kind of attribute. If in doubt, use this.
*/
public class TextAttributeDescriptor extends AttributeDescriptor implements IPropertyDescriptor {
private String mUiName;
private String mTooltip;
/**
* Creates a new {@link TextAttributeDescriptor}
*
* @param xmlLocalName The XML name of the attribute (case sensitive)
* @param uiName The UI name of the attribute. Cannot be an empty string and cannot be null.
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
* @param tooltip A non-empty tooltip string or null
*/
public TextAttributeDescriptor(String xmlLocalName, String uiName,
String nsUri, String tooltip) {
super(xmlLocalName, nsUri);
mUiName = uiName;
mTooltip = (tooltip != null && tooltip.length() > 0) ? tooltip : null;
}
/**
* @return The UI name of the attribute. Cannot be an empty string and cannot be null.
*/
public final String getUiName() {
return mUiName;
}
/**
* The tooltip string is either null or a non-empty string.
* <p/>
* The tooltip is based on the Javadoc of the attribute and already processed via
* {@link DescriptorsUtils#formatTooltip(String)} to be displayed right away as
* a UI tooltip.
* <p/>
* An empty string is converted to null, to match the behavior of setToolTipText() in
* {@link Control}.
*
* @return A non-empty tooltip string or null
*/
public final String getTooltip() {
return mTooltip;
}
/**
* @return A new {@link UiTextAttributeNode} linked to this descriptor.
*/
@Override
public UiAttributeNode createUiNode(UiElementNode uiParent) {
return new UiTextAttributeNode(this, uiParent);
}
// ------- IPropertyDescriptor Methods
public CellEditor createPropertyEditor(Composite parent) {
return new TextValueCellEditor(parent);
}
public String getCategory() {
ElementDescriptor parent = getParent();
if (parent != null) {
return parent.getUiName();
}
return null;
}
public String getDescription() {
return mTooltip;
}
public String getDisplayName() {
return mUiName;
}
public String[] getFilterFlags() {
return null;
}
public Object getHelpContextIds() {
return null;
}
public Object getId() {
return this;
}
public ILabelProvider getLabelProvider() {
return AttributeDescriptorLabelProvider.getProvider();
}
public boolean isCompatibleWith(IPropertyDescriptor anotherProperty) {
return anotherProperty == this;
}
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright (C) 2007 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.editors.descriptors;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.editors.uimodel.UiTextValueNode;
/**
* Describes the value of an XML element.
* <p/>
* The value is a simple text string, displayed by an {@link UiTextValueNode}.
*/
public class TextValueDescriptor extends TextAttributeDescriptor {
/**
* Creates a new {@link TextValueDescriptor}
*
* @param uiName The UI name of the attribute. Cannot be an empty string and cannot be null.
* @param tooltip A non-empty tooltip string or null
*/
public TextValueDescriptor(String uiName, String tooltip) {
super("#text" /* xmlLocalName */, uiName, null /* nsUri */, tooltip);
}
/**
* @return A new {@link UiTextValueNode} linked to this descriptor.
*/
@Override
public UiAttributeNode createUiNode(UiElementNode uiParent) {
return new UiTextValueNode(this, uiParent);
}
}

View File

@@ -1,81 +0,0 @@
/*
* Copyright (C) 2007 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.editors.descriptors;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
/**
* Describes an XMLNS attribute that is hidden.
* <p/>
* Such an attribute has no user interface and no corresponding {@link UiAttributeNode}.
* It also has a single constant default value.
* <p/>
* When loading an XML, we'll ignore this attribute.
* However when writing a new XML, we should always write this attribute.
* <p/>
* Currently this is used for the xmlns:android attribute in the manifest element.
*/
public final class XmlnsAttributeDescriptor extends AttributeDescriptor {
/**
* URI of the reserved "xmlns" prefix, as described in
* http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#normalizeDocumentAlgo
*/
public final static String XMLNS_URI = "http://www.w3.org/2000/xmlns/"; //$NON-NLS-1$
private String mValue;
public XmlnsAttributeDescriptor(String defaultPrefix, String value) {
super(defaultPrefix, XMLNS_URI);
mValue = value;
}
/**
* Returns the value of this specialized attribute descriptor, which is the URI associated
* to the declared namespace prefix.
*/
public String getValue() {
return mValue;
}
/**
* Returns the "xmlns" prefix that is always used by this node for its namespace URI.
* This is defined by the XML specification.
*/
public String getXmlNsPrefix() {
return "xmlns"; //$NON-NLS-1$
}
/**
* Returns the fully-qualified attribute name, namely "xmlns:xxx" where xxx is
* the defaultPrefix passed in the constructor.
*/
public String getXmlNsName() {
return getXmlNsPrefix() + ":" + getXmlLocalName(); //$NON-NLS-1$
}
/**
* @return Always returns null. {@link XmlnsAttributeDescriptor} has no user interface.
*/
@Override
public UiAttributeNode createUiNode(UiElementNode uiParent) {
return null;
}
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright (C) 2007 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.editors.layout;
import com.android.ide.eclipse.editors.AndroidContentAssist;
import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors;
/**
* Content Assist Processor for /res/layout XML files
*/
class LayoutContentAssist extends AndroidContentAssist {
/**
* Constructor for LayoutContentAssist
*/
public LayoutContentAssist() {
super(LayoutDescriptors.getInstance().getDescriptor().getChildren());
}
}

View File

@@ -1,140 +0,0 @@
/*
* 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.ide.eclipse.editors.layout;
import com.android.ide.eclipse.editors.IconFactory;
import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
import com.android.ide.eclipse.editors.resources.configurations.ResourceQualifier;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
import com.android.ide.eclipse.editors.wizards.ConfigurationSelector;
import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.ConfigurationState;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TrayDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
/**
* Dialog to choose a non existing {@link FolderConfiguration}.
*/
class LayoutCreatorDialog extends TrayDialog {
private ConfigurationSelector mSelector;
private Composite mStatusComposite;
private Label mStatusLabel;
private Label mStatusImage;
private final FolderConfiguration mConfig = new FolderConfiguration();
private final String mFileName;
/**
* Creates a dialog, and init the UI from a {@link FolderConfiguration}.
* @param parentShell the parent {@link Shell}.
* @param config The starting configuration.
*/
LayoutCreatorDialog(Shell parentShell, String fileName, FolderConfiguration config) {
super(parentShell);
mFileName = fileName;
// FIXME: add some data to know what configurations already exist.
mConfig.set(config);
}
@Override
protected Control createDialogArea(Composite parent) {
Composite top = new Composite(parent, SWT.NONE);
top.setLayoutData(new GridData());
top.setLayout(new GridLayout(1, false));
new Label(top, SWT.NONE).setText(
String.format("Configuration for the alternate version of %1$s", mFileName));
mSelector = new ConfigurationSelector(top);
mSelector.setConfiguration(mConfig);
// parent's layout is a GridLayout as specified in the javadoc.
GridData gd = new GridData();
gd.widthHint = ConfigurationSelector.WIDTH_HINT;
gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
mSelector.setLayoutData(gd);
// add a listener to check on the validity of the FolderConfiguration as
// they are built.
mSelector.setOnChangeListener(new Runnable() {
public void run() {
ConfigurationState state = mSelector.getState();
switch (state) {
case OK:
mSelector.getConfiguration(mConfig);
resetStatus();
mStatusImage.setImage(null);
getButton(IDialogConstants.OK_ID).setEnabled(true);
break;
case INVALID_CONFIG:
ResourceQualifier invalidQualifier = mSelector.getInvalidQualifier();
mStatusLabel.setText(String.format(
"Invalid Configuration: %1$s has no filter set.",
invalidQualifier.getName()));
mStatusImage.setImage(IconFactory.getInstance().getIcon("warning")); //$NON-NLS-1$
getButton(IDialogConstants.OK_ID).setEnabled(false);
break;
case REGION_WITHOUT_LANGUAGE:
mStatusLabel.setText(
"The Region qualifier requires the Language qualifier.");
mStatusImage.setImage(IconFactory.getInstance().getIcon("warning")); //$NON-NLS-1$
getButton(IDialogConstants.OK_ID).setEnabled(false);
break;
}
// need to relayout, because of the change in size in mErrorImage.
mStatusComposite.layout();
}
});
mStatusComposite = new Composite(top, SWT.NONE);
mStatusComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
GridLayout gl = new GridLayout(2, false);
mStatusComposite.setLayout(gl);
gl.marginHeight = gl.marginWidth = 0;
mStatusImage = new Label(mStatusComposite, SWT.NONE);
mStatusLabel = new Label(mStatusComposite, SWT.NONE);
mStatusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
resetStatus();
return top;
}
public void getConfiguration(FolderConfiguration config) {
config.set(mConfig);
}
/**
* resets the status label to show the file that will be created.
*/
private void resetStatus() {
mStatusLabel.setText(String.format("New File: res/%1$s/%2$s",
mConfig.getFolderName(ResourceFolderType.LAYOUT), mFileName));
}
}

View File

@@ -1,369 +0,0 @@
/*
* Copyright (C) 2007 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.editors.layout;
import com.android.ide.eclipse.common.EclipseUiHelper;
import com.android.ide.eclipse.editors.AndroidEditor;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor;
import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolder;
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.gef.ui.parts.TreeViewer;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IShowEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.w3c.dom.Document;
/**
* Multi-page form editor for /res/layout XML files.
*/
public class LayoutEditor extends AndroidEditor implements IShowEditorInput, IPartListener {
public static final String ID = "com.android.ide.eclipse.editors.layout.LayoutEditor"; //$NON-NLS-1$
/** Root node of the UI element hierarchy */
private UiDocumentNode mUiRootNode;
/** Listener to update the root node if the resource framework changes */
private Runnable mResourceRefreshListener;
private GraphicalLayoutEditor mGraphicalEditor;
private int mGraphicalEditorIndex;
/** Implementation of the {@link IContentOutlinePage} for this editor */
private UiContentOutlinePage mOutline;
/** Custom implementation of {@link IPropertySheetPage} for this editor */
private UiPropertySheetPage mPropertyPage;
/**
* Creates the form editor for resources XML files.
*/
public LayoutEditor() {
super();
initUiRootNode();
}
/**
* @return The root node of the UI element hierarchy
*/
@Override
public UiDocumentNode getUiRootNode() {
return mUiRootNode;
}
// ---- Base Class Overrides ----
@Override
public void dispose() {
if (mResourceRefreshListener != null) {
EditorsPlugin.getDefault().removeResourceChangedListener(mResourceRefreshListener);
mResourceRefreshListener = null;
}
getSite().getPage().removePartListener(this);
super.dispose();
}
/**
* Save the XML.
* <p/>
* The actual save operation is done in the super class by committing
* all data to the XML model and then having the Structured XML Editor
* save the XML.
* <p/>
* Here we just need to tell the graphical editor that the model has
* been saved.
*/
@Override
public void doSave(IProgressMonitor monitor) {
super.doSave(monitor);
if (mGraphicalEditor != null) {
mGraphicalEditor.doSave(monitor);
}
}
/**
* Returns whether the "save as" operation is supported by this editor.
* <p/>
* Save-As is a valid operation for the ManifestEditor since it acts on a
* single source file.
*
* @see IEditorPart
*/
@Override
public boolean isSaveAsAllowed() {
return true;
}
/**
* Create the various form pages.
*/
@Override
protected void createFormPages() {
try {
// The graphical layout editor is now enabled by default.
// In case there's an issue we provide a way to disable it using an
// env variable.
if (System.getenv("ANDROID_DISABLE_LAYOUT") == null) {
if (mGraphicalEditor == null) {
mGraphicalEditor = new GraphicalLayoutEditor(this);
mGraphicalEditorIndex = addPage(mGraphicalEditor, getEditorInput());
setPageText(mGraphicalEditorIndex, mGraphicalEditor.getTitle());
} else {
mGraphicalEditor.reloadEditor();
}
// update the config based on the opened file.
IEditorInput input = getEditorInput();
if (input instanceof FileEditorInput) {
FileEditorInput fileInput = (FileEditorInput)input;
ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(
fileInput.getFile());
if (resFolder != null) {
mGraphicalEditor.editNewFile(resFolder.getConfiguration());
}
}
// put in place the listener to handle layout recompute only when needed.
getSite().getPage().addPartListener(this);
}
} catch (PartInitException e) {
EditorsPlugin.log(e, "Error creating nested page"); //$NON-NLS-1$
}
}
/* (non-java doc)
* Change the tab/title name to include the name of the layout.
*/
@Override
protected void setInput(IEditorInput input) {
super.setInput(input);
handleNewInput(input);
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#setInputWithNotify(org.eclipse.ui.IEditorInput)
*/
@Override
protected void setInputWithNotify(IEditorInput input) {
super.setInputWithNotify(input);
handleNewInput(input);
}
/**
* Called to replace the current {@link IEditorInput} with another one.
* <p/>This is used when {@link MatchingStrategy} returned <code>true</code> which means we're
* opening a different configuration of the same layout.
*/
public void showEditorInput(IEditorInput editorInput) {
// save the current editor input.
doSave(new NullProgressMonitor());
// get the current page
int currentPage = getActivePage();
// remove the pages, except for the graphical editor, which will be dynamically adapted
// to the new model.
// page after the graphical editor:
int count = getPageCount();
for (int i = count - 1 ; i > mGraphicalEditorIndex ; i--) {
removePage(i);
}
// pages before the graphical editor
for (int i = mGraphicalEditorIndex - 1 ; i >= 0 ; i--) {
removePage(i);
}
// set the current input.
setInputWithNotify(editorInput);
// re-create or reload the pages with the default page shown as the previous active page.
createAndroidPages();
selectDefaultPage(Integer.toString(currentPage));
// update the outline
if (mOutline != null && mGraphicalEditor != null) {
mOutline.reloadModel();
}
}
/**
* Processes the new XML Model, which XML root node is given.
*
* @param xml_doc The XML document, if available, or null if none exists.
*/
@Override
protected void xmlModelChanged(Document xml_doc) {
mUiRootNode.loadFromXmlNode(xml_doc);
// update the model first, since it is used by the viewers.
super.xmlModelChanged(xml_doc);
if (mGraphicalEditor != null) {
mGraphicalEditor.onXmlModelChanged();
}
if (mOutline != null) {
mOutline.reloadModel();
}
}
/* (non-java doc)
* Returns the IContentOutlinePage when asked for it.
*/
@SuppressWarnings("unchecked")
@Override
public Object getAdapter(Class adapter) {
// for the outline, force it to come from the Graphical Editor.
// This fixes the case where a layout file is opened in XML view first and the outline
// gets stuck in the XML outline.
if (IContentOutlinePage.class == adapter && mGraphicalEditor != null) {
if (mOutline == null) {
mOutline = new UiContentOutlinePage(mGraphicalEditor, new TreeViewer());
}
return mOutline;
}
if (IPropertySheetPage.class == adapter && mGraphicalEditor != null) {
if (mPropertyPage == null) {
mPropertyPage = new UiPropertySheetPage();
}
return mPropertyPage;
}
// return default
return super.getAdapter(adapter);
}
@Override
protected void pageChange(int newPageIndex) {
super.pageChange(newPageIndex);
if (mGraphicalEditor != null && newPageIndex == mGraphicalEditorIndex) {
mGraphicalEditor.activated();
}
}
// ----- IPartListener Methods ----
public void partActivated(IWorkbenchPart part) {
if (part == this) {
if (mGraphicalEditor != null && getActivePage() == mGraphicalEditorIndex) {
mGraphicalEditor.activated();
}
}
}
public void partBroughtToTop(IWorkbenchPart part) {
partActivated(part);
}
public void partClosed(IWorkbenchPart part) {
// pass
}
public void partDeactivated(IWorkbenchPart part) {
// pass
}
public void partOpened(IWorkbenchPart part) {
EclipseUiHelper.showView(EclipseUiHelper.CONTENT_OUTLINE_VIEW_ID, false /* activate */);
EclipseUiHelper.showView(EclipseUiHelper.PROPERTY_SHEET_VIEW_ID, false /* activate */);
}
// ---- Local Methods ----
/**
* Returns true if the Graphics editor page is visible.
* This <b>must</b> be called from the UI thread.
*/
boolean isGraphicalEditorActive() {
IWorkbenchPartSite workbenchSite = getSite();
IWorkbenchPage workbenchPage = workbenchSite.getPage();
// check if the editor is visible in the workbench page
if (workbenchPage.isPartVisible(this)) {
// and then if the page of the editor is visible (not to be confused with
// the workbench page)
return mGraphicalEditorIndex == getActivePage();
}
return false;
}
/**
* Creates the initial UI Root Node, including the known mandatory elements.
*/
private void initUiRootNode() {
// The root UI node is always created, even if there's no corresponding XML node.
if (mUiRootNode == null) {
DocumentDescriptor desc = LayoutDescriptors.getInstance().getDescriptor();
mUiRootNode = (UiDocumentNode) desc.createUiNode();
mUiRootNode.setEditor(this);
// Add a listener to refresh the root node if the resource framework changes
// by forcing it to parse its own XML
mResourceRefreshListener = new Runnable() {
public void run() {
commitPages(false /* onSave */);
mUiRootNode.reloadFromXmlNode(mUiRootNode.getXmlDocument());
if (mOutline != null) {
mOutline.reloadModel();
}
if (mGraphicalEditor != null) {
mGraphicalEditor.recomputeLayout();
}
}
};
EditorsPlugin.getDefault().addResourceChangedListener(mResourceRefreshListener);
mResourceRefreshListener.run();
}
}
/**
* Handles a new input, and update the part name.
* @param input the new input.
*/
private void handleNewInput(IEditorInput input) {
if (input instanceof FileEditorInput) {
FileEditorInput fileInput = (FileEditorInput) input;
IFile file = fileInput.getFile();
setPartName(String.format("%1$s",
file.getName()));
}
}
}

View File

@@ -1,214 +0,0 @@
/*
* 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.ide.eclipse.editors.layout;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolder;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor;
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IFileListener;
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IResourceEventListener;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceDelta;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* Monitor for file changes triggering a layout redraw.
*/
public final class LayoutReloadMonitor implements IFileListener, IResourceEventListener {
// singleton, enforced by private constructor.
private final static LayoutReloadMonitor sThis = new LayoutReloadMonitor();
/**
* Map of listeners by IProject.
*/
private final Map<IProject, List<ILayoutReloadListener>> mListenerMap =
new HashMap<IProject, List<ILayoutReloadListener>>();
private final static int CHANGE_CODE = 0;
private final static int CHANGE_RESOURCES = 1;
private final static int CHANGE_R = 2;
private final static int CHANGE_COUNT = 3;
/**
* List of projects having received a file change. the boolean[] contains 3 values:
* <ul><li>CHANGE_CODE: code change flag.</li>
* <li>CHANGE_RESOURCES: resource change flag.</li>
* <li>CHANGE_R: R clas change flag</li></ul>
*/
private final Map<IProject, boolean[]> mChangedProjects = new HashMap<IProject, boolean[]>();
/**
* Classes which implement this interface provide a method to respond to resource changes
* triggering a layout redraw
*/
public interface ILayoutReloadListener {
/**
* Sent when the layout needs to be redrawn
* @param codeChange The trigger happened due to a code change.
* @param rChange The trigger happened due to a change in the R class.
* @param resChange The trigger happened due to a resource change.
*/
void reloadLayout(boolean codeChange, boolean rChange, boolean resChange);
}
/**
* Returns the single instance of {@link LayoutReloadMonitor}.
*/
public static LayoutReloadMonitor getMonitor() {
return sThis;
}
private LayoutReloadMonitor() {
ResourceMonitor monitor = ResourceMonitor.getMonitor();
monitor.addFileListener(this, IResourceDelta.ADDED | IResourceDelta.CHANGED);
monitor.addResourceEventListener(this);
}
/**
* Adds a listener for a given {@link IProject}.
* @param project
* @param listener
*/
public void addListener(IProject project, ILayoutReloadListener listener) {
synchronized (mListenerMap) {
List<ILayoutReloadListener> list = mListenerMap.get(project);
if (list == null) {
list = new ArrayList<ILayoutReloadListener>();
mListenerMap.put(project, list);
}
list.add(listener);
}
}
/**
* Removes a listener for a given {@link IProject}.
* @param project
* @param listener
*/
public void removeListener(IProject project, ILayoutReloadListener listener) {
synchronized (mListenerMap) {
List<ILayoutReloadListener> list = mListenerMap.get(project);
if (list != null) {
list.remove(listener);
}
}
}
/*
* (non-Javadoc)
* @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IFileListener#fileChanged(org.eclipse.core.resources.IFile, org.eclipse.core.resources.IMarkerDelta[], int)
*
* Callback for ResourceMonitor.IFileListener. Called when a file changed.
* This records the changes for each project, but does not notify listeners.
* @see #resourceChangeEventEnd
*/
public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
// get the file project
IProject project = file.getProject();
// if this project has already been marked as modified, we do nothing.
boolean[] changeFlags = mChangedProjects.get(project);
if (changeFlags != null && changeFlags[CHANGE_CODE] && changeFlags[CHANGE_RESOURCES] &&
changeFlags[CHANGE_R]) {
return;
}
// now check that the file is *NOT* a layout file (those automatically trigger a layout
// reload and we don't want to do it twice.
ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file);
if (resFolder != null) {
if (resFolder.getType() != ResourceFolderType.LAYOUT) {
// this is a resource change!
if (changeFlags == null) {
changeFlags = new boolean[CHANGE_COUNT];
mChangedProjects.put(project, changeFlags);
}
changeFlags[CHANGE_RESOURCES] = true;
}
} else if (AndroidConstants.EXT_CLASS.equals(file.getFileExtension())) {
if (file.getName().matches("R[\\$\\.](.*)")) {
// this is a R change!
if (changeFlags == null) {
changeFlags = new boolean[CHANGE_COUNT];
mChangedProjects.put(project, changeFlags);
}
changeFlags[CHANGE_R] = true;
} else {
// this is a code change!
if (changeFlags == null) {
changeFlags = new boolean[CHANGE_COUNT];
mChangedProjects.put(project, changeFlags);
}
changeFlags[CHANGE_CODE] = true;
}
}
}
/*
* (non-Javadoc)
* @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IResourceEventListener#resourceChangeEventStart()
*
* Callback for ResourceMonitor.IResourceEventListener. Called at the beginning of a resource
* change event. This is called once, while fileChanged can be called several times.
*
*/
public void resourceChangeEventStart() {
// nothing to be done here, it all happens in the resourceChangeEventEnd
}
/*
* (non-Javadoc)
* @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IResourceEventListener#resourceChangeEventEnd()
*
* Callback for ResourceMonitor.IResourceEventListener. Called at the end of a resource
* change event. This is where we notify the listeners.
*/
public void resourceChangeEventEnd() {
// for each IProject that was changed, we notify all the listeners.
synchronized (mListenerMap) {
for (Entry<IProject, boolean[]> project : mChangedProjects.entrySet()) {
List<ILayoutReloadListener> listeners = mListenerMap.get(project.getKey());
boolean[] flags = project.getValue();
if (listeners != null) {
for (ILayoutReloadListener listener : listeners) {
listener.reloadLayout(flags[CHANGE_CODE], flags[CHANGE_R],
flags[CHANGE_RESOURCES]);
}
}
}
}
// empty the list.
mChangedProjects.clear();
}
}

View File

@@ -1,30 +0,0 @@
/*
* 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.ide.eclipse.editors.layout;
import com.android.ide.eclipse.editors.AndroidSourceViewerConfig;
/**
* Source Viewer Configuration that calls in LayoutContentAssist.
*/
public class LayoutSourceViewerConfig extends AndroidSourceViewerConfig {
public LayoutSourceViewerConfig() {
super(new LayoutContentAssist());
}
}

View File

@@ -1,87 +0,0 @@
/*
* 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.ide.eclipse.editors.layout;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolder;
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
import com.android.ide.eclipse.editors.ui.tree.UiTreeBlock;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.core.resources.IFile;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormPage;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.part.FileEditorInput;
/**
* Page for the layout tree-based form editor.
*
* @deprecated This is being phased out. We use the GraphicalLayoutEditor instead.
*/
public final class LayoutTreePage extends FormPage {
/** Page id used for switching tabs programmatically */
public final static String PAGE_ID = "layout_tree_page"; //$NON-NLS-1$
/** Container editor */
LayoutEditor mEditor;
public LayoutTreePage(LayoutEditor editor) {
super(editor, PAGE_ID, "Layout"); // tab's label, keep it short
mEditor = editor;
}
/**
* Creates the content in the form hosted in this page.
*
* @param managedForm the form hosted in this page.
*/
@Override
protected void createFormContent(IManagedForm managedForm) {
super.createFormContent(managedForm);
ScrolledForm form = managedForm.getForm();
String configText = null;
IEditorInput input = mEditor.getEditorInput();
if (input instanceof FileEditorInput) {
FileEditorInput fileInput = (FileEditorInput)input;
IFile iFile = fileInput.getFile();
ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(iFile);
if (resFolder != null) {
configText = resFolder.getConfiguration().toDisplayString();
}
}
if (configText != null) {
form.setText(String.format("Android Layout (%1$s)", configText));
} else {
form.setText("Android Layout");
}
form.setImage(EditorsPlugin.getAndroidLogo());
UiElementNode rootNode = mEditor.getUiRootNode();
UiTreeBlock block = new UiTreeBlock(mEditor, rootNode,
true /* autoCreateRoot */,
null /* no element filters */,
"Layout Elements",
"List of all layout elements in this XML file.");
block.createContent(managedForm);
}
}

View File

@@ -1,64 +0,0 @@
/*
* 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.ide.eclipse.editors.layout;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolder;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
import org.eclipse.core.resources.IFile;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorMatchingStrategy;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.FileEditorInput;
/**
* Matching strategy for the Layout Editor. This is used to open all configurations of a layout
* in the same editor.
*/
public class MatchingStrategy implements IEditorMatchingStrategy {
public boolean matches(IEditorReference editorRef, IEditorInput input) {
// first check that the file being opened is a layout file.
if (input instanceof FileEditorInput) {
FileEditorInput fileInput = (FileEditorInput)input;
// get the IFile object and check it's in one of the layout folders.
IFile iFile = fileInput.getFile();
ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(iFile);
// if it's a layout, we know check the name of the fileInput against the name of the
// file being currently edited by the editor since those are independent of the config.
if (resFolder != null && resFolder.getType() == ResourceFolderType.LAYOUT) {
try {
IEditorInput editorInput = editorRef.getEditorInput();
if (editorInput instanceof FileEditorInput) {
FileEditorInput editorFileInput = (FileEditorInput)editorInput;
IFile editorIFile = editorFileInput.getFile();
return editorIFile.getProject().equals(iFile.getProject())
&& editorIFile.getName().equals(iFile.getName());
}
} catch (PartInitException e) {
// we do nothing, we'll just return false.
}
}
}
return false;
}
}

View File

@@ -1,124 +0,0 @@
/*
* 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.ide.eclipse.editors.layout;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors;
import org.eclipse.gef.palette.CombinedTemplateCreationEntry;
import org.eclipse.gef.palette.MarqueeToolEntry;
import org.eclipse.gef.palette.PaletteDrawer;
import org.eclipse.gef.palette.PaletteGroup;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.palette.PanningSelectionToolEntry;
import org.eclipse.gef.requests.CreationFactory;
import java.util.List;
/**
* Factory that creates the palette for the {@link GraphicalLayoutEditor}.
*/
public class PaletteFactory {
private static PaletteRoot sPaletteRoot;
private static Runnable sSdkChangedListener;
/** Static factory, nothing to instantiate here. */
private PaletteFactory() {
}
public static PaletteRoot createPaletteRoot() {
if (sSdkChangedListener == null) {
sSdkChangedListener = new Runnable() {
public void run() {
if (sPaletteRoot != null) {
// The SDK has changed. Remove the drawer groups and rebuild them.
for (int n = sPaletteRoot.getChildren().size() - 1; n >= 0; n--) {
sPaletteRoot.getChildren().remove(n);
}
addTools(sPaletteRoot);
addViews(sPaletteRoot, "Layouts",
LayoutDescriptors.getInstance().getLayoutDescriptors());
addViews(sPaletteRoot, "Views",
LayoutDescriptors.getInstance().getViewDescriptors());
}
}
};
EditorsPlugin.getDefault().addResourceChangedListener(sSdkChangedListener);
}
if (sPaletteRoot == null) {
sPaletteRoot = new PaletteRoot();
sSdkChangedListener.run();
}
return sPaletteRoot;
}
private static void addTools(PaletteRoot paletteRoot) {
PaletteGroup group = new PaletteGroup("Tools");
// Default tools: selection and marquee selection
PanningSelectionToolEntry entry = new PanningSelectionToolEntry();
group.add(entry);
paletteRoot.setDefaultEntry(entry);
group.add(new MarqueeToolEntry());
paletteRoot.add(group);
}
private static void addViews(PaletteRoot paletteRoot, String groupName,
List<ElementDescriptor> descriptors) {
PaletteDrawer group = new PaletteDrawer(groupName);
for (ElementDescriptor desc : descriptors) {
CombinedTemplateCreationEntry entry = new CombinedTemplateCreationEntry(
desc.getUiName(), // label
desc.getTooltip(), // short description
desc.getClass(), // template
new ElementFactory(desc), // factory
desc.getImageDescriptor(), // small icon
desc.getImageDescriptor() // large icon
);
group.add(entry);
}
paletteRoot.add(group);
}
//------------------------------------------
public static class ElementFactory implements CreationFactory {
private final ElementDescriptor mDescriptor;
public ElementFactory(ElementDescriptor descriptor) {
mDescriptor = descriptor;
}
public Object getNewObject() {
return mDescriptor;
}
public Object getObjectType() {
return mDescriptor;
}
}
}

View File

@@ -1,155 +0,0 @@
/*
* 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.ide.eclipse.editors.layout;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestHelper;
import com.android.ide.eclipse.editors.resources.manager.ProjectClassLoader;
import com.android.ide.eclipse.editors.resources.manager.ProjectResources;
import com.android.layoutlib.api.IProjectCallback;
import org.eclipse.core.resources.IProject;
import java.lang.reflect.Constructor;
import java.util.HashMap;
/**
* Loader for Android Project class in order to use them in the layout editor.
*/
public final class ProjectCallback implements IProjectCallback {
private final HashMap<String, Class<?>> mLoadedClasses = new HashMap<String, Class<?>>();
private final IProject mProject;
private final ClassLoader mParentClassLoader;
private final ProjectResources mProjectRes;
private boolean mUsed = false;
private String mNamespace;
ProjectCallback(ClassLoader classLoader, ProjectResources projectRes, IProject project) {
mParentClassLoader = classLoader;
mProjectRes = projectRes;
mProject = project;
}
/**
* {@inheritDoc}
*
* This implementation goes through the output directory of the Eclipse project and loads the
* <code>.class</code> file directly.
*/
@SuppressWarnings("unchecked")
public Object loadView(String className, Class[] constructorSignature,
Object[] constructorParameters)
throws ClassNotFoundException, Exception {
// look for a cached version
Class<?> clazz = mLoadedClasses.get(className);
if (clazz != null) {
return instantiateClass(clazz, constructorSignature, constructorParameters);
}
// load the class.
ProjectClassLoader loader = new ProjectClassLoader(mParentClassLoader, mProject);
clazz = loader.loadClass(className);
if (clazz != null) {
mUsed = true;
mLoadedClasses.put(className, clazz);
return instantiateClass(clazz, constructorSignature, constructorParameters);
}
return null;
}
/**
* {@inheritDoc}
*
* Returns the namespace for the project. The namespace contains a standard part + the
* application package.
*/
public String getNamespace() {
if (mNamespace == null) {
AndroidManifestHelper manifest = new AndroidManifestHelper(mProject);
String javaPackage = manifest.getPackageName();
mNamespace = String.format(AndroidConstants.NS_CUSTOM_RESOURCES, javaPackage);
}
return mNamespace;
}
/*
* (non-Javadoc)
* @see com.android.layoutlib.api.IProjectCallback#resolveResourceValue(int)
*/
public String[] resolveResourceValue(int id) {
if (mProjectRes != null) {
return mProjectRes.resolveResourceValue(id);
}
return null;
}
/*
* (non-Javadoc)
* @see com.android.layoutlib.api.IProjectCallback#resolveResourceValue(int[])
*/
public String resolveResourceValue(int[] id) {
if (mProjectRes != null) {
return mProjectRes.resolveResourceValue(id);
}
return null;
}
/*
* (non-Javadoc)
* @see com.android.layoutlib.api.IProjectCallback#getResourceValue(java.lang.String, java.lang.String)
*/
public Integer getResourceValue(String type, String name) {
if (mProjectRes != null) {
return mProjectRes.getResourceValue(type, name);
}
return null;
}
/**
* Returns whether the loader has received requests to load custom views.
* <p/>This allows to efficiently only recreate when needed upon code change in the project.
*/
boolean isUsed() {
return mUsed;
}
/**
* Instantiate a class object, using a specific constructor and parameters.
* @param clazz the class to instantiate
* @param constructorSignature the signature of the constructor to use
* @param constructorParameters the parameters to use in the constructor.
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
private Object instantiateClass(Class<?> clazz, Class[] constructorSignature,
Object[] constructorParameters) throws Exception {
Constructor<?> constructor = clazz.getConstructor(constructorSignature);
constructor.setAccessible(true);
return constructor.newInstance(constructorParameters);
}
}

View File

@@ -1,568 +0,0 @@
/*
* Copyright (C) 2008 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.ide.eclipse.editors.layout;
import com.android.ide.eclipse.common.EclipseUiHelper;
import com.android.ide.eclipse.editors.IconFactory;
import com.android.ide.eclipse.editors.layout.parts.UiDocumentTreeEditPart;
import com.android.ide.eclipse.editors.layout.parts.UiElementTreeEditPart;
import com.android.ide.eclipse.editors.layout.parts.UiElementTreeEditPartFactory;
import com.android.ide.eclipse.editors.layout.parts.UiLayoutTreeEditPart;
import com.android.ide.eclipse.editors.layout.parts.UiViewTreeEditPart;
import com.android.ide.eclipse.editors.ui.tree.CopyCutAction;
import com.android.ide.eclipse.editors.ui.tree.PasteAction;
import com.android.ide.eclipse.editors.ui.tree.UiActions;
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.ui.parts.ContentOutlinePage;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
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.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.swt.SWT;
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.Control;
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.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IActionBars;
import java.util.LinkedList;
/**
* Implementation of the {@link ContentOutlinePage} to display {@link UiElementNode}.
*/
class UiContentOutlinePage extends ContentOutlinePage {
private GraphicalLayoutEditor mEditor;
private Action mAddAction;
private Action mDeleteAction;
private Action mUpAction;
private Action mDownAction;
private UiOutlineActions mUiActions = new UiOutlineActions();
public UiContentOutlinePage(GraphicalLayoutEditor editor, final EditPartViewer viewer) {
super(viewer);
mEditor = editor;
IconFactory factory = IconFactory.getInstance();
mAddAction = new Action("Add...") {
@Override
public void run() {
UiElementNode node = getModelSelection();
mUiActions.doAdd(node, viewer.getControl().getShell());
}
};
mAddAction.setToolTipText("Adds a new element.");
mAddAction.setImageDescriptor(factory.getImageDescriptor("add")); //$NON-NLS-1$
mDeleteAction = new Action("Remove...") {
@Override
public void run() {
UiElementNode node = getModelSelection();
mUiActions.doRemove(node, viewer.getControl().getShell());
}
};
mDeleteAction.setToolTipText("Removes an existing selected element.");
mDeleteAction.setImageDescriptor(factory.getImageDescriptor("delete")); //$NON-NLS-1$
mUpAction = new Action("Up") {
@Override
public void run() {
UiElementNode node = getModelSelection();
mUiActions.doUp(node);
}
};
mUpAction.setToolTipText("Moves the selected element up");
mUpAction.setImageDescriptor(factory.getImageDescriptor("up")); //$NON-NLS-1$
mDownAction = new Action("Down") {
@Override
public void run() {
UiElementNode node = getModelSelection();
mUiActions.doDown(node);
}
};
mDownAction.setToolTipText("Moves the selected element down");
mDownAction.setImageDescriptor(factory.getImageDescriptor("down")); //$NON-NLS-1$
// all actions disabled by default.
mAddAction.setEnabled(false);
mDeleteAction.setEnabled(false);
mUpAction.setEnabled(false);
mDownAction.setEnabled(false);
addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
ISelection selection = event.getSelection();
// the selection is never empty. The least it'll contain is the
// UiDocumentTreeEditPart object.
if (selection instanceof StructuredSelection) {
StructuredSelection structSel = (StructuredSelection)selection;
if (structSel.size() == 1 &&
structSel.getFirstElement() instanceof UiDocumentTreeEditPart) {
mDeleteAction.setEnabled(false);
mUpAction.setEnabled(false);
mDownAction.setEnabled(false);
} else {
mDeleteAction.setEnabled(true);
mUpAction.setEnabled(true);
mDownAction.setEnabled(true);
}
// the "add" button is always enabled, in order to be able to set the
// initial root node
mAddAction.setEnabled(true);
}
}
});
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.IPage#createControl(org.eclipse.swt.widgets.Composite)
*/
@Override
public void createControl(Composite parent) {
// create outline viewer page
getViewer().createControl(parent);
// configure outline viewer
getViewer().setEditPartFactory(new UiElementTreeEditPartFactory());
setupOutline();
setupContextMenu();
setupTooltip();
setupDoubleClick();
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.part.Page#setActionBars(org.eclipse.ui.IActionBars)
*
* Called automatically after createControl
*/
@Override
public void setActionBars(IActionBars actionBars) {
IToolBarManager toolBarManager = actionBars.getToolBarManager();
toolBarManager.add(mAddAction);
toolBarManager.add(mDeleteAction);
toolBarManager.add(new Separator());
toolBarManager.add(mUpAction);
toolBarManager.add(mDownAction);
IMenuManager menuManager = actionBars.getMenuManager();
menuManager.add(mAddAction);
menuManager.add(mDeleteAction);
menuManager.add(new Separator());
menuManager.add(mUpAction);
menuManager.add(mDownAction);
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.IPage#dispose()
*/
@Override
public void dispose() {
breakConnectionWithEditor();
// dispose
super.dispose();
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.IPage#getControl()
*/
@Override
public Control getControl() {
return getViewer().getControl();
}
void setNewEditor(GraphicalLayoutEditor editor) {
mEditor = editor;
setupOutline();
}
void breakConnectionWithEditor() {
// unhook outline viewer
mEditor.getSelectionSynchronizer().removeViewer(getViewer());
}
private void setupOutline() {
getViewer().setEditDomain(mEditor.getEditDomain());
// hook outline viewer
mEditor.getSelectionSynchronizer().addViewer(getViewer());
// initialize outline viewer with model
getViewer().setContents(mEditor.getModel());
}
private void setupContextMenu() {
MenuManager menuManager = new MenuManager();
menuManager.setRemoveAllWhenShown(true);
menuManager.addMenuListener(new IMenuListener() {
/**
* The menu is about to be shown. The menu manager has already been
* requested to remove any existing menu item. This method gets the
* tree selection and if it is of the appropriate type it re-creates
* the necessary actions.
*/
public void menuAboutToShow(IMenuManager manager) {
UiElementNode selected = getModelSelection();
if (selected != null) {
doCreateMenuAction(manager, selected);
return;
}
doCreateMenuAction(manager, null /* ui_node */);
}
});
Control control = getControl();
Menu contextMenu = menuManager.createContextMenu(control);
control.setMenu(contextMenu);
}
/**
* Adds the menu actions to the context menu when the given UI node is selected in
* the tree view.
*
* @param manager The context menu manager
* @param ui_node The UI node selected in the tree. Can be null, in which case the root
* is to be modified.
*/
private void doCreateMenuAction(IMenuManager manager, UiElementNode ui_node) {
if (ui_node != null && ui_node.getXmlNode() != null) {
manager.add(new CopyCutAction(mEditor.getLayoutEditor(), mEditor.getClipboard(),
null, ui_node, true /* cut */));
manager.add(new CopyCutAction(mEditor.getLayoutEditor(), mEditor.getClipboard(),
null, ui_node, false /* cut */));
// Paste is not valid if it would add a second element on a terminal element
// which parent is a document -- an XML document can only have one child. This
// means paste is valid if the current UI node can have children or if the parent
// is not a document.
UiElementNode ui_root = ui_node.getUiRoot();
if (ui_root.getDescriptor().hasChildren() ||
!(ui_root.getUiParent() instanceof UiDocumentNode)) {
manager.add(new PasteAction(mEditor.getLayoutEditor(), mEditor.getClipboard(),
ui_node));
}
manager.add(new Separator());
}
// Append "add" and "remove" actions. They do the same thing as the add/remove
// buttons on the side.
manager.add(mAddAction);
manager.add(mDeleteAction);
manager.add(new Separator());
manager.add(mUpAction);
manager.add(mDownAction);
manager.add(new Separator());
Action propertiesAction = new Action("Properties") {
@Override
public void run() {
EclipseUiHelper.showView(EclipseUiHelper.PROPERTY_SHEET_VIEW_ID,
true /* activate */);
}
};
propertiesAction.setToolTipText("Displays properties of the selected element.");
manager.add(propertiesAction);
}
/**
* Updates the outline view with the model of the {@link GraphicalLayoutEditor}.
* <p/>
* This attemps to preserve the selection, if any.
*/
public void reloadModel() {
// Attemps to preserve the UiNode selection, if any
UiElementNode uiNode = null;
try {
// get current selection using the model rather than the edit part as
// reloading the content may change the actual edit part.
uiNode = getModelSelection();
// perform the update
getViewer().setContents(mEditor.getModel());
} finally {
// restore selection
setModelSelection(uiNode);
}
}
/**
* Returns the currently selected element, if any, in the viewer.
* This returns the viewer's elements (i.e. an {@link UiElementTreeEditPart})
* and not the underlying model node.
* <p/>
* When there is no actual selection, this might still return the root node,
* which is of type {@link UiDocumentTreeEditPart}.
*/
private UiElementTreeEditPart getViewerSelection() {
ISelection selection = getSelection();
if (selection instanceof StructuredSelection) {
StructuredSelection structuredSelection = (StructuredSelection)selection;
if (structuredSelection.size() == 1) {
Object selectedObj = structuredSelection.getFirstElement();
if (selectedObj instanceof UiElementTreeEditPart) {
return (UiElementTreeEditPart) selectedObj;
}
}
}
return null;
}
/**
* Returns the currently selected model element, which is either an
* {@link UiViewTreeEditPart} or an {@link UiLayoutTreeEditPart}.
* <p/>
* Returns null if there is no selection or if the implicit root is "selected"
* (which actually represents the lack of a real element selection.)
*/
private UiElementNode getModelSelection() {
UiElementTreeEditPart part = getViewerSelection();
if (part instanceof UiViewTreeEditPart || part instanceof UiLayoutTreeEditPart) {
return (UiElementNode) part.getModel();
}
return null;
}
/**
* Selects the corresponding edit part in the tree viewer.
*/
private void setViewerSelection(UiElementTreeEditPart selectedPart) {
if (selectedPart != null && !(selectedPart instanceof UiDocumentTreeEditPart)) {
LinkedList<UiElementTreeEditPart> segments = new LinkedList<UiElementTreeEditPart>();
for (UiElementTreeEditPart part = selectedPart;
!(part instanceof UiDocumentTreeEditPart);
part = (UiElementTreeEditPart) part.getParent()) {
segments.add(0, part);
}
setSelection(new TreeSelection(new TreePath(segments.toArray())));
}
}
/**
* Selects the corresponding model element in the tree viewer.
*/
private void setModelSelection(UiElementNode uiNodeToSelect) {
if (uiNodeToSelect != null) {
// find an edit part that has the requested model element
UiElementTreeEditPart part = findPartForModel(
(UiElementTreeEditPart) getViewer().getContents(),
uiNodeToSelect);
// if we found a part, select it and reveal it
if (part != null) {
setViewerSelection(part);
getViewer().reveal(part);
}
}
}
/**
* Utility method that tries to find an edit part that matches a given model UI node.
*
* @param rootPart The root of the viewer edit parts
* @param uiNode The UI node model to find
* @return The part that matches the model or null if it's not in the sub tree.
*/
private UiElementTreeEditPart findPartForModel(UiElementTreeEditPart rootPart,
UiElementNode uiNode) {
if (rootPart.getModel() == uiNode) {
return rootPart;
}
for (Object part : rootPart.getChildren()) {
if (part instanceof UiElementTreeEditPart) {
UiElementTreeEditPart found = findPartForModel(
(UiElementTreeEditPart) part, uiNode);
if (found != null) {
return found;
}
}
}
return null;
}
/**
* 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;
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 UiElementTreeEditPart) {
Object model = ((UiElementTreeEditPart) data).getModel();
if (model instanceof UiElementNode) {
tooltip = ((UiElementNode) model).getDescriptor().getTooltip();
}
}
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);
Point pt = tree.toDisplay(rect.x, 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);
}
/**
* Sets up double-click action on the tree.
* <p/>
* By default, double-click (a.k.a. "default selection") on a valid list item will
* show the property view.
*/
private void setupDoubleClick() {
final Tree tree = (Tree) getControl();
tree.addListener(SWT.DefaultSelection, new Listener() {
public void handleEvent(Event event) {
EclipseUiHelper.showView(EclipseUiHelper.PROPERTY_SHEET_VIEW_ID,
true /* activate */);
}
});
}
// ---------------
private class UiOutlineActions extends UiActions {
@Override
protected UiDocumentNode getRootNode() {
return mEditor.getModel(); // this is LayoutEditor.getUiRootNode()
}
// Select the new item
@Override
protected void selectUiNode(UiElementNode uiNodeToSelect) {
setModelSelection(uiNodeToSelect);
}
@Override
public void commitPendingXmlChanges() {
// Pass. There is nothing to commit before the XML is changed here.
}
}
}

View File

@@ -1,405 +0,0 @@
/*
* 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.ide.eclipse.editors.layout;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import com.android.layoutlib.api.IXmlPullParser;
import org.w3c.dom.Node;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
/**
* XmlPullParser implementation on top of {@link UiElementNode}.
* <p/>It's designed to work on layout files, and will most likely not work on other resource
* files.
*/
public final class UiElementPullParser implements IXmlPullParser {
private final ArrayList<UiElementNode> mNodeStack = new ArrayList<UiElementNode>();
private int mParsingState = START_DOCUMENT;
private UiElementNode mRoot;
public UiElementPullParser(UiElementNode top) {
mRoot = top;
push(mRoot);
}
private UiElementNode getCurrentNode() {
if (mNodeStack.size() > 0) {
return mNodeStack.get(mNodeStack.size()-1);
}
return null;
}
private Node getAttribute(int i) {
if (mParsingState != START_TAG) {
throw new IndexOutOfBoundsException();
}
// get the current uiNode
UiElementNode uiNode = getCurrentNode();
// get its xml node
Node xmlNode = uiNode.getXmlNode();
if (xmlNode != null) {
return xmlNode.getAttributes().item(i);
}
return null;
}
private void push(UiElementNode node) {
mNodeStack.add(node);
}
private UiElementNode pop() {
return mNodeStack.remove(mNodeStack.size()-1);
}
// ------------- IXmlPullParser --------
/**
* {@inheritDoc}
*
* This implementation returns the underlying DOM node.
*/
public Object getViewKey() {
return getCurrentNode();
}
// ------------- XmlPullParser --------
public void setFeature(String name, boolean state) throws XmlPullParserException {
if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
return;
}
if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) {
return;
}
throw new XmlPullParserException("Unsupported feature: " + name);
}
public boolean getFeature(String name) {
if (FEATURE_PROCESS_NAMESPACES.equals(name)) {
return true;
}
if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {
return true;
}
return false;
}
public void setProperty(String name, Object value) throws XmlPullParserException {
throw new XmlPullParserException("setProperty() not supported");
}
public Object getProperty(String name) {
return null;
}
public void setInput(Reader in) throws XmlPullParserException {
throw new XmlPullParserException("setInput() not supported");
}
public void setInput(InputStream inputStream, String inputEncoding)
throws XmlPullParserException {
throw new XmlPullParserException("setInput() not supported");
}
public void defineEntityReplacementText(String entityName, String replacementText)
throws XmlPullParserException {
throw new XmlPullParserException("defineEntityReplacementText() not supported");
}
public String getNamespacePrefix(int pos) throws XmlPullParserException {
throw new XmlPullParserException("getNamespacePrefix() not supported");
}
public String getInputEncoding() {
return null;
}
public String getNamespace(String prefix) {
throw new RuntimeException("getNamespace() not supported");
}
public int getNamespaceCount(int depth) throws XmlPullParserException {
throw new XmlPullParserException("getNamespaceCount() not supported");
}
public String getPositionDescription() {
return "XML DOM element depth:" + mNodeStack.size();
}
public String getNamespaceUri(int pos) throws XmlPullParserException {
throw new XmlPullParserException("getNamespaceUri() not supported");
}
public int getColumnNumber() {
return -1;
}
public int getLineNumber() {
return -1;
}
public int getAttributeCount() {
UiElementNode node = getCurrentNode();
if (node != null) {
return node.getUiAttributes().size();
}
return 0;
}
public String getAttributeName(int i) {
Node attribute = getAttribute(i);
if (attribute != null) {
return attribute.getLocalName();
}
return null;
}
public String getAttributeNamespace(int i) {
Node attribute = getAttribute(i);
if (attribute != null) {
return attribute.getNamespaceURI();
}
return ""; //$NON-NLS-1$
}
public String getAttributePrefix(int i) {
Node attribute = getAttribute(i);
if (attribute != null) {
return attribute.getPrefix();
}
return null;
}
public String getAttributeType(int arg0) {
return "CDATA";
}
public String getAttributeValue(int i) {
Node attribute = getAttribute(i);
if (attribute != null) {
return attribute.getNodeValue();
}
return null;
}
public String getAttributeValue(String namespace, String localName) {
// get the current uiNode
UiElementNode uiNode = getCurrentNode();
// get its xml node
Node xmlNode = uiNode.getXmlNode();
if (xmlNode != null) {
Node attribute = xmlNode.getAttributes().getNamedItemNS(namespace, localName);
if (attribute != null) {
return attribute.getNodeValue();
}
}
return null;
}
public int getDepth() {
return mNodeStack.size();
}
public int getEventType() throws XmlPullParserException {
return mParsingState;
}
public String getName() {
if (mParsingState == START_TAG || mParsingState == END_TAG) {
return getCurrentNode().getDescriptor().getXmlLocalName();
}
return null;
}
public String getNamespace() {
if (mParsingState == START_TAG || mParsingState == END_TAG) {
return getCurrentNode().getDescriptor().getNamespace();
}
return null;
}
public String getPrefix() {
if (mParsingState == START_TAG || mParsingState == END_TAG) {
// FIXME will NEVER work
if (getCurrentNode().getDescriptor().getXmlLocalName().startsWith("android:")) { //$NON-NLS-1$
return "android"; //$NON-NLS-1$
}
}
return null;
}
public String getText() {
return null;
}
public char[] getTextCharacters(int[] arg0) {
return null;
}
public boolean isAttributeDefault(int arg0) {
return false;
}
public boolean isEmptyElementTag() throws XmlPullParserException {
if (mParsingState == START_TAG) {
return getCurrentNode().getUiChildren().size() == 0;
}
throw new XmlPullParserException("Must be on START_TAG");
}
public boolean isWhitespace() throws XmlPullParserException {
return false;
}
public int next() throws XmlPullParserException, IOException {
UiElementNode node;
switch (mParsingState) {
case END_DOCUMENT:
throw new XmlPullParserException("Nothing after the end");
case START_DOCUMENT:
/* intended fall-through */
case START_TAG:
// get the current node, and look for text or children (children first)
node = getCurrentNode();
List<UiElementNode> children = node.getUiChildren();
if (children.size() > 0) {
// move to the new child, and don't change the state.
push(children.get(0));
// in case the current state is CURRENT_DOC, we set the proper state.
mParsingState = START_TAG;
} else {
if (mParsingState == START_DOCUMENT) {
// this handles the case where there's no node.
mParsingState = END_DOCUMENT;
} else {
mParsingState = END_TAG;
}
}
break;
case END_TAG:
// look for a sibling. if no sibling, go back to the parent
node = getCurrentNode();
node = node.getUiNextSibling();
if (node != null) {
// to go to the sibling, we need to remove the current node,
pop();
// and add its sibling.
push(node);
mParsingState = START_TAG;
} else {
// move back to the parent
pop();
// we have only one element left (mRoot), then we're done with the document.
if (mNodeStack.size() == 1) {
mParsingState = END_DOCUMENT;
} else {
mParsingState = END_TAG;
}
}
break;
case TEXT:
// not used
break;
case CDSECT:
// not used
break;
case ENTITY_REF:
// not used
break;
case IGNORABLE_WHITESPACE:
// not used
break;
case PROCESSING_INSTRUCTION:
// not used
break;
case COMMENT:
// not used
break;
case DOCDECL:
// not used
break;
}
return mParsingState;
}
public int nextTag() throws XmlPullParserException, IOException {
int eventType = next();
if (eventType != START_TAG && eventType != END_TAG) {
throw new XmlPullParserException("expected start or end tag", this, null);
}
return eventType;
}
public String nextText() throws XmlPullParserException, IOException {
if (getEventType() != START_TAG) {
throw new XmlPullParserException("parser must be on START_TAG to read next text", this,
null);
}
int eventType = next();
if (eventType == TEXT) {
String result = getText();
eventType = next();
if (eventType != END_TAG) {
throw new XmlPullParserException(
"event TEXT it must be immediately followed by END_TAG", this, null);
}
return result;
} else if (eventType == END_TAG) {
return "";
} else {
throw new XmlPullParserException("parser must be on START_TAG or TEXT to read text",
this, null);
}
}
public int nextToken() throws XmlPullParserException, IOException {
return next();
}
public void require(int type, String namespace, String name) throws XmlPullParserException,
IOException {
if (type != getEventType() || (namespace != null && !namespace.equals(getNamespace()))
|| (name != null && !name.equals(getName())))
throw new XmlPullParserException("expected " + TYPES[type] + getPositionDescription());
}
}

View File

@@ -1,139 +0,0 @@
/*
* 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.ide.eclipse.editors.layout;
import org.eclipse.swt.SWT;
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.views.properties.PropertySheetEntry;
import org.eclipse.ui.views.properties.PropertySheetPage;
/**
* A customized property sheet page for the graphical layout editor.
* <p/>
* Currently it just provides a custom tooltip to display attributes javadocs.
*/
public class UiPropertySheetPage extends PropertySheetPage {
public UiPropertySheetPage() {
super();
}
@Override
public void createControl(Composite parent) {
super.createControl(parent);
setupTooltip();
}
/**
* 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;
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);
Point pt = tree.toDisplay(rect.x, 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);
}
}

View File

@@ -1,262 +0,0 @@
/*
* 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.ide.eclipse.editors.layout.descriptors;
import com.android.ide.eclipse.common.resources.ViewClassInfo;
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import java.util.HashMap;
import java.util.List;
/**
* Service responsible for creating/managing {@link ElementDescriptor} objects for custom
* View classes per project.
* <p/>
* The service provides an on-demand monitoring of custom classes to check for changes. Monitoring
* starts once a request for an {@link ElementDescriptor} object has been done for a specific
* class.<br>
* The monitoring will notify a listen of any changes in the class triggering a change in its
* associated {@link ElementDescriptor} object.
* <p/>
* If the custom class does not exist, no monitoring is put in place to avoid having to listen
* to all class changes in the projects.
*
*/
public final class CustomViewDescriptorService {
private static CustomViewDescriptorService sThis = new CustomViewDescriptorService();
/**
* Map where keys are the project, and values are another map containing all the known
* custom View class for this project. The custom View class are stored in a map
* where the keys are the fully qualified class name, and the values are their associated
* {@link ElementDescriptor}.
*/
private HashMap<IProject, HashMap<String, ElementDescriptor>> mCustomDescriptorMap =
new HashMap<IProject, HashMap<String, ElementDescriptor>>();
/**
* TODO will be used to update the ElementDescriptor of the custom view when it
* is modified (either the class itself or its attributes.xml)
*/
@SuppressWarnings("unused")
private ICustomViewDescriptorListener mListener;
/**
* Classes which implements this interface provide a method that deal with modifications
* in custom View class triggering a change in its associated {@link ViewClassInfo} object.
*/
public interface ICustomViewDescriptorListener {
/**
* Sent when a custom View class has changed and its {@link ElementDescriptor} was modified.
* @param project the project containing the class.
* @param className the fully qualified class name.
* @param descriptor the updated ElementDescriptor.
*/
public void updatedClassInfo(IProject project, String className, ElementDescriptor descriptor);
}
/**
* Returns the singleton instance of {@link CustomViewDescriptorService}.
*/
public static CustomViewDescriptorService getInstance() {
return sThis;
}
/**
* Sets the listener receiving custom View class modification notifications.
* @param listener the listener to receive the notifications.
*
* TODO will be used to update the ElementDescriptor of the custom view when it
* is modified (either the class itself or its attributes.xml)
*/
public void setListener(ICustomViewDescriptorListener listener) {
mListener = listener;
}
/**
* Returns the {@link ElementDescriptor} for a particular project/class.
* <p/>
* If it is the first time the <code>ElementDescriptor</code> is requested, the method
* will check that the specified class is in fact a custom View class. Once this is
* established, a monitoring for that particular class is initiated. Any change will
* trigger a notification to the {@link ICustomViewDescriptorListener}.
* @param project the project containing the class.
* @param fqClassName the fully qualified name of the class.
* @return a <code>ElementDescriptor</code> or <code>null</code> if the class was not
* a custom View class.
*/
public ElementDescriptor getDescriptor(IProject project, String fqClassName) {
// look in the map first
synchronized (mCustomDescriptorMap) {
HashMap<String, ElementDescriptor> map = mCustomDescriptorMap.get(project);
if (map != null) {
ElementDescriptor descriptor = map.get(fqClassName);
if (descriptor != null) {
return descriptor;
}
}
// if we step here, it looks like we haven't created it yet.
// First lets check this is in fact a valid type in the project
try {
// We expect the project to be both opened and of java type (since it's an android
// project), so we can create a IJavaProject object from our IProject.
IJavaProject javaProject = JavaCore.create(project);
// replace $ by . in the class name
String javaClassName = fqClassName.replaceAll("\\$", "\\."); //$NON-NLS-1$ //$NON-NLS-2$
// look for the IType object for this class
IType type = javaProject.findType(javaClassName);
if (type != null && type.exists()) {
// the type exists. Let's get the parent class and its ViewClassInfo.
// get the type hierarchy
ITypeHierarchy hierarchy = type.newSupertypeHierarchy(
new NullProgressMonitor());
ElementDescriptor parentDescriptor = getDescriptor(
hierarchy.getSuperclass(type), project, hierarchy);
if (parentDescriptor != null) {
// we have a valid parent, lets create a new ElementDescriptor.
ViewElementDescriptor descriptor = new ViewElementDescriptor(fqClassName,
fqClassName, // ui_name
fqClassName, // canonical class name
null, // tooltip
null, // sdk_url
getAttributeDescriptor(type, parentDescriptor),
null, // layout attributes
null, // children
false /* mandatory */);
synchronized (mCustomDescriptorMap) {
map = mCustomDescriptorMap.get(project);
if (map == null) {
map = new HashMap<String, ElementDescriptor>();
mCustomDescriptorMap.put(project, map);
}
map.put(fqClassName, descriptor);
}
//TODO setup listener on this resource change.
return descriptor;
}
}
} catch (JavaModelException e) {
// there was an error accessing any of the IType, we'll just return null;
}
}
return null;
}
/**
* Computes (if needed) and returns the {@link ElementDescriptor} for the specified type.
*
* @param type
* @param project
* @param typeHierarchy
* @return A ViewElementDescriptor
*/
private ViewElementDescriptor getDescriptor(IType type, IProject project,
ITypeHierarchy typeHierarchy) {
// check if the type is a built-in View class.
List<ElementDescriptor> builtInList = LayoutDescriptors.getInstance().getViewDescriptors();
String canonicalName = type.getFullyQualifiedName();
for (ElementDescriptor desc : builtInList) {
if (desc instanceof ViewElementDescriptor) {
ViewElementDescriptor viewDescriptor = (ViewElementDescriptor)desc;
if (canonicalName.equals(viewDescriptor.getCanonicalClassName())) {
return viewDescriptor;
}
}
}
// it's not a built-in class? Lets look if the superclass is built-in
IType parentType = typeHierarchy.getSuperclass(type);
if (parentType != null) {
ViewElementDescriptor parentDescriptor = getDescriptor(parentType, project,
typeHierarchy);
if (parentDescriptor != null) {
// parent class is a valid View class with a descriptor, so we create one
// for this class.
ViewElementDescriptor descriptor = new ViewElementDescriptor(canonicalName,
canonicalName, // ui_name
canonicalName, // canonical name
null, // tooltip
null, // sdk_url
getAttributeDescriptor(type, parentDescriptor),
null, // layout attributes
null, // children
false /* mandatory */);
// add it to the map
synchronized (mCustomDescriptorMap) {
HashMap<String, ElementDescriptor> map = mCustomDescriptorMap.get(project);
if (map == null) {
map = new HashMap<String, ElementDescriptor>();
mCustomDescriptorMap.put(project, map);
}
map.put(canonicalName, descriptor);
}
//TODO setup listener on this resource change.
return descriptor;
}
}
// class is neither a built-in view class, nor extend one. return null.
return null;
}
/**
* Returns the array of {@link AttributeDescriptor} for the specified {@link IType}.
* <p/>
* The array should contain the descriptor for this type and all its supertypes.
* @param type the type for which the {@link AttributeDescriptor} are returned.
* @param parentDescriptor the {@link ElementDescriptor} of the direct superclass.
*/
private AttributeDescriptor[] getAttributeDescriptor(IType type,
ElementDescriptor parentDescriptor) {
// TODO add the class attribute descriptors to the parent descriptors.
return parentDescriptor.getAttributes();
}
}

View File

@@ -1,200 +0,0 @@
/*
* 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.ide.eclipse.editors.layout.descriptors;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.resources.ViewClassInfo;
import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo;
import com.android.ide.eclipse.common.resources.ViewClassInfo.LayoutParamsInfo;
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.descriptors.SeparatorAttributeDescriptor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Complete description of the layout structure.
*/
public class LayoutDescriptors {
// Public attributes names, attributes descriptors and elements descriptors
public static final String ID_ATTR = "id"; //$NON-NLS-1$
/** Singleton instance */
private static LayoutDescriptors sThis;
/** The document descriptor. Contains all layouts and views linked together. */
private DocumentDescriptor mDescriptor =
new DocumentDescriptor("layout_doc", null); //$NON-NLS-1$
/** The list of all known ViewLayout descriptors. */
private ArrayList<ElementDescriptor> mLayoutDescriptors = new ArrayList<ElementDescriptor>();
/** The list of all known View (not ViewLayout) descriptors. */
private ArrayList<ElementDescriptor> mViewDescriptors = new ArrayList<ElementDescriptor>();
/** Returns a singleton instance of the {@link LayoutDescriptors}. */
public static synchronized LayoutDescriptors getInstance() {
if (sThis == null) {
sThis = new LayoutDescriptors();
}
return sThis;
}
/** @return the document descriptor. Contains all layouts and views linked together. */
public DocumentDescriptor getDescriptor() {
return mDescriptor;
}
/** @return The read-only list of all known ViewLayout descriptors. */
public List<ElementDescriptor> getLayoutDescriptors() {
return Collections.unmodifiableList(mLayoutDescriptors);
}
/** @return The read-only list of all known View (not ViewLayout) descriptors. */
public List<ElementDescriptor> getViewDescriptors() {
return Collections.unmodifiableList(mViewDescriptors);
}
/**
* Updates the document descriptor.
* <p/>
* It first computes the new children of the descriptor and then update them
* all at once.
* <p/>
* TODO: differentiate groups from views in the tree UI? => rely on icons
* <p/>
*
* @param views The list of views in the framework.
* @param layouts The list of layouts in the framework.
*/
public synchronized void updateDescriptors(ViewClassInfo[] views, ViewClassInfo[] layouts) {
ArrayList<ElementDescriptor> newViews = new ArrayList<ElementDescriptor>();
if (views != null) {
for (ViewClassInfo info : views) {
ElementDescriptor desc = convertView(info);
newViews.add(desc);
}
}
ArrayList<ElementDescriptor> newLayouts = new ArrayList<ElementDescriptor>();
if (layouts != null) {
for (ViewClassInfo info : layouts) {
ElementDescriptor desc = convertView(info);
newLayouts.add(desc);
}
}
ArrayList<ElementDescriptor> newDescriptors = new ArrayList<ElementDescriptor>();
newDescriptors.addAll(newLayouts);
newDescriptors.addAll(newViews);
ElementDescriptor[] newArray = newDescriptors.toArray(
new ElementDescriptor[newDescriptors.size()]);
// Link all layouts to everything else here.. recursively
for (ElementDescriptor layoutDesc : newLayouts) {
layoutDesc.setChildren(newArray);
}
mViewDescriptors = newViews;
mLayoutDescriptors = newLayouts;
mDescriptor.setChildren(newArray);
}
/**
* Creates an element descriptor from a given {@link ViewClassInfo}.
*/
private ElementDescriptor convertView(ViewClassInfo info) {
String xml_name = info.getShortClassName();
String tooltip = info.getJavaDoc();
// Process all View attributes
ArrayList<AttributeDescriptor> attributes = new ArrayList<AttributeDescriptor>();
DescriptorsUtils.appendAttributes(attributes,
null, // elementName
AndroidConstants.NS_RESOURCES,
info.getAttributes(),
null, // requiredAttributes
null /* overrides */);
for (ViewClassInfo link = info.getSuperClass();
link != null;
link = link.getSuperClass()) {
AttributeInfo[] attrList = link.getAttributes();
if (attrList.length > 0) {
attributes.add(new SeparatorAttributeDescriptor(
String.format("Attributes from %1$s", link.getShortClassName())));
DescriptorsUtils.appendAttributes(attributes,
null, // elementName
AndroidConstants.NS_RESOURCES,
attrList,
null, // requiredAttributes
null /* overrides */);
}
}
// Process all LayoutParams attributes
ArrayList<AttributeDescriptor> layoutAttributes = new ArrayList<AttributeDescriptor>();
LayoutParamsInfo layoutParams = info.getLayoutData();
for(; layoutParams != null; layoutParams = layoutParams.getSuperClass()) {
boolean need_separator = true;
for (AttributeInfo attr_info : layoutParams.getAttributes()) {
if (DescriptorsUtils.containsAttribute(layoutAttributes,
AndroidConstants.NS_RESOURCES, attr_info)) {
continue;
}
if (need_separator) {
String title;
if (layoutParams.getShortClassName().equals(
AndroidConstants.CLASS_LAYOUTPARAMS)) {
title = String.format("Layout Attributes from %1$s",
layoutParams.getViewLayoutClass().getShortClassName());
} else {
title = String.format("Layout Attributes from %1$s (%2$s)",
layoutParams.getViewLayoutClass().getShortClassName(),
layoutParams.getShortClassName());
}
layoutAttributes.add(new SeparatorAttributeDescriptor(title));
need_separator = false;
}
DescriptorsUtils.appendAttribute(layoutAttributes,
null, // elementName
AndroidConstants.NS_RESOURCES,
attr_info,
false, // required
null /* overrides */);
}
}
return new ViewElementDescriptor(xml_name,
xml_name, // ui_name
info.getCanonicalClassName(),
tooltip,
null, // sdk_url
attributes.toArray(new AttributeDescriptor[attributes.size()]),
layoutAttributes.toArray(new AttributeDescriptor[layoutAttributes.size()]),
null, // children
false /* mandatory */);
}
}

View File

@@ -1,139 +0,0 @@
/*
* 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.ide.eclipse.editors.layout.descriptors;
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.layout.uimodel.UiViewElementNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
/**
* {@link ViewElementDescriptor} describes the properties expected for a given XML element node
* representing a class in an XML Layout file.
*
* @see ElementDescriptor
*/
public final class ViewElementDescriptor extends ElementDescriptor {
private String mCanonicalClassName;
/** The list of layout attributes. Can be empty but not null. */
private AttributeDescriptor[] mLayoutAttributes;
/**
* Constructs a new {@link ViewElementDescriptor} based on its XML name, UI name,
* the canonical name of the class it represents, its tooltip, its SDK url, its attributes list,
* its children list and its mandatory flag.
*
* @param xml_name The XML element node name. Case sensitive.
* @param ui_name The XML element name for the user interface, typically capitalized.
* @param canonicalClassName The canonical class name the {@link ViewElementDescriptor} is
* representing.
* @param tooltip An optional tooltip. Can be null or empty.
* @param sdk_url An optional SKD URL. Can be null or empty.
* @param attributes The list of allowed attributes. Can be null or empty.
* @param layoutAttributes The list of layout attributes. Can be null or empty.
* @param children The list of allowed children. Can be null or empty.
* @param mandatory Whether this node must always exist (even for empty models). A mandatory
* UI node is never deleted and it may lack an actual XML node attached. A non-mandatory
* UI node MUST have an XML node attached and it will cease to exist when the XML node
* ceases to exist.
*/
public ViewElementDescriptor(String xml_name, String ui_name,
String canonicalClassName,
String tooltip, String sdk_url,
AttributeDescriptor[] attributes, AttributeDescriptor[] layoutAttributes,
ElementDescriptor[] children, boolean mandatory) {
super(xml_name, ui_name, tooltip, sdk_url, attributes, children, mandatory);
mCanonicalClassName = canonicalClassName;
mLayoutAttributes = layoutAttributes != null ? layoutAttributes : new AttributeDescriptor[0];
}
/**
* Constructs a new {@link ElementDescriptor} based on its XML name, the canonical
* name of the class it represents, and its children list.
* The UI name is build by capitalizing the XML name.
* The UI nodes will be non-mandatory.
*
* @param xml_name The XML element node name. Case sensitive.
* @param canonicalClassName The canonical class name the {@link ViewElementDescriptor} is
* representing.
* @param children The list of allowed children. Can be null or empty.
* @param mandatory Whether this node must always exist (even for empty models). A mandatory
* UI node is never deleted and it may lack an actual XML node attached. A non-mandatory
* UI node MUST have an XML node attached and it will cease to exist when the XML node
* ceases to exist.
*/
public ViewElementDescriptor(String xml_name, String canonicalClassName,
ElementDescriptor[] children,
boolean mandatory) {
super(xml_name, children, mandatory);
mCanonicalClassName = canonicalClassName;
}
/**
* Constructs a new {@link ElementDescriptor} based on its XML name and children list.
* The UI name is build by capitalizing the XML name.
* The UI nodes will be non-mandatory.
*
* @param xml_name The XML element node name. Case sensitive.
* @param canonicalClassName The canonical class name the {@link ViewElementDescriptor} is
* representing.
* @param children The list of allowed children. Can be null or empty.
*/
public ViewElementDescriptor(String xml_name, String canonicalClassName,
ElementDescriptor[] children) {
super(xml_name, children);
mCanonicalClassName = canonicalClassName;
}
/**
* Constructs a new {@link ElementDescriptor} based on its XML name and on the canonical
* name of the class it represents.
* The UI name is build by capitalizing the XML name.
* The UI nodes will be non-mandatory.
*
* @param xml_name The XML element node name. Case sensitive.
* @param canonicalClassName The canonical class name the {@link ViewElementDescriptor} is
* representing.
*/
public ViewElementDescriptor(String xml_name, String canonicalClassName) {
super(xml_name);
mCanonicalClassName = canonicalClassName;
}
/**
* Returns the canonical name of the class represented by this element descriptor.
*/
public String getCanonicalClassName() {
return mCanonicalClassName;
}
/** Returns the list of layout attributes. Can be empty but not null. */
public AttributeDescriptor[] getLayoutAttributes() {
return mLayoutAttributes;
}
/**
* @return A new {@link UiViewElementNode} linked to this descriptor.
*/
@Override
public UiElementNode createUiNode() {
return new UiViewElementNode(this);
}
}

View File

@@ -1,152 +0,0 @@
/*
* 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.ide.eclipse.editors.layout.parts;
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.draw2d.AbstractBackground;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.FreeformLayer;
import org.eclipse.draw2d.FreeformLayout;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.widgets.Display;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
/**
* Graphical edit part for the root document.
* <p/>
* It acts as a simple container.
*/
public class UiDocumentEditPart extends UiElementEditPart {
private Display mDisplay;
private FreeformLayer mLayer;
private ImageBackground mImage;
private Label mChild = null;
final static class ImageBackground extends AbstractBackground {
private BufferedImage mBufferedImage;
private Image mImage;
ImageBackground() {
}
ImageBackground(BufferedImage image, Display display) {
setImage(image, display);
}
@Override
public void paintBackground(IFigure figure, Graphics graphics, Insets insets) {
if (mImage != null) {
Rectangle rect = getPaintRectangle(figure, insets);
graphics.drawImage(mImage, rect.x, rect.y);
}
}
void setImage(BufferedImage image, Display display) {
if (image != null) {
int[] data = ((DataBufferInt)image.getData().getDataBuffer()).getData();
ImageData imageData = new ImageData(image.getWidth(), image.getHeight(), 32,
new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF));
imageData.setPixels(0, 0, data.length, data, 0);
mImage = new Image(display, imageData);
} else {
mImage = null;
}
}
BufferedImage getBufferedImage() {
return mBufferedImage;
}
}
public UiDocumentEditPart(UiDocumentNode uiDocumentNode, Display display) {
super(uiDocumentNode);
mDisplay = display;
}
@Override
protected IFigure createFigure() {
mLayer = new FreeformLayer();
mLayer.setLayoutManager(new FreeformLayout());
mLayer.setOpaque(true);
mLayer.setBackgroundColor(ColorConstants.lightGray);
return mLayer;
}
@Override
protected void refreshVisuals() {
UiElementNode model = (UiElementNode)getModel();
Object editData = model.getEditData();
if (editData instanceof BufferedImage) {
BufferedImage image = (BufferedImage)editData;
if (mImage == null || image != mImage.getBufferedImage()) {
mImage = new ImageBackground(image, mDisplay);
}
mLayer.setBorder(mImage);
if (mChild != null && mChild.getParent() == mLayer) {
mLayer.remove(mChild);
}
} else if (editData instanceof String) {
mLayer.setBorder(null);
if (mChild == null) {
mChild = new Label();
}
mChild.setText((String)editData);
if (mChild != null && mChild.getParent() != mLayer) {
mLayer.add(mChild);
}
Rectangle bounds = mChild.getTextBounds();
bounds.x = bounds.y = 0;
mLayer.setConstraint(mChild, bounds);
}
// refresh the children as well
refreshChildrenVisuals();
}
@Override
protected void hideSelection() {
// no selection at this level.
}
@Override
protected void showSelection() {
// no selection at this level.
}
}

View File

@@ -1,37 +0,0 @@
/*
* 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.ide.eclipse.editors.layout.parts;
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
import java.util.List;
/**
* Implementation of {@link UiElementTreeEditPart} for the document root.
*/
public class UiDocumentTreeEditPart extends UiElementTreeEditPart {
public UiDocumentTreeEditPart(UiDocumentNode model) {
super(model);
}
@SuppressWarnings("unchecked")
@Override
protected List getModelChildren() {
return getUiNode().getUiChildren();
}
}

View File

@@ -1,199 +0,0 @@
/*
* 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.ide.eclipse.editors.layout.parts;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.uimodel.IUiUpdateListener;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.DragTracker;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.gef.editpolicies.SelectionEditPolicy;
import org.eclipse.gef.tools.SelectEditPartTracker;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import java.util.List;
/**
* An {@link EditPart} for a {@link UiElementNode}.
*/
public abstract class UiElementEditPart extends AbstractGraphicalEditPart
implements IUiUpdateListener {
public UiElementEditPart(UiElementNode uiElementNode) {
setModel(uiElementNode);
}
//-------------------------
// Derived classes must define these
abstract protected void hideSelection();
abstract protected void showSelection();
//-------------------------
// Base class overrides
@Override
public DragTracker getDragTracker(Request request) {
return new SelectEditPartTracker(this);
}
@Override
protected void createEditPolicies() {
installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE, new SelectionEditPolicy() {
@Override
protected void hideSelection() {
UiElementEditPart.this.hideSelection();
}
@Override
protected void showSelection() {
UiElementEditPart.this.showSelection();
}
});
// TODO add editing policies
}
/* (non-javadoc)
* Returns a List containing the children model objects.
* Must not return null, instead use the super which returns an empty list.
*/
@SuppressWarnings("unchecked")
@Override
protected List getModelChildren() {
return getUiNode().getUiChildren();
}
@Override
public void activate() {
super.activate();
getUiNode().addUpdateListener(this);
}
@Override
public void deactivate() {
super.deactivate();
getUiNode().removeUpdateListener(this);
}
@Override
protected void refreshVisuals() {
if (getFigure().getParent() != null) {
((GraphicalEditPart) getParent()).setLayoutConstraint(this, getFigure(), getBounds());
}
// update the visuals of the children as well
refreshChildrenVisuals();
}
protected void refreshChildrenVisuals() {
if (children != null) {
for (Object child : children) {
if (child instanceof UiElementEditPart) {
UiElementEditPart childPart = (UiElementEditPart)child;
childPart.refreshVisuals();
}
}
}
}
//-------------------------
// IUiUpdateListener implementation
public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) {
// TODO: optimize by refreshing only when needed
switch(state) {
case ATTR_UPDATED:
refreshVisuals();
break;
case CHILDREN_CHANGED:
refreshChildren();
// new children list, need to update the layout
refreshVisuals();
break;
case CREATED:
refreshVisuals();
break;
case DELETED:
// pass
break;
}
}
//-------------------------
// Local methods
/** @return The object model casted to an {@link UiElementNode} */
protected final UiElementNode getUiNode() {
return (UiElementNode) getModel();
}
protected final ElementDescriptor getDescriptor() {
return getUiNode().getDescriptor();
}
protected final UiElementEditPart getEditPartParent() {
EditPart parent = getParent();
if (parent instanceof UiElementEditPart) {
return (UiElementEditPart)parent;
}
return null;
}
/**
* Returns a given XML attribute.
* @param attrbName The local name of the attribute.
* @return the attribute as a {@link String}, if it exists, or <code>null</code>
*/
protected final String getStringAttr(String attrName) {
UiElementNode uiNode = getUiNode();
if (uiNode.getXmlNode() != null) {
Node xmlNode = uiNode.getXmlNode();
if (xmlNode != null) {
NamedNodeMap nodeAttributes = xmlNode.getAttributes();
if (nodeAttributes != null) {
Node attr = nodeAttributes.getNamedItemNS(
AndroidConstants.NS_RESOURCES, attrName);
if (attr != null) {
return attr.getNodeValue();
}
}
}
}
return null;
}
protected final Rectangle getBounds() {
UiElementNode model = (UiElementNode)getModel();
Object editData = model.getEditData();
if (editData instanceof Rectangle) {
return (Rectangle)editData; // return a copy?
}
// return a dummy rect
return new Rectangle(0, 0, 0, 0);
}
}

View File

@@ -1,74 +0,0 @@
/*
* 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.ide.eclipse.editors.layout.parts;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.gef.editparts.AbstractTreeEditPart;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
/**
* Base {@link AbstractTreeEditPart} to represent {@link UiElementNode} objects in the
* {@link IContentOutlinePage} linked to the layout editor.
*/
public class UiElementTreeEditPart extends AbstractTreeEditPart {
public UiElementTreeEditPart(UiElementNode uiElementNode) {
setModel(uiElementNode);
}
@Override
protected void createEditPolicies() {
// TODO Auto-generated method stub
super.createEditPolicies();
}
@Override
protected Image getImage() {
return getUiNode().getDescriptor().getIcon();
}
@Override
protected String getText() {
return getUiNode().getShortDescription();
}
@Override
public void activate() {
if (!isActive()) {
super.activate();
// TODO
}
}
@Override
public void deactivate() {
if (isActive()) {
super.deactivate();
// TODO
}
}
/**
* Returns the casted model object represented by this {@link AbstractTreeEditPart}.
*/
protected UiElementNode getUiNode() {
return (UiElementNode)getModel();
}
}

View File

@@ -1,47 +0,0 @@
/*
* 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.ide.eclipse.editors.layout.parts;
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartFactory;
import org.eclipse.gef.editparts.AbstractTreeEditPart;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
/**
* {@link EditPartFactory} to create {@link AbstractTreeEditPart} for {@link UiElementNode} objects.
* These objects are used in the {@link IContentOutlinePage} linked to the layout editor.
*/
public class UiElementTreeEditPartFactory implements EditPartFactory {
public EditPart createEditPart(EditPart context, Object model) {
if (model instanceof UiDocumentNode) {
return new UiDocumentTreeEditPart((UiDocumentNode) model);
} else if (model instanceof UiElementNode) {
UiElementNode node = (UiElementNode) model;
if (node.getDescriptor().hasChildren()) {
return new UiLayoutTreeEditPart(node);
} else {
return new UiViewTreeEditPart(node);
}
}
return null;
}
}

View File

@@ -1,55 +0,0 @@
/*
* 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.ide.eclipse.editors.layout.parts;
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartFactory;
import org.eclipse.swt.widgets.Display;
/**
* A factory that returns the appropriate {@link EditPart} for a given model object.
* <p/>
* The only model objects we use are {@link UiElementNode} objects and they are
* edited using {@link UiElementEditPart}.
*/
public class UiElementsEditPartFactory implements EditPartFactory {
private Display mDisplay;
public UiElementsEditPartFactory(Display display) {
mDisplay = display;
}
public EditPart createEditPart(EditPart context, Object model) {
if (model instanceof UiDocumentNode) {
return new UiDocumentEditPart((UiDocumentNode) model, mDisplay);
} else if (model instanceof UiElementNode) {
UiElementNode node = (UiElementNode) model;
if (node.getDescriptor().hasChildren()) {
return new UiLayoutEditPart(node);
}
return new UiViewEditPart(node);
}
return null;
}
}

View File

@@ -1,87 +0,0 @@
/*
* 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.ide.eclipse.editors.layout.parts;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.LineBorder;
import org.eclipse.draw2d.XYLayout;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editpolicies.ContainerEditPolicy;
import org.eclipse.gef.requests.CreateRequest;
/**
* Graphical edit part for an {@link UiElementNode} that represents a ViewLayout.
* <p/>
* It acts as a simple container.
*/
public final class UiLayoutEditPart extends UiElementEditPart {
public UiLayoutEditPart(UiElementNode uiElementNode) {
super(uiElementNode);
}
@Override
protected void createEditPolicies() {
super.createEditPolicies();
installEditPolicy(EditPolicy.CONTAINER_ROLE, new ContainerEditPolicy() {
@Override
protected Command getCreateCommand(CreateRequest request) {
return null;
}
});
}
@Override
protected IFigure createFigure() {
Label f = new Label();
f.setLayoutManager(new XYLayout());
return f;
}
@Override
protected void hideSelection() {
IFigure f = getFigure();
if (f instanceof Label) {
f.setBorder(null);
}
}
@Override
protected void showSelection() {
IFigure f = getFigure();
if (f instanceof Label) {
f.setBorder(new LineBorder(ColorConstants.red, 1));
}
}
public void showDropTarget() {
IFigure f = getFigure();
if (f instanceof Label) {
f.setBorder(new LineBorder(ColorConstants.blue, 1));
}
}
public void hideDropTarget() {
hideSelection();
}
}

View File

@@ -1,39 +0,0 @@
/*
* 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.ide.eclipse.editors.layout.parts;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import java.util.List;
/**
* Implementation of {@link UiElementTreeEditPart} for layout objects.
*/
public class UiLayoutTreeEditPart extends UiElementTreeEditPart {
public UiLayoutTreeEditPart(UiElementNode node) {
super(node);
}
@SuppressWarnings("unchecked")
@Override
protected List getModelChildren() {
return getUiNode().getUiChildren();
}
}

View File

@@ -1,56 +0,0 @@
/*
* 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.ide.eclipse.editors.layout.parts;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.LineBorder;
/**
* Graphical edit part for an {@link UiElementNode} that represents a View.
*/
public class UiViewEditPart extends UiElementEditPart {
public UiViewEditPart(UiElementNode uiElementNode) {
super(uiElementNode);
}
@Override
protected IFigure createFigure() {
Label f = new Label();
return f;
}
@Override
protected void hideSelection() {
IFigure f = getFigure();
if (f instanceof Label) {
f.setBorder(null);
}
}
@Override
protected void showSelection() {
IFigure f = getFigure();
if (f instanceof Label) {
f.setBorder(new LineBorder(ColorConstants.red, 1));
}
}
}

View File

@@ -1,30 +0,0 @@
/*
* 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.ide.eclipse.editors.layout.parts;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
/**
* Implementation of {@link UiElementTreeEditPart} for view objects.
*/
public class UiViewTreeEditPart extends UiElementTreeEditPart {
public UiViewTreeEditPart(UiElementNode node) {
super(node);
}
}

View File

@@ -1,109 +0,0 @@
/*
* 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.ide.eclipse.editors.layout.uimodel;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor;
import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors;
import com.android.ide.eclipse.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
/**
* Specialized version of {@link UiElementNode} for the {@link ViewElementDescriptor}s.
*/
public class UiViewElementNode extends UiElementNode {
private AttributeDescriptor[] mCachedAttributeDescriptors;
public UiViewElementNode(ViewElementDescriptor elementDescriptor) {
super(elementDescriptor);
}
/**
* Returns an AttributeDescriptor array that depends on the current UiParent.
* <p/>
* The array merges both "direct" attributes with the descriptor layout attributes.
* The array instance is cached and cleared if the UiParent is changed.
*/
@Override
public AttributeDescriptor[] getAttributeDescriptors() {
if (mCachedAttributeDescriptors != null) {
return mCachedAttributeDescriptors;
}
UiElementNode ui_parent = getUiParent();
AttributeDescriptor[] direct_attrs = super.getAttributeDescriptors();
mCachedAttributeDescriptors = direct_attrs;
AttributeDescriptor[] layout_attrs = null;
boolean need_xmlns = false;
if (ui_parent instanceof UiDocumentNode) {
// Limitation: right now the layout behaves as if everything was
// owned by a FrameLayout.
// TODO replace by something user-configurable.
for (ElementDescriptor desc : LayoutDescriptors.getInstance().getLayoutDescriptors()) {
if (desc instanceof ViewElementDescriptor &&
desc.getXmlName().equals(AndroidConstants.CLASS_FRAMELAYOUT)) {
layout_attrs = ((ViewElementDescriptor) desc).getLayoutAttributes();
need_xmlns = true;
break;
}
}
} else if (ui_parent instanceof UiViewElementNode){
layout_attrs =
((ViewElementDescriptor) ui_parent.getDescriptor()).getLayoutAttributes();
}
if (layout_attrs == null || layout_attrs.length == 0) {
return mCachedAttributeDescriptors;
}
mCachedAttributeDescriptors =
new AttributeDescriptor[direct_attrs.length +
layout_attrs.length +
(need_xmlns ? 1 : 0)];
System.arraycopy(direct_attrs, 0,
mCachedAttributeDescriptors, 0,
direct_attrs.length);
System.arraycopy(layout_attrs, 0,
mCachedAttributeDescriptors, direct_attrs.length,
layout_attrs.length);
if (need_xmlns) {
AttributeDescriptor desc = new XmlnsAttributeDescriptor(
"android", //$NON-NLS-1$
AndroidConstants.NS_RESOURCES);
mCachedAttributeDescriptors[direct_attrs.length + layout_attrs.length] = desc;
}
return mCachedAttributeDescriptors;
}
/**
* Sets the parent of this UI node.
* <p/>
* Also removes the cached AttributeDescriptor array that depends on the current UiParent.
*/
@Override
protected void setUiParent(UiElementNode parent) {
super.setUiParent(parent);
mCachedAttributeDescriptors = null;
}
}

View File

@@ -1,34 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest;
import com.android.ide.eclipse.editors.AndroidContentAssist;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
/**
* Content Assist Processor for AndroidManifest.xml
*/
final class ManifestContentAssist extends AndroidContentAssist {
/**
* Constructor for ManifestContentAssist
*/
public ManifestContentAssist() {
super(new ElementDescriptor[] { AndroidManifestDescriptors.MANIFEST_ELEMENT });
}
}

View File

@@ -1,327 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidXPathFactory;
import com.android.ide.eclipse.editors.AndroidEditor;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
import com.android.ide.eclipse.editors.manifest.pages.ApplicationPage;
import com.android.ide.eclipse.editors.manifest.pages.InstrumentationPage;
import com.android.ide.eclipse.editors.manifest.pages.OverviewPage;
import com.android.ide.eclipse.editors.manifest.pages.PermissionPage;
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor;
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IFileListener;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.FileEditorInput;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import java.util.List;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
/**
* Multi-page form editor for AndroidManifest.xml.
*/
public final class ManifestEditor extends AndroidEditor {
private final static String EMPTY = ""; //$NON-NLS-1$
/** Root node of the UI element hierarchy */
private UiElementNode mUiManifestNode;
/** Listener to update the root node if the resource framework changes */
private Runnable mResourceRefreshListener;
/** The Application Page tab */
private ApplicationPage mAppPage;
/** The Overview Manifest Page tab */
private OverviewPage mOverviewPage;
/**
* Creates the form editor for AndroidManifest.xml.
*/
public ManifestEditor() {
super();
initUiManifestNode();
}
/**
* Return the root node of the UI element hierarchy, which here
* is the "manifest" node.
*/
@Override
public UiElementNode getUiRootNode() {
return mUiManifestNode;
}
// ---- Base Class Overrides ----
@Override
public void dispose() {
if (mResourceRefreshListener != null) {
EditorsPlugin.getDefault().removeResourceChangedListener(mResourceRefreshListener);
mResourceRefreshListener = null;
}
super.dispose();
}
/**
* Returns whether the "save as" operation is supported by this editor.
* <p/>
* Save-As is a valid operation for the ManifestEditor since it acts on a
* single source file.
*
* @see IEditorPart
*/
@Override
public boolean isSaveAsAllowed() {
return true;
}
/**
* Creates the various form pages.
*/
@Override
protected void createFormPages() {
try {
addPage(mOverviewPage = new OverviewPage(this));
addPage(mAppPage = new ApplicationPage(this));
addPage(new PermissionPage(this));
addPage(new InstrumentationPage(this));
} catch (PartInitException e) {
EditorsPlugin.log(e, "Error creating nested page"); //$NON-NLS-1$
}
}
/* (non-java doc)
* Change the tab/title name to include the project name.
*/
@Override
protected void setInput(IEditorInput input) {
super.setInput(input);
IFile inputFile = getInputFile();
if (inputFile != null) {
setPartName(String.format("%1$s Manifest", inputFile.getProject().getName()));
}
}
/**
* Processes the new XML Model, which XML root node is given.
*
* @param xml_doc The XML document, if available, or null if none exists.
*/
@Override
protected void xmlModelChanged(Document xml_doc) {
mUiManifestNode.setXmlDocument(xml_doc);
if (xml_doc != null) {
ElementDescriptor manifest_desc = mUiManifestNode.getDescriptor();
try {
XPath xpath = AndroidXPathFactory.newXPath();
Node node = (Node) xpath.evaluate("/" + manifest_desc.getXmlName(), //$NON-NLS-1$
xml_doc,
XPathConstants.NODE);
assert node != null && node.getNodeName().equals(manifest_desc.getXmlName());
// Refresh the manifest UI node and all its descendants
mUiManifestNode.loadFromXmlNode(node);
startMonitoringMarkers();
} catch (XPathExpressionException e) {
EditorsPlugin.log(e, "XPath error when trying to find '%s' element in XML.", //$NON-NLS-1$
manifest_desc.getXmlName());
}
}
super.xmlModelChanged(xml_doc);
}
/**
* Reads and processes the current markers and adds a listener for marker changes.
*/
private void startMonitoringMarkers() {
final IFile inputFile = getInputFile();
if (inputFile != null) {
updateFromExistingMarkers(inputFile);
ResourceMonitor.getMonitor().addFileListener(new IFileListener() {
public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
if (file.equals(inputFile)) {
processMarkerChanges(markerDeltas);
}
}
}, IResourceDelta.CHANGED);
}
}
/**
* Processes the markers of the specified {@link IFile} and updates the error status of
* {@link UiElementNode}s and {@link UiAttributeNode}s.
* @param inputFile the file being edited.
*/
private void updateFromExistingMarkers(IFile inputFile) {
try {
// get the markers for the file
IMarker[] markers = inputFile.findMarkers(AndroidConstants.MARKER_ANDROID, true,
IResource.DEPTH_ZERO);
UiElementNode app_ui_node = mUiManifestNode.findUiChildNode(
AndroidManifestDescriptors.APPLICATION_ELEMENT.getXmlName());
List<UiElementNode> children = app_ui_node.getUiChildren();
for (IMarker marker : markers) {
processMarker(marker, children, IResourceDelta.ADDED);
}
} catch (CoreException e) {
// findMarkers can throw an exception, in which case, we'll do nothing.
}
}
/**
* Processes a {@link IMarker} change.
* @param markerDeltas the list of {@link IMarkerDelta}
*/
private void processMarkerChanges(IMarkerDelta[] markerDeltas) {
UiElementNode app_ui_node = mUiManifestNode.findUiChildNode(
AndroidManifestDescriptors.APPLICATION_ELEMENT.getXmlName());
List<UiElementNode> children = app_ui_node.getUiChildren();
for (IMarkerDelta markerDelta : markerDeltas) {
processMarker(markerDelta.getMarker(), children, markerDelta.getKind());
}
}
/**
* Processes a new/old/updated marker.
* @param marker The marker being added/removed/changed
* @param nodeList the list of activity/service/provider/receiver nodes.
* @param kind the change kind. Can be {@link IResourceDelta#ADDED},
* {@link IResourceDelta#REMOVED}, or {@link IResourceDelta#CHANGED}
*/
private void processMarker(IMarker marker, List<UiElementNode> nodeList, int kind) {
// get the data from the marker
String nodeType = marker.getAttribute(AndroidConstants.MARKER_ATTR_TYPE, EMPTY);
if (nodeType == EMPTY) {
return;
}
String className = marker.getAttribute(AndroidConstants.MARKER_ATTR_CLASS, EMPTY);
if (className == EMPTY) {
return;
}
for (UiElementNode ui_node : nodeList) {
if (ui_node.getDescriptor().getXmlName().equals(nodeType)) {
for (UiAttributeNode attr : ui_node.getUiAttributes()) {
if (attr.getDescriptor().getXmlLocalName().equals(
AndroidManifestDescriptors.ANDROID_NAME_ATTR)) {
if (attr.getCurrentValue().equals(className)) {
if (kind == IResourceDelta.REMOVED) {
attr.setHasError(false);
} else {
attr.setHasError(true);
}
return;
}
}
}
}
}
}
/**
* Creates the initial UI Root Node, including the known mandatory elements.
*/
private void initUiManifestNode() {
// The manifest UI node is always created, even if there's no corresponding XML node.
if (mUiManifestNode == null) {
ElementDescriptor manifest_desc = AndroidManifestDescriptors.MANIFEST_ELEMENT;
mUiManifestNode = manifest_desc.createUiNode();
mUiManifestNode.setEditor(this);
// Similarly, always create the /manifest/application and /manifest/uses-sdk nodes
ElementDescriptor app_desc = AndroidManifestDescriptors.APPLICATION_ELEMENT;
boolean present = false;
for (UiElementNode ui_node : mUiManifestNode.getUiChildren()) {
if (ui_node.getDescriptor() == app_desc) {
present = true;
break;
}
}
if (!present) {
mUiManifestNode.appendNewUiChild(app_desc);
}
app_desc = AndroidManifestDescriptors.USES_SDK_ELEMENT;
present = false;
for (UiElementNode ui_node : mUiManifestNode.getUiChildren()) {
if (ui_node.getDescriptor() == app_desc) {
present = true;
break;
}
}
if (!present) {
mUiManifestNode.appendNewUiChild(app_desc);
}
// Add a listener to refresh the root node if the resource framework changes
// by forcing it to parse its own XML
mResourceRefreshListener = new Runnable() {
public void run() {
commitPages(false /* onSave */);
mUiManifestNode.reloadFromXmlNode(mUiManifestNode.getXmlNode());
if (mOverviewPage != null) {
mOverviewPage.refreshUiApplicationNode();
}
if (mAppPage != null) {
mAppPage.refreshUiApplicationNode();
}
}
};
EditorsPlugin.getDefault().addResourceChangedListener(mResourceRefreshListener);
mResourceRefreshListener.run();
}
}
/**
* Returns the {@link IFile} being edited, or <code>null</code> if it couldn't be computed.
*/
private IFile getInputFile() {
IEditorInput input = getEditorInput();
if (input instanceof FileEditorInput) {
FileEditorInput fileInput = (FileEditorInput) input;
return fileInput.getFile();
}
return null;
}
}

View File

@@ -1,100 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest;
import org.eclipse.jface.action.IAction;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.ide.IDEActionFactory;
import org.eclipse.ui.part.MultiPageEditorActionBarContributor;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
/**
* Manages the installation/deinstallation of global actions for multi-page
* editors. Responsible for the redirection of global actions to the active
* editor. Multi-page contributor replaces the contributors for the individual
* editors in the multi-page editor.
*
* TODO: Doesn't look like we need this. Remove it if not needed.
* @deprecated
*/
final class ManifestEditorContributor extends MultiPageEditorActionBarContributor {
private IEditorPart mActiveEditorPart;
/**
* Creates a multi-page contributor.
*
* Marked as Private so it can't be instanciated. This is a cheap way to make sure
* it's not being used. As noted in constructor, should be removed if not used.
* @deprecated
*/
private ManifestEditorContributor() {
super();
}
/**
* Returns the action registed with the given text editor.
*
* @return IAction or null if editor is null.
*/
protected IAction getAction(ITextEditor editor, String actionID) {
return (editor == null ? null : editor.getAction(actionID));
}
/*
* (non-JavaDoc) Method declared in
* AbstractMultiPageEditorActionBarContributor.
*/
@Override
public void setActivePage(IEditorPart part) {
if (mActiveEditorPart == part)
return;
mActiveEditorPart = part;
IActionBars actionBars = getActionBars();
if (actionBars != null) {
ITextEditor editor =
(part instanceof ITextEditor) ? (ITextEditor)part : null;
actionBars.setGlobalActionHandler(ActionFactory.DELETE.getId(),
getAction(editor, ITextEditorActionConstants.DELETE));
actionBars.setGlobalActionHandler(ActionFactory.UNDO.getId(),
getAction(editor, ITextEditorActionConstants.UNDO));
actionBars.setGlobalActionHandler(ActionFactory.REDO.getId(),
getAction(editor, ITextEditorActionConstants.REDO));
actionBars.setGlobalActionHandler(ActionFactory.CUT.getId(),
getAction(editor, ITextEditorActionConstants.CUT));
actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(),
getAction(editor, ITextEditorActionConstants.COPY));
actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(),
getAction(editor, ITextEditorActionConstants.PASTE));
actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
getAction(editor, ITextEditorActionConstants.SELECT_ALL));
actionBars.setGlobalActionHandler(ActionFactory.FIND.getId(),
getAction(editor, ITextEditorActionConstants.FIND));
actionBars.setGlobalActionHandler(
IDEActionFactory.BOOKMARK.getId(), getAction(editor,
IDEActionFactory.BOOKMARK.getId()));
actionBars.updateActionBars();
}
}
}

View File

@@ -1,30 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest;
import com.android.ide.eclipse.editors.AndroidSourceViewerConfig;
/**
* Source Viewer Configuration that calls in ManifestContentAssist.
*/
public final class ManifestSourceViewerConfig extends AndroidSourceViewerConfig {
public ManifestSourceViewerConfig() {
super(new ManifestContentAssist());
}
}

View File

@@ -1,497 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.descriptors;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.resources.DeclareStyleableInfo;
import com.android.ide.eclipse.common.resources.ResourceType;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.ListAttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.ReferenceAttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
import org.eclipse.core.runtime.IStatus;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;
import java.util.Map.Entry;
/**
* Complete description of the AndroidManifest.xml structure.
* <p/>
* The root element are static instances which always exists.
* However their sub-elements and attributes are created only when the SDK changes or is
* loaded the first time.
*/
public class AndroidManifestDescriptors {
private static final String MANIFEST_NODE_NAME = "manifest"; //$NON-NLS-1$
private static final String ANDROID_MANIFEST_STYLEABLE = "AndroidManifest"; //$NON-NLS-1$
// Public attributes names, attributes descriptors and elements descriptors
public static final String ANDROID_LABEL_ATTR = "label"; //$NON-NLS-1$
public static final String ANDROID_NAME_ATTR = "name"; //$NON-NLS-1$
public static final String PACKAGE_ATTR = "package"; //$NON-NLS-1$
/** The {@link ElementDescriptor} for the root Manifest element. */
public static final ElementDescriptor MANIFEST_ELEMENT;
/** The {@link ElementDescriptor} for the root Application element. */
public static final ElementDescriptor APPLICATION_ELEMENT;
/** The {@link ElementDescriptor} for the root Instrumentation element. */
public static final ElementDescriptor INTRUMENTATION_ELEMENT;
/** The {@link ElementDescriptor} for the root Permission element. */
public static final ElementDescriptor PERMISSION_ELEMENT;
/** The {@link ElementDescriptor} for the root UsesPermission element. */
public static final ElementDescriptor USES_PERMISSION_ELEMENT;
/** The {@link ElementDescriptor} for the root UsesSdk element. */
public static final ElementDescriptor USES_SDK_ELEMENT;
/** The {@link ElementDescriptor} for the root PermissionGroup element. */
public static final ElementDescriptor PERMISSION_GROUP_ELEMENT;
/** The {@link ElementDescriptor} for the root PermissionTree element. */
public static final ElementDescriptor PERMISSION_TREE_ELEMENT;
/** Private package attribute for the manifest element. Needs to be handled manually. */
private static final TextAttributeDescriptor PACKAGE_ATTR_DESC;
static {
APPLICATION_ELEMENT = createElement("application", null, true); //$NON-NLS-1$ + no child & mandatory
INTRUMENTATION_ELEMENT = createElement("instrumentation"); //$NON-NLS-1$
PERMISSION_ELEMENT = createElement("permission"); //$NON-NLS-1$
USES_PERMISSION_ELEMENT = createElement("uses-permission"); //$NON-NLS-1$
USES_SDK_ELEMENT = createElement("uses-sdk", null, true); //$NON-NLS-1$ + no child & mandatory
PERMISSION_GROUP_ELEMENT = createElement("permission-group"); //$NON-NLS-1$
PERMISSION_TREE_ELEMENT = createElement("permission-tree"); //$NON-NLS-1$
MANIFEST_ELEMENT = createElement(
MANIFEST_NODE_NAME, // xml name
new ElementDescriptor[] {
APPLICATION_ELEMENT,
INTRUMENTATION_ELEMENT,
PERMISSION_ELEMENT,
USES_PERMISSION_ELEMENT,
PERMISSION_GROUP_ELEMENT,
PERMISSION_TREE_ELEMENT,
USES_SDK_ELEMENT,
},
true /* mandatory */);
// The "package" attribute is treated differently as it doesn't have the standard
// Android XML namespace.
PACKAGE_ATTR_DESC = new PackageAttributeDescriptor(PACKAGE_ATTR,
"Package",
null /* nsUri */,
"This attribute gives a unique name for the package, using a Java-style naming convention to avoid name collisions.\nFor example, applications published by Google could have names of the form com.google.app.appname");
}
/**
* Updates the document descriptor.
* <p/>
* It first computes the new children of the descriptor and then updates them
* all at once.
*
* @param manifestMap The map style => attributes from the attrs_manifest.xml file
*/
public static synchronized void updateDescriptors(
Map<String, DeclareStyleableInfo> manifestMap) {
XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(
"android", //$NON-NLS-1$
AndroidConstants.NS_RESOURCES);
// -- setup the various attribute format overrides --
// The key for each override is "element1,element2,.../attr-xml-local-name" or
// "*/attr-xml-local-name" to match the attribute in any element.
Map<String, Object> overrides = new HashMap<String, Object>();
overrides.put("*/icon", new DescriptorsUtils.ITextAttributeCreator() { //$NON-NLS-1$
public TextAttributeDescriptor create(String xmlName, String uiName, String nsUri,
String tooltip) {
return new ReferenceAttributeDescriptor(
ResourceType.DRAWABLE,
xmlName, uiName, nsUri,
tooltip);
}
});
overrides.put("*/theme", ThemeAttributeDescriptor.class); //$NON-NLS-1$
overrides.put("*/permission", ListAttributeDescriptor.class); //$NON-NLS-1$
overrides.put("*/targetPackage", PackageAttributeDescriptor.class); //$NON-NLS-1$
overrides.put("action,category,uses-permission/" + ANDROID_NAME_ATTR, //$NON-NLS-1$
ListAttributeDescriptor.class);
overrides.put("application/" + ANDROID_NAME_ATTR, ApplicationAttributeDescriptor.class); //$NON-NLS-1$
overrideClassName(overrides, "activity", AndroidConstants.CLASS_ACTIVITY); //$NON-NLS-1$
overrideClassName(overrides, "receiver", AndroidConstants.CLASS_BROADCASTRECEIVER); //$NON-NLS-1$
overrideClassName(overrides, "service", AndroidConstants.CLASS_SERVICE); //$NON-NLS-1$
overrideClassName(overrides, "provider", AndroidConstants.CLASS_CONTENTPROVIDER); //$NON-NLS-1$
// -- list element nodes already created --
// These elements are referenced by already opened editors, so we want to update them
// but not re-create them when reloading an SDK on the fly.
HashMap<String, ElementDescriptor> elementDescs =
new HashMap<String, ElementDescriptor>();
elementDescs.put(MANIFEST_ELEMENT.getXmlLocalName(), MANIFEST_ELEMENT);
elementDescs.put(APPLICATION_ELEMENT.getXmlLocalName(), APPLICATION_ELEMENT);
elementDescs.put(INTRUMENTATION_ELEMENT.getXmlLocalName(), INTRUMENTATION_ELEMENT);
elementDescs.put(PERMISSION_ELEMENT.getXmlLocalName(), PERMISSION_ELEMENT);
elementDescs.put(USES_PERMISSION_ELEMENT.getXmlLocalName(), USES_PERMISSION_ELEMENT);
elementDescs.put(USES_SDK_ELEMENT.getXmlLocalName(), USES_SDK_ELEMENT);
elementDescs.put(PERMISSION_GROUP_ELEMENT.getXmlLocalName(), PERMISSION_GROUP_ELEMENT);
elementDescs.put(PERMISSION_TREE_ELEMENT.getXmlLocalName(), PERMISSION_TREE_ELEMENT);
// --
inflateElement(manifestMap, overrides, elementDescs,
MANIFEST_ELEMENT, "AndroidManifest"); //$NON-NLS-1$
insertAttribute(MANIFEST_ELEMENT, PACKAGE_ATTR_DESC);
sanityCheck(manifestMap, MANIFEST_ELEMENT);
}
/**
* Sets up an attribute override for ANDROID_NAME_ATTR using a ClassAttributeDescriptor
* with the specified class name.
*/
private static void overrideClassName(Map<String, Object> overrides,
String elementName, final String className) {
overrides.put(elementName + "/" + ANDROID_NAME_ATTR,
new DescriptorsUtils.ITextAttributeCreator() {
public TextAttributeDescriptor create(String xmlName, String uiName, String nsUri,
String tooltip) {
if (AndroidConstants.CLASS_ACTIVITY.equals(className)) {
return new ClassAttributeDescriptor(
className,
PostActivityCreationAction.getAction(),
xmlName, uiName + "*", //$NON-NLS-1$
nsUri,
tooltip,
true /*mandatory */);
} else if (AndroidConstants.CLASS_BROADCASTRECEIVER.equals(className)) {
return new ClassAttributeDescriptor(
className,
PostReceiverCreationAction.getAction(),
xmlName, uiName + "*", //$NON-NLS-1$
nsUri,
tooltip,
true /*mandatory */);
} else {
return new ClassAttributeDescriptor(
className,
xmlName, uiName + "*", //$NON-NLS-1$
nsUri,
tooltip,
true /*mandatory */);
}
}
});
}
/**
* Returns a new ElementDescriptor constructed from the information given here
* and the javadoc & attributes extracted from the style map if any.
* <p/>
* Creates an element with no attribute overrides.
*/
private static ElementDescriptor createElement(
String xmlName,
ElementDescriptor[] childrenElements,
boolean mandatory) {
// Creates an element with no attribute overrides.
String styleName = guessStyleName(xmlName);
String sdkUrl = DescriptorsUtils.MANIFEST_SDK_URL + styleName;
String uiName = getUiName(xmlName);
ElementDescriptor element = new ManifestElementDescriptor(xmlName, uiName, null, sdkUrl,
null, childrenElements, mandatory);
return element;
}
/**
* Returns a new ElementDescriptor constructed from its XML local name.
* <p/>
* This version creates an element not mandatory.
*/
private static ElementDescriptor createElement(String xmlName) {
// Creates an element with no child and not mandatory
return createElement(xmlName, null, false);
}
/**
* Inserts an attribute in this element attribute list if it is not present there yet
* (based on the attribute XML name.)
* The attribute is inserted at the beginning of the attribute list.
*/
private static void insertAttribute(ElementDescriptor element, AttributeDescriptor newAttr) {
AttributeDescriptor[] attributes = element.getAttributes();
for (AttributeDescriptor attr : attributes) {
if (attr.getXmlLocalName().equals(newAttr.getXmlLocalName())) {
return;
}
}
AttributeDescriptor[] newArray = new AttributeDescriptor[attributes.length + 1];
newArray[0] = newAttr;
System.arraycopy(attributes, 0, newArray, 1, attributes.length);
element.setAttributes(newArray);
}
/**
* "Inflates" the properties of an {@link ElementDescriptor} from the styleable declaration.
* <p/>
* This first creates all the attributes for the given ElementDescriptor.
* It then find all children of the descriptor, inflate them recursively and set them
* as child to this ElementDescriptor.
*
* @param styleMap The input styleable map for manifest elements & attributes
* @param overrides A list of attribute overrides (to customize the type of the attribute
* descriptors)
* @param existingElementDescs A map of already created element descriptors, keyed by
* XML local name. This is used to use the static elements created initially by this
* class, which are referenced directly by editors (so that reloading an SDK won't
* break these references)
* @param elemDesc The current {@link ElementDescriptor} to inflate.
* @param styleName The name of the {@link ElementDescriptor} to inflate. Its XML local name
* will be guessed automatically from the style name.
*/
private static void inflateElement(
Map<String, DeclareStyleableInfo> styleMap,
Map<String, Object> overrides,
HashMap<String, ElementDescriptor> existingElementDescs,
ElementDescriptor elemDesc,
String styleName) {
assert elemDesc != null;
assert styleName != null;
// define attributes
DeclareStyleableInfo style = styleMap != null ? styleMap.get(styleName) : null;
if (style != null) {
ArrayList<AttributeDescriptor> attrDescs = new ArrayList<AttributeDescriptor>();
DescriptorsUtils.appendAttributes(attrDescs,
elemDesc.getXmlLocalName(),
AndroidConstants.NS_RESOURCES,
style.getAttributes(), null, overrides);
elemDesc.setTooltip(style.getJavaDoc());
elemDesc.setAttributes(attrDescs.toArray(new AttributeDescriptor[attrDescs.size()]));
}
// find all elements that have this one as parent
ArrayList<ElementDescriptor> children = new ArrayList<ElementDescriptor>();
for (Entry<String, DeclareStyleableInfo> entry : styleMap.entrySet()) {
DeclareStyleableInfo childStyle = entry.getValue();
boolean isParent = false;
String[] parents = childStyle.getParents();
if (parents != null) {
for (String parent: parents) {
if (styleName.equals(parent)) {
isParent = true;
break;
}
}
}
if (isParent) {
String childStyleName = entry.getKey();
String childXmlName = guessXmlName(childStyleName);
// create or re-use element
ElementDescriptor child = existingElementDescs.get(childXmlName);
if (child == null) {
child = createElement(childXmlName);
existingElementDescs.put(childXmlName, child);
}
children.add(child);
inflateElement(styleMap, overrides, existingElementDescs, child, childStyleName);
}
}
elemDesc.setChildren(children.toArray(new ElementDescriptor[children.size()]));
}
/**
* Get an UI name from the element XML name.
* <p/>
* Capitalizes the first letter and replace non-alphabet by a space followed by a capital.
*/
private static String getUiName(String xmlName) {
StringBuilder sb = new StringBuilder();
boolean capitalize = true;
for (char c : xmlName.toCharArray()) {
if (capitalize && c >= 'a' && c <= 'z') {
sb.append((char)(c + 'A' - 'a'));
capitalize = false;
} else if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) {
sb.append(' ');
capitalize = true;
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* Guesses the style name for a given XML element name.
* <p/>
* The rules are:
* - capitalize the first letter:
* - if there's a dash, skip it and capitalize the next one
* - prefix AndroidManifest
* The exception is "manifest" which just becomes AndroidManifest.
* <p/>
* Examples:
* - manifest => AndroidManifest
* - application => AndroidManifestApplication
* - uses-permission => AndroidManifestUsesPermission
*/
private static String guessStyleName(String xmlName) {
StringBuilder sb = new StringBuilder();
if (!xmlName.equals(MANIFEST_NODE_NAME)) {
boolean capitalize = true;
for (char c : xmlName.toCharArray()) {
if (capitalize && c >= 'a' && c <= 'z') {
sb.append((char)(c + 'A' - 'a'));
capitalize = false;
} else if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) {
// not a letter -- skip the character and capitalize the next one
capitalize = true;
} else {
sb.append(c);
}
}
}
sb.insert(0, ANDROID_MANIFEST_STYLEABLE);
return sb.toString();
}
/**
* This method performs a sanity check to make sure all the styles declared in the
* manifestMap are actually defined in the actual element descriptors and reachable from
* the manifestElement root node.
*/
private static void sanityCheck(Map<String, DeclareStyleableInfo> manifestMap,
ElementDescriptor manifestElement) {
TreeSet<String> elementsDeclared = new TreeSet<String>();
findAllElementNames(manifestElement, elementsDeclared);
TreeSet<String> stylesDeclared = new TreeSet<String>();
for (String styleName : manifestMap.keySet()) {
if (styleName.startsWith(ANDROID_MANIFEST_STYLEABLE)) {
stylesDeclared.add(styleName);
}
}
for (Iterator<String> it = elementsDeclared.iterator(); it.hasNext();) {
String xmlName = it.next();
String styleName = guessStyleName(xmlName);
if (stylesDeclared.remove(styleName)) {
it.remove();
}
}
StringBuilder sb = new StringBuilder();
if (!stylesDeclared.isEmpty()) {
sb.append("Warning, ADT/SDK Mismatch! The following elements are declared by the SDK but unknown to ADT: ");
for (String name : stylesDeclared) {
name = guessXmlName(name);
if (name != stylesDeclared.last()) {
sb.append(", "); //$NON-NLS-1$
}
}
EditorsPlugin.log(IStatus.WARNING, "%s", sb.toString());
EditorsPlugin.printToConsole(null, sb);
sb.setLength(0);
}
if (!elementsDeclared.isEmpty()) {
sb.append("Warning, ADT/SDK Mismatch! The following elements are declared by ADT but not by the SDK: ");
for (String name : elementsDeclared) {
sb.append(name);
if (name != elementsDeclared.last()) {
sb.append(", "); //$NON-NLS-1$
}
}
EditorsPlugin.log(IStatus.WARNING, "%s", sb.toString());
EditorsPlugin.printToConsole(null, sb);
}
}
/**
* Performs an approximate translation of the style name into a potential
* xml name. This is more or less the reverse from guessStyleName().
*
* @return The XML local name for a given style name.
*/
private static String guessXmlName(String name) {
StringBuilder sb = new StringBuilder();
if (ANDROID_MANIFEST_STYLEABLE.equals(name)) {
sb.append(MANIFEST_NODE_NAME);
} else {
name = name.replace(ANDROID_MANIFEST_STYLEABLE, ""); //$NON-NLS-1$
boolean first_char = true;
for (char c : name.toCharArray()) {
if (c >= 'A' && c <= 'Z') {
if (!first_char) {
sb.append('-');
}
c = (char) (c - 'A' + 'a');
}
sb.append(c);
first_char = false;
}
}
return sb.toString();
}
/**
* Helper method used by {@link #sanityCheck(Map, ElementDescriptor)} to find all the
* {@link ElementDescriptor} names defined by the tree of descriptors.
* <p/>
* Note: this assumes no circular reference in the tree of {@link ElementDescriptor}s.
*/
private static void findAllElementNames(ElementDescriptor element, TreeSet<String> declared) {
declared.add(element.getXmlName());
for (ElementDescriptor desc : element.getChildren()) {
findAllElementNames(desc, declared);
}
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.descriptors;
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
import com.android.ide.eclipse.editors.manifest.model.UiClassAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
/**
* Describes an 'Application' class XML attribute. It is displayed by a
* {@link UiClassAttributeNode}, that restricts creation and selection to classes
* inheriting from android.app.Application.
*/
public class ApplicationAttributeDescriptor extends TextAttributeDescriptor {
public ApplicationAttributeDescriptor(String xmlLocalName, String uiName,
String nsUri, String tooltip) {
super(xmlLocalName, uiName, nsUri, tooltip);
}
/**
* @return A new {@link UiClassAttributeNode} linked to this descriptor.
*/
@Override
public UiAttributeNode createUiNode(UiElementNode uiParent) {
return new UiClassAttributeNode("android.app.Application", //$NON-NLS-1$
null /* postCreationAction */, false /* mandatory */, this, uiParent);
}
}

View File

@@ -1,88 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.descriptors;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
import com.android.ide.eclipse.editors.manifest.model.UiClassAttributeNode;
import com.android.ide.eclipse.editors.manifest.model.UiClassAttributeNode.IPostTypeCreationAction;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
/**
* Describes an XML attribute representing a class name.
* It is displayed by a {@link UiClassAttributeNode}.
*/
public class ClassAttributeDescriptor extends TextAttributeDescriptor {
/** Superclass of the class value. */
private String mSuperClassName;
private IPostTypeCreationAction mPostCreationAction;
/** indicates if the class parameter is mandatory */
boolean mMandatory;
/**
* Creates a new {@link ClassAttributeDescriptor}
* @param superClassName the fully qualified name of the superclass of the class represented
* by the attribute.
* @param xmlLocalName The XML name of the attribute (case sensitive, with android: prefix).
* @param uiName The UI name of the attribute. Cannot be an empty string and cannot be null.
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
* @param tooltip A non-empty tooltip string or null.
* @param mandatory indicates if the class attribute is mandatory.
*/
public ClassAttributeDescriptor(String superClassName,
String xmlLocalName, String uiName, String nsUri,
String tooltip, boolean mandatory) {
super(xmlLocalName, uiName, nsUri, tooltip);
mSuperClassName = superClassName;
}
/**
* Creates a new {@link ClassAttributeDescriptor}
* @param superClassName the fully qualified name of the superclass of the class represented
* by the attribute.
* @param postCreationAction the {@link IPostTypeCreationAction} to be executed on the
* newly created class.
* @param xmlLocalName The XML local name of the attribute (case sensitive).
* @param uiName The UI name of the attribute. Cannot be an empty string and cannot be null.
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
* @param tooltip A non-empty tooltip string or null.
* @param mandatory indicates if the class attribute is mandatory.
*/
public ClassAttributeDescriptor(String superClassName,
IPostTypeCreationAction postCreationAction,
String xmlLocalName, String uiName, String nsUri,
String tooltip, boolean mandatory) {
super(xmlLocalName, uiName, nsUri, tooltip);
mSuperClassName = superClassName;
mPostCreationAction = postCreationAction;
}
/**
* @return A new {@link UiClassAttributeNode} linked to this descriptor.
*/
@Override
public UiAttributeNode createUiNode(UiElementNode uiParent) {
return new UiClassAttributeNode(mSuperClassName, mPostCreationAction,
mMandatory, this, uiParent);
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.descriptors;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
import com.android.ide.eclipse.editors.manifest.model.UiClassAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
/**
* Describes a 'Instrumentation' class XML attribute. It is displayed by a
* {@link UiClassAttributeNode}, that restricts creation and selection to classes inheriting from
* android.app.Instrumentation.
*/
public class InstrumentationAttributeDescriptor extends TextAttributeDescriptor {
public InstrumentationAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
String tooltip) {
super(xmlLocalName, uiName, nsUri, tooltip);
}
/**
* @return A new {@link UiClassAttributeNode} linked to this descriptor.
*/
@Override
public UiAttributeNode createUiNode(UiElementNode uiParent) {
return new UiClassAttributeNode(AndroidConstants.CLASS_INSTRUMENTATION,
null /* postCreationAction */, true /* mandatory */, this, uiParent);
}
}

View File

@@ -1,96 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.descriptors;
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.manifest.model.UiManifestElementNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
/**
* {@link ManifestElementDescriptor} describes an XML element node, with its
* element name, its possible attributes, its possible child elements but also
* its display name and tooltip.
*
* This {@link ElementDescriptor} is specialized to create {@link UiManifestElementNode} UI nodes.
*/
public class ManifestElementDescriptor extends ElementDescriptor {
/**
* Constructs a new {@link ManifestElementDescriptor}.
*
* @param xml_name The XML element node name. Case sensitive.
* @param ui_name The XML element name for the user interface, typically capitalized.
* @param tooltip An optional tooltip. Can be null or empty.
* @param sdk_url An optional SKD URL. Can be null or empty.
* @param attributes The list of allowed attributes. Can be null or empty.
* @param children The list of allowed children. Can be null or empty.
* @param mandatory Whether this node must always exist (even for empty models).
*/
public ManifestElementDescriptor(String xml_name, String ui_name, String tooltip, String sdk_url,
AttributeDescriptor[] attributes,
ElementDescriptor[] children,
boolean mandatory) {
super(xml_name, ui_name, tooltip, sdk_url, attributes, children, mandatory);
}
/**
* Constructs a new {@link ManifestElementDescriptor}.
*
* @param xml_name The XML element node name. Case sensitive.
* @param ui_name The XML element name for the user interface, typically capitalized.
* @param tooltip An optional tooltip. Can be null or empty.
* @param sdk_url An optional SKD URL. Can be null or empty.
* @param attributes The list of allowed attributes. Can be null or empty.
* @param children The list of allowed children. Can be null or empty.
*/
public ManifestElementDescriptor(String xml_name, String ui_name, String tooltip, String sdk_url,
AttributeDescriptor[] attributes,
ElementDescriptor[] children) {
super(xml_name, ui_name, tooltip, sdk_url, attributes, children, false);
}
/**
* This is a shortcut for
* ManifestElementDescriptor(xml_name, xml_name.capitalize(), null, null, null, children).
* This constructor is mostly used for unit tests.
*
* @param xml_name The XML element node name. Case sensitive.
*/
public ManifestElementDescriptor(String xml_name, ElementDescriptor[] children) {
super(xml_name, children);
}
/**
* This is a shortcut for
* ManifestElementDescriptor(xml_name, xml_name.capitalize(), null, null, null, null).
* This constructor is mostly used for unit tests.
*
* @param xml_name The XML element node name. Case sensitive.
*/
public ManifestElementDescriptor(String xml_name) {
super(xml_name, null);
}
/**
* @return A new {@link UiElementNode} linked to this descriptor.
*/
@Override
public UiElementNode createUiNode() {
return new UiManifestElementNode(this);
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.descriptors;
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
import com.android.ide.eclipse.editors.manifest.model.UiPackageAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
/**
* Describes a package XML attribute. It is displayed by a {@link UiPackageAttributeNode}.
*/
public class PackageAttributeDescriptor extends TextAttributeDescriptor {
public PackageAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
String tooltip) {
super(xmlLocalName, uiName, nsUri, tooltip);
}
/**
* @return A new {@link UiPackageAttributeNode} linked to this descriptor.
*/
@Override
public UiAttributeNode createUiNode(UiElementNode uiParent) {
return new UiPackageAttributeNode(this, uiParent);
}
}

View File

@@ -1,88 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.descriptors;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.manifest.model.UiClassAttributeNode.IPostTypeCreationAction;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
/**
* Action to be executed after an Activity class is created.
*/
class PostActivityCreationAction implements IPostTypeCreationAction {
private final static PostActivityCreationAction sAction = new PostActivityCreationAction();
private PostActivityCreationAction() {
// private constructor to enforce singleton.
}
/**
* Returns the action.
*/
public static IPostTypeCreationAction getAction() {
return sAction;
}
/**
* Processes a newly created Activity.
*
*/
public void processNewType(IType newType) {
try {
String methodContent =
" /** Called when the activity is first created. */\n" +
" @Override\n" +
" public void onCreate(Bundle savedInstanceState) {\n" +
" super.onCreate(savedInstanceState);\n" +
"\n" +
" // TODO Auto-generated method stub\n" +
" }";
newType.createMethod(methodContent, null /* sibling*/, false /* force */,
new NullProgressMonitor());
// we need to add the import for Bundle, so we need the compilation unit.
// Since the type could be enclosed in other types, we loop till we find it.
ICompilationUnit compilationUnit = null;
IJavaElement element = newType;
do {
IJavaElement parentElement = element.getParent();
if (parentElement != null) {
if (parentElement.getElementType() == IJavaElement.COMPILATION_UNIT) {
compilationUnit = (ICompilationUnit)parentElement;
}
element = parentElement;
} else {
break;
}
} while (compilationUnit == null);
if (compilationUnit != null) {
compilationUnit.createImport(AndroidConstants.CLASS_BUNDLE,
null /* sibling */, new NullProgressMonitor());
}
} catch (JavaModelException e) {
}
}
}

View File

@@ -1,88 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.descriptors;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.manifest.model.UiClassAttributeNode.IPostTypeCreationAction;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
/**
* Action to be executed after an BroadcastReceiver class is created.
*/
class PostReceiverCreationAction implements IPostTypeCreationAction {
private final static PostReceiverCreationAction sAction = new PostReceiverCreationAction();
private PostReceiverCreationAction() {
// private constructor to enforce singleton.
}
/**
* Returns the action.
*/
public static IPostTypeCreationAction getAction() {
return sAction;
}
/**
* Processes a newly created Activity.
*
*/
public void processNewType(IType newType) {
try {
String methodContent =
" @Override\n" +
" public void onReceive(Context context, Intent intent) {\n" +
" // TODO Auto-generated method stub\n" +
" }";
newType.createMethod(methodContent, null /* sibling*/, false /* force */,
new NullProgressMonitor());
// we need to add the import for Bundle, so we need the compilation unit.
// Since the type could be enclosed in other types, we loop till we find it.
ICompilationUnit compilationUnit = null;
IJavaElement element = newType;
do {
IJavaElement parentElement = element.getParent();
if (parentElement != null) {
if (parentElement.getElementType() == IJavaElement.COMPILATION_UNIT) {
compilationUnit = (ICompilationUnit)parentElement;
}
element = parentElement;
} else {
break;
}
} while (compilationUnit == null);
if (compilationUnit != null) {
compilationUnit.createImport(AndroidConstants.CLASS_CONTEXT,
null /* sibling */, new NullProgressMonitor());
compilationUnit.createImport(AndroidConstants.CLASS_INTENT,
null /* sibling */, new NullProgressMonitor());
}
} catch (JavaModelException e) {
// looks like the class already existed (this happens when the user check to create
// inherited abstract methods).
}
}
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.descriptors;
import com.android.ide.eclipse.common.resources.ResourceType;
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.editors.uimodel.UiResourceAttributeNode;
/**
* Describes a Theme/Style XML attribute displayed by a {@link UiResourceAttributeNode}
*/
public final class ThemeAttributeDescriptor extends TextAttributeDescriptor {
public ThemeAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
String tooltip) {
super(xmlLocalName, uiName, nsUri, tooltip);
}
/**
* @return A new {@link UiResourceAttributeNode} linked to this theme descriptor.
*/
@Override
public UiAttributeNode createUiNode(UiElementNode uiParent) {
return new UiResourceAttributeNode(ResourceType.STYLE, this, uiParent);
}
}

View File

@@ -1,624 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.model;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestHelper;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.editors.AndroidEditor;
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
import com.android.ide.eclipse.editors.ui.SectionHelper;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.editors.uimodel.UiTextAttributeNode;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.ui.IJavaElementSearchConstants;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.actions.OpenNewClassWizardAction;
import org.eclipse.jdt.ui.dialogs.ITypeInfoFilterExtension;
import org.eclipse.jdt.ui.dialogs.ITypeInfoRequestor;
import org.eclipse.jdt.ui.dialogs.ITypeSelectionComponent;
import org.eclipse.jdt.ui.dialogs.TypeSelectionExtension;
import org.eclipse.jdt.ui.wizards.NewClassWizardPage;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.SelectionDialog;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.TableWrapData;
import org.w3c.dom.Element;
import java.util.ArrayList;
/**
* Represents an XML attribute for a class, that can be modified using a simple text field or
* a dialog to choose an existing class. Also, there's a link to create a new class.
* <p/>
* See {@link UiTextAttributeNode} for more information.
*/
public class UiClassAttributeNode extends UiTextAttributeNode {
private String mReferenceClass;
private IPostTypeCreationAction mPostCreationAction;
private boolean mMandatory;
private class HierarchyTypeSelection extends TypeSelectionExtension {
private IJavaProject mJavaProject;
public HierarchyTypeSelection(IProject project, String referenceClass) {
mJavaProject = JavaCore.create(project);
mReferenceClass = referenceClass;
}
@Override
public ITypeInfoFilterExtension getFilterExtension() {
return new ITypeInfoFilterExtension() {
public boolean select(ITypeInfoRequestor typeInfoRequestor) {
String packageName = typeInfoRequestor.getPackageName();
String typeName = typeInfoRequestor.getTypeName();
String enclosingType = typeInfoRequestor.getEnclosingName();
// build the full class name.
StringBuilder sb = new StringBuilder(packageName);
sb.append('.');
if (enclosingType.length() > 0) {
sb.append(enclosingType);
sb.append('.');
}
sb.append(typeName);
String className = sb.toString();
try {
IType type = mJavaProject.findType(className);
if (type != null) {
// get the type hierarchy
ITypeHierarchy hierarchy = type.newSupertypeHierarchy(
new NullProgressMonitor());
// if the super class is not the reference class, it may inherit from
// it so we get its supertype. At some point it will be null and we
// will return false;
IType superType = type;
while ((superType = hierarchy.getSuperclass(superType)) != null) {
if (mReferenceClass.equals(superType.getFullyQualifiedName())) {
return true;
}
}
}
} catch (JavaModelException e) {
}
return false;
}
};
}
}
/**
* Classes which implement this interface provide a method processing newly created classes.
*/
public static interface IPostTypeCreationAction {
/**
* Sent to process a newly created class.
* @param newType the IType representing the newly created class.
*/
public void processNewType(IType newType);
}
/**
* Creates a {@link UiClassAttributeNode} object that will display ui to select or create
* classes.
* @param referenceClass The allowed supertype of the classes that are to be selected
* or created. Can be null.
* @param postCreationAction a {@link IPostTypeCreationAction} object handling post creation
* modification of the class.
* @param mandatory indicates if the class value is mandatory
* @param attributeDescriptor the {@link AttributeDescriptor} object linked to the Ui Node.
*/
public UiClassAttributeNode(String referenceClass, IPostTypeCreationAction postCreationAction,
boolean mandatory, AttributeDescriptor attributeDescriptor, UiElementNode uiParent) {
super(attributeDescriptor, uiParent);
mReferenceClass = referenceClass;
mPostCreationAction = postCreationAction;
mMandatory = mandatory;
}
/* (non-java doc)
* Creates a label widget and an associated text field.
* <p/>
* As most other parts of the android manifest editor, this assumes the
* parent uses a table layout with 2 columns.
*/
@Override
public void createUiControl(final Composite parent, IManagedForm managedForm) {
setManagedForm(managedForm);
FormToolkit toolkit = managedForm.getToolkit();
TextAttributeDescriptor desc = (TextAttributeDescriptor) getDescriptor();
StringBuilder label = new StringBuilder();
label.append("<form><p><a href='unused'>");
label.append(desc.getUiName());
label.append("</a></p></form>");
FormText formText = SectionHelper.createFormText(parent, toolkit, true /* isHtml */,
label.toString(), true /* setupLayoutData */);
formText.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
super.linkActivated(e);
handleLabelClick();
}
});
formText.setLayoutData(new TableWrapData(TableWrapData.LEFT, TableWrapData.MIDDLE));
SectionHelper.addControlTooltip(formText, desc.getTooltip());
Composite composite = toolkit.createComposite(parent);
composite.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.MIDDLE));
GridLayout gl = new GridLayout(2, false);
gl.marginHeight = gl.marginWidth = 0;
composite.setLayout(gl);
// Fixes missing text borders under GTK... also requires adding a 1-pixel margin
// for the text field below
toolkit.paintBordersFor(composite);
final Text text = toolkit.createText(composite, getCurrentValue());
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
gd.horizontalIndent = 1; // Needed by the fixed composite borders under GTK
text.setLayoutData(gd);
Button browseButton = toolkit.createButton(composite, "Browse...", SWT.PUSH);
setTextWidget(text);
browseButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
handleBrowseClick();
}
});
}
/* (non-java doc)
*
* Add a modify listener that will check the validity of the class
*/
@Override
protected void onAddValidators(final Text text) {
ModifyListener listener = new ModifyListener() {
public void modifyText(ModifyEvent e) {
try {
String textValue = text.getText().trim();
if (textValue.length() == 0) {
if (mMandatory) {
setErrorMessage("Value is mandatory", text);
} else {
setErrorMessage(null, text);
}
return;
}
// first we need the current java package.
String javaPackage = getManifestPackage();
// build the fully qualified name of the class
String className = AndroidManifestHelper.combinePackageAndClassName(javaPackage,
textValue);
// only test the vilibility for activities.
boolean testVisibility = AndroidConstants.CLASS_ACTIVITY.equals(
mReferenceClass);
// test the class
setErrorMessage(BaseProjectHelper.testClassForManifest(
BaseProjectHelper.getJavaProject(getProject()), className,
mReferenceClass, testVisibility), text);
} catch (CoreException ce) {
setErrorMessage(ce.getMessage(), text);
}
}
};
text.addModifyListener(listener);
// Make sure the validator removes its message(s) when the widget is disposed
text.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
// we don't want to use setErrorMessage, because we don't want to reset
// the error flag in the UiAttributeNode
getManagedForm().getMessageManager().removeMessage(text, text);
}
});
// Finally call the validator once to make sure the initial value is processed
listener.modifyText(null);
}
private void handleBrowseClick() {
Text text = getTextWidget();
// we need to get the project of the manifest.
IProject project = getProject();
if (project != null) {
// Create a search scope including only the source folder of the current
// project.
IJavaSearchScope scope = SearchEngine.createJavaSearchScope(
getPackageFragmentRoots(project), false);
try {
SelectionDialog dlg = JavaUI.createTypeDialog(text.getShell(),
PlatformUI.getWorkbench().getProgressService(),
scope,
IJavaElementSearchConstants.CONSIDER_CLASSES, // style
false, // no multiple selection
"**", //$NON-NLS-1$ //filter
new HierarchyTypeSelection(project, mReferenceClass));
dlg.setMessage(String.format("Select class name for element %1$s:",
getUiParent().getBreadcrumbTrailDescription(false /* include_root */)));
if (dlg instanceof ITypeSelectionComponent) {
((ITypeSelectionComponent)dlg).triggerSearch();
}
if (dlg.open() == Window.OK) {
Object[] results = dlg.getResult();
if (results.length == 1) {
handleNewType((IType)results[0]);
}
}
} catch (JavaModelException e1) {
}
}
}
private void handleLabelClick() {
// get the current value
String className = getTextWidget().getText().trim();
// get the package name from the manifest.
String packageName = getManifestPackage();
if (className.length() == 0) {
createNewClass(packageName, null /* className */);
} else {
// build back the fully qualified class name.
String fullClassName = className;
if (className.startsWith(".")) { //$NON-NLS-1$
fullClassName = packageName + className;
} else {
String[] segments = className.split(AndroidConstants.RE_DOT);
if (segments.length == 1) {
fullClassName = packageName + "." + className; //$NON-NLS-1$
}
}
// in case the type is enclosed, we need to replace the $ with .
fullClassName = fullClassName.replaceAll("\\$", "\\."); //$NON-NLS-1$ //$NON-NLS2$
// now we try to find the file that contains this class and we open it in the editor.
IProject project = getProject();
IJavaProject javaProject = JavaCore.create(project);
try {
IType result = javaProject.findType(fullClassName);
if (result != null) {
JavaUI.openInEditor(result);
} else {
// split the last segment from the fullClassname
int index = fullClassName.lastIndexOf('.');
if (index != -1) {
createNewClass(fullClassName.substring(0, index),
fullClassName.substring(index+1));
} else {
createNewClass(packageName, className);
}
}
} catch (JavaModelException e) {
} catch (PartInitException e) {
}
}
}
private IProject getProject() {
UiElementNode uiNode = getUiParent();
AndroidEditor editor = uiNode.getEditor();
IEditorInput input = editor.getEditorInput();
if (input instanceof IFileEditorInput) {
// from the file editor we can get the IFile object, and from it, the IProject.
IFile file = ((IFileEditorInput)input).getFile();
return file.getProject();
}
return null;
}
/**
* Returns the current value of the /manifest/package attribute.
* @return the package or an empty string if not found
*/
private String getManifestPackage() {
// get the root uiNode to get the 'package' attribute value.
UiElementNode rootNode = getUiParent().getUiRoot();
Element xmlElement = (Element) rootNode.getXmlNode();
if (xmlElement != null) {
return xmlElement.getAttribute(AndroidManifestDescriptors.PACKAGE_ATTR);
}
return ""; //$NON-NLS-1$
}
/**
* Computes and return the {@link IPackageFragmentRoot}s corresponding to the source folders of
* the specified project.
* @param project the project
* @return an array of IPackageFragmentRoot.
*/
private IPackageFragmentRoot[] getPackageFragmentRoots(IProject project) {
ArrayList<IPackageFragmentRoot> result = new ArrayList<IPackageFragmentRoot>();
try {
IJavaProject javaProject = JavaCore.create(project);
IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots();
for (int i = 0; i < roots.length; i++) {
IClasspathEntry entry = roots[i].getRawClasspathEntry();
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
result.add(roots[i]);
}
}
} catch (JavaModelException e) {
}
return result.toArray(new IPackageFragmentRoot[result.size()]);
}
private void handleNewType(IType type) {
Text text = getTextWidget();
// get the fully qualified name with $ to properly detect the enclosing types.
String name = type.getFullyQualifiedName('$');
String packageValue = getManifestPackage();
// check if the class doesn't start with the package.
if (packageValue.length() > 0 && name.startsWith(packageValue)) {
// if it does, we remove the package and the first dot.
name = name.substring(packageValue.length() + 1);
// look for how many segments we have left.
// if one, just write it that way.
// if more than one, write it with a leading dot.
String[] packages = name.split(AndroidConstants.RE_DOT);
if (packages.length == 1) {
text.setText(name);
} else {
text.setText("." + name); //$NON-NLS-1$
}
} else {
text.setText(name);
}
}
private void createNewClass(String packageName, String className) {
// create the wizard page for the class creation, and configure it
NewClassWizardPage page = new NewClassWizardPage();
// set the parent class
page.setSuperClass(mReferenceClass, true /* canBeModified */);
// get the source folders as java elements.
IPackageFragmentRoot[] roots = getPackageFragmentRoots(getProject());
IPackageFragmentRoot currentRoot = null;
IPackageFragment currentFragment = null;
int packageMatchCount = -1;
for (IPackageFragmentRoot root : roots) {
// Get the java element for the package.
// This method is said to always return a IPackageFragment even if the
// underlying folder doesn't exist...
IPackageFragment fragment = root.getPackageFragment(packageName);
if (fragment != null && fragment.exists()) {
// we have a perfect match! we use it.
currentRoot = root;
currentFragment = fragment;
packageMatchCount = -1;
break;
} else {
// we don't have a match. we look for the fragment with the best match
// (ie the closest parent package we can find)
try {
IJavaElement[] children;
children = root.getChildren();
for (IJavaElement child : children) {
if (child instanceof IPackageFragment) {
fragment = (IPackageFragment)child;
if (packageName.startsWith(fragment.getElementName())) {
// its a match. get the number of segments
String[] segments = fragment.getElementName().split("\\."); //$NON-NLS-1$
if (segments.length > packageMatchCount) {
packageMatchCount = segments.length;
currentFragment = fragment;
currentRoot = root;
}
}
}
}
} catch (JavaModelException e) {
// Couldn't get the children: we just ignore this package root.
}
}
}
ArrayList<IPackageFragment> createdFragments = null;
if (currentRoot != null) {
// if we have a perfect match, we set it and we're done.
if (packageMatchCount == -1) {
page.setPackageFragmentRoot(currentRoot, true /* canBeModified*/);
page.setPackageFragment(currentFragment, true /* canBeModified */);
} else {
// we have a partial match.
// create the package. We have to start with the first segment so that we
// know what to delete in case of a cancel.
try {
createdFragments = new ArrayList<IPackageFragment>();
int totalCount = packageName.split("\\.").length; //$NON-NLS-1$
int count = 0;
int index = -1;
// skip the matching packages
while (count < packageMatchCount) {
index = packageName.indexOf('.', index+1);
count++;
}
// create the rest of the segments, except for the last one as indexOf will
// return -1;
while (count < totalCount - 1) {
index = packageName.indexOf('.', index+1);
count++;
createdFragments.add(currentRoot.createPackageFragment(
packageName.substring(0, index),
true /* force*/, new NullProgressMonitor()));
}
// create the last package
createdFragments.add(currentRoot.createPackageFragment(
packageName, true /* force*/, new NullProgressMonitor()));
// set the root and fragment in the Wizard page
page.setPackageFragmentRoot(currentRoot, true /* canBeModified*/);
page.setPackageFragment(createdFragments.get(createdFragments.size()-1),
true /* canBeModified */);
} catch (JavaModelException e) {
// if we can't create the packages, there's a problem. we revert to the default
// package
for (IPackageFragmentRoot root : roots) {
// Get the java element for the package.
// This method is said to always return a IPackageFragment even if the
// underlying folder doesn't exist...
IPackageFragment fragment = root.getPackageFragment(packageName);
if (fragment != null && fragment.exists()) {
page.setPackageFragmentRoot(root, true /* canBeModified*/);
page.setPackageFragment(fragment, true /* canBeModified */);
break;
}
}
}
}
} else if (roots.length > 0) {
// if we haven't found a valid fragment, we set the root to the first source folder.
page.setPackageFragmentRoot(roots[0], true /* canBeModified*/);
}
// if we have a starting class name we use it
if (className != null) {
page.setTypeName(className, true /* canBeModified*/);
}
// create the action that will open it the wizard.
OpenNewClassWizardAction action = new OpenNewClassWizardAction();
action.setConfiguredWizardPage(page);
action.run();
IJavaElement element = action.getCreatedElement();
if (element != null) {
if (element.getElementType() == IJavaElement.TYPE) {
IType type = (IType)element;
if (mPostCreationAction != null) {
mPostCreationAction.processNewType(type);
}
handleNewType(type);
}
} else {
// lets delete the packages we created just for this.
// we need to start with the leaf and go up
if (createdFragments != null) {
try {
for (int i = createdFragments.size() - 1 ; i >= 0 ; i--) {
createdFragments.get(i).delete(true /* force*/, new NullProgressMonitor());
}
} catch (JavaModelException e) {
e.printStackTrace();
}
}
}
}
/**
* Sets the error messages. If message is <code>null</code>, the message is removed.
* @param message the message to set, or <code>null</code> to remove the current message
* @param textWidget the {@link Text} widget associated to the message.
*/
private final void setErrorMessage(String message, Text textWidget) {
if (message != null) {
setHasError(true);
getManagedForm().getMessageManager().addMessage(textWidget, message, null /* data */,
IMessageProvider.ERROR, textWidget);
} else {
setHasError(false);
getManagedForm().getMessageManager().removeMessage(textWidget, textWidget);
}
}
@Override
public String[] getPossibleValues() {
// TODO: compute a list of existing classes for content assist completion
return null;
}
}

View File

@@ -1,89 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.model;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
import com.android.ide.eclipse.editors.manifest.descriptors.ManifestElementDescriptor;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.w3c.dom.Element;
/**
* Represents an XML node that can be modified by the user interface in the XML editor.
* <p/>
* Each tree viewer used in the application page's parts needs to keep a model representing
* each underlying node in the tree. This interface represents the base type for such a node.
* <p/>
* Each node acts as an intermediary model between the actual XML model (the real data support)
* and the tree viewers or the corresponding page parts.
* <p/>
* Element nodes don't contain data per se. Their data is contained in their attributes
* as well as their children's attributes, see {@link UiAttributeNode}.
* <p/>
* The structure of a given {@link UiElementNode} is declared by a corresponding
* {@link ElementDescriptor}.
*/
public final class UiManifestElementNode extends UiElementNode {
/**
* Creates a new {@link UiElementNode} described by a given {@link ElementDescriptor}.
*
* @param elementDescriptor The {@link ElementDescriptor} for the XML node. Cannot be null.
*/
public UiManifestElementNode(ManifestElementDescriptor elementDescriptor) {
super(elementDescriptor);
}
/**
* Computes a short string describing the UI node suitable for tree views.
* Uses the element's attribute "android:name" if present, or the "android:label" one
* followed by the element's name.
*
* @return A short string describing the UI node suitable for tree views.
*/
@Override
public String getShortDescription() {
if (getXmlNode() != null &&
getXmlNode() instanceof Element &&
getXmlNode().hasAttributes()) {
// Application and Manifest nodes have a special treatment: they are unique nodes
// so we don't bother trying to differentiate their strings and we fall back to
// just using the UI name below.
ElementDescriptor desc = getDescriptor();
if (desc != AndroidManifestDescriptors.MANIFEST_ELEMENT &&
desc != AndroidManifestDescriptors.APPLICATION_ELEMENT) {
Element elem = (Element) getXmlNode();
String attr = elem.getAttributeNS(AndroidConstants.NS_RESOURCES,
AndroidManifestDescriptors.ANDROID_NAME_ATTR);
if (attr == null || attr.length() == 0) {
attr = elem.getAttributeNS(AndroidConstants.NS_RESOURCES,
AndroidManifestDescriptors.ANDROID_LABEL_ATTR);
}
if (attr != null && attr.length() > 0) {
return String.format("%1$s (%2$s)", attr, getDescriptor().getUiName());
}
}
}
return String.format("%1$s", getDescriptor().getUiName());
}
}

View File

@@ -1,319 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.model;
import com.android.ide.eclipse.editors.AndroidEditor;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
import com.android.ide.eclipse.editors.ui.SectionHelper;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.editors.uimodel.UiTextAttributeNode;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.actions.OpenNewPackageWizardAction;
import org.eclipse.jdt.ui.actions.ShowInPackageViewAction;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.dialogs.SelectionDialog;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.TableWrapData;
import java.util.ArrayList;
/**
* Represents an XML attribute for a package, that can be modified using a simple text field or
* a dialog to choose an existing package. Also, there's a link to create a new package.
* <p/>
* See {@link UiTextAttributeNode} for more information.
*/
public class UiPackageAttributeNode extends UiTextAttributeNode {
/**
* Creates a {@link UiPackageAttributeNode} object that will display ui to select or create
* a package.
* @param attributeDescriptor the {@link AttributeDescriptor} object linked to the Ui Node.
*/
public UiPackageAttributeNode(AttributeDescriptor attributeDescriptor, UiElementNode uiParent) {
super(attributeDescriptor, uiParent);
}
/* (non-java doc)
* Creates a label widget and an associated text field.
* <p/>
* As most other parts of the android manifest editor, this assumes the
* parent uses a table layout with 2 columns.
*/
@Override
public void createUiControl(final Composite parent, final IManagedForm managedForm) {
setManagedForm(managedForm);
FormToolkit toolkit = managedForm.getToolkit();
TextAttributeDescriptor desc = (TextAttributeDescriptor) getDescriptor();
StringBuilder label = new StringBuilder();
label.append("<form><p><a href='unused'>"); //$NON-NLS-1$
label.append(desc.getUiName());
label.append("</a></p></form>"); //$NON-NLS-1$
FormText formText = SectionHelper.createFormText(parent, toolkit, true /* isHtml */,
label.toString(), true /* setupLayoutData */);
formText.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
super.linkActivated(e);
doLabelClick();
}
});
formText.setLayoutData(new TableWrapData(TableWrapData.LEFT, TableWrapData.MIDDLE));
SectionHelper.addControlTooltip(formText, desc.getTooltip());
Composite composite = toolkit.createComposite(parent);
composite.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.MIDDLE));
GridLayout gl = new GridLayout(2, false);
gl.marginHeight = gl.marginWidth = 0;
composite.setLayout(gl);
// Fixes missing text borders under GTK... also requires adding a 1-pixel margin
// for the text field below
toolkit.paintBordersFor(composite);
final Text text = toolkit.createText(composite, getCurrentValue());
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
gd.horizontalIndent = 1; // Needed by the fixed composite borders under GTK
text.setLayoutData(gd);
setTextWidget(text);
Button browseButton = toolkit.createButton(composite, "Browse...", SWT.PUSH);
browseButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
doBrowseClick();
}
});
}
/* (non-java doc)
* Adds a validator to the text field that calls managedForm.getMessageManager().
*/
@Override
protected void onAddValidators(final Text text) {
ModifyListener listener = new ModifyListener() {
public void modifyText(ModifyEvent e) {
String package_name = text.getText();
if (package_name.indexOf('.') < 1) {
getManagedForm().getMessageManager().addMessage(text,
"Package name should contain at least two identifiers.",
null /* data */, IMessageProvider.ERROR, text);
} else {
getManagedForm().getMessageManager().removeMessage(text, text);
}
}
};
text.addModifyListener(listener);
// Make sure the validator removes its message(s) when the widget is disposed
text.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
getManagedForm().getMessageManager().removeMessage(text, text);
}
});
// Finally call the validator once to make sure the initial value is processed
listener.modifyText(null);
}
/**
* Handles response to the Browse button by creating a Package dialog.
* */
private void doBrowseClick() {
Text text = getTextWidget();
// we need to get the project of the manifest.
IProject project = getProject();
if (project != null) {
try {
SelectionDialog dlg = JavaUI.createPackageDialog(text.getShell(),
JavaCore.create(project), 0);
dlg.setTitle("Select Android Package");
dlg.setMessage("Select the package for the Android project.");
dlg.setDefaultImage(EditorsPlugin.getAndroidLogo());
if (dlg.open() == Window.OK) {
Object[] results = dlg.getResult();
if (results.length == 1) {
setPackageTextField((IPackageFragment)results[0]);
}
}
} catch (JavaModelException e1) {
}
}
}
/**
* Handles response to the Label hyper link being activated.
*/
private void doLabelClick() {
// get the current package name
String package_name = getTextWidget().getText().trim();
if (package_name.length() == 0) {
createNewPackage();
} else {
// Try to select the package in the Package Explorer for the current
// project and the current editor's site.
IProject project = getProject();
if (project == null) {
EditorsPlugin.log(IStatus.ERROR, "Failed to get project for UiPackageAttribute"); //$NON-NLS-1$
return;
}
IWorkbenchPartSite site = getUiParent().getEditor().getSite();
if (site == null) {
EditorsPlugin.log(IStatus.ERROR, "Failed to get editor site for UiPackageAttribute"); //$NON-NLS-1$
return;
}
for (IPackageFragmentRoot root : getPackageFragmentRoots(project)) {
IPackageFragment fragment = root.getPackageFragment(package_name);
if (fragment != null && fragment.exists()) {
ShowInPackageViewAction action = new ShowInPackageViewAction(site);
action.run(fragment);
// This action's run() doesn't provide the status (although internally it could)
// so we just assume it worked.
return;
}
}
}
}
/**
* Utility method that returns the project for the current file being edited.
*
* @return The IProject for the current file being edited or null.
*/
private IProject getProject() {
UiElementNode uiNode = getUiParent();
AndroidEditor editor = uiNode.getEditor();
IEditorInput input = editor.getEditorInput();
if (input instanceof IFileEditorInput) {
// from the file editor we can get the IFile object, and from it, the IProject.
IFile file = ((IFileEditorInput)input).getFile();
return file.getProject();
}
return null;
}
/**
* Utility method that computes and returns the list of {@link IPackageFragmentRoot}
* corresponding to the source folder of the specified project.
*
* @param project the project
* @return an array of IPackageFragmentRoot. Can be empty but not null.
*/
private IPackageFragmentRoot[] getPackageFragmentRoots(IProject project) {
ArrayList<IPackageFragmentRoot> result = new ArrayList<IPackageFragmentRoot>();
try {
IJavaProject javaProject = JavaCore.create(project);
IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots();
for (int i = 0; i < roots.length; i++) {
IClasspathEntry entry = roots[i].getRawClasspathEntry();
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
result.add(roots[i]);
}
}
} catch (JavaModelException e) {
}
return result.toArray(new IPackageFragmentRoot[result.size()]);
}
/**
* Utility method that sets the package's text field to the package fragment's name.
* */
private void setPackageTextField(IPackageFragment type) {
Text text = getTextWidget();
String name = type.getElementName();
text.setText(name);
}
/**
* Displays and handles a "Create Package Wizard".
*
* This is invoked by doLabelClick() when clicking on the hyperlink label with an
* empty package text field.
*/
private void createNewPackage() {
OpenNewPackageWizardAction action = new OpenNewPackageWizardAction();
IProject project = getProject();
action.setSelection(new StructuredSelection(project));
action.run();
IJavaElement element = action.getCreatedElement();
if (element != null &&
element.exists() &&
element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
setPackageTextField((IPackageFragment) element);
}
}
@Override
public String[] getPossibleValues() {
// TODO: compute a list of existing packages for content assist completion
return null;
}
}

View File

@@ -1,174 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.pages;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor;
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
import com.android.ide.eclipse.editors.ui.UiElementPart;
import com.android.ide.eclipse.editors.uimodel.IUiUpdateListener;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
/**
* Application's attributes section part for Application page.
* <p/>
* This part is displayed at the top of the application page and displays all the possible
* attributes of an application node in the AndroidManifest (icon, class name, label, etc.)
*/
final class ApplicationAttributesPart extends UiElementPart {
/** Listen to changes to the UI node for <application> and updates the UI */
private AppNodeUpdateListener mAppNodeUpdateListener;
/** ManagedForm needed to create the UI controls */
private IManagedForm mManagedForm;
public ApplicationAttributesPart(Composite body, FormToolkit toolkit, ManifestEditor editor,
UiElementNode applicationUiNode) {
super(body, toolkit, editor, applicationUiNode,
"Application Attributes", // section title
"Defines the attributes specific to the application.", // section description
Section.TWISTIE | Section.EXPANDED);
}
/**
* Changes and refreshes the Application UI node handle by the this part.
*/
@Override
public void setUiElementNode(UiElementNode uiElementNode) {
super.setUiElementNode(uiElementNode);
createUiAttributes(mManagedForm);
}
/* (non-java doc)
* Create the controls to edit the attributes for the given ElementDescriptor.
* <p/>
* This MUST not be called by the constructor. Instead it must be called from
* <code>initialize</code> (i.e. right after the form part is added to the managed form.)
* <p/>
* Derived classes can override this if necessary.
*
* @param managedForm The owner managed form
*/
@Override
protected void createFormControls(final IManagedForm managedForm) {
mManagedForm = managedForm;
setTable(createTableLayout(managedForm.getToolkit(), 4 /* numColumns */));
mAppNodeUpdateListener = new AppNodeUpdateListener();
getUiElementNode().addUpdateListener(mAppNodeUpdateListener);
createUiAttributes(mManagedForm);
}
@Override
public void dispose() {
super.dispose();
if (getUiElementNode() != null && mAppNodeUpdateListener != null) {
getUiElementNode().removeUpdateListener(mAppNodeUpdateListener);
mAppNodeUpdateListener = null;
}
}
@Override
protected void createUiAttributes(IManagedForm managedForm) {
Composite table = getTable();
if (table == null || managedForm == null) {
return;
}
// Remove any old UI controls
for (Control c : table.getChildren()) {
c.dispose();
}
UiElementNode uiElementNode = getUiElementNode();
AttributeDescriptor[] attr_desc_list = uiElementNode.getAttributeDescriptors();
// Display the attributes in 2 columns:
// attr 0 | attr 4
// attr 1 | attr 5
// attr 2 | attr 6
// attr 3 | attr 7
// that is we have to fill the grid in order 0, 4, 1, 5, 2, 6, 3, 7
// thus index = i/2 + (i is odd * n/2)
int n = attr_desc_list.length;
int n2 = (int) Math.ceil(n / 2.0);
for (int i = 0; i < n; i++) {
AttributeDescriptor attr_desc = attr_desc_list[i / 2 + (i & 1) * n2];
if (attr_desc instanceof XmlnsAttributeDescriptor) {
// Do not show hidden attributes
continue;
}
UiAttributeNode ui_attr = uiElementNode.findUiAttribute(attr_desc);
if (ui_attr != null) {
ui_attr.createUiControl(table, managedForm);
} else {
// The XML has an extra attribute which wasn't declared in
// AndroidManifestDescriptors. This is not a problem, we just ignore it.
EditorsPlugin.log(IStatus.WARNING,
"Attribute %1$s not declared in node %2$s, ignored.", //$NON-NLS-1$
attr_desc.getXmlLocalName(),
uiElementNode.getDescriptor().getXmlName());
}
}
if (n == 0) {
createLabel(table, managedForm.getToolkit(),
"No attributes to display, waiting for SDK to finish loading...",
null /* tooltip */ );
}
// Initialize the enabled/disabled state
if (mAppNodeUpdateListener != null) {
mAppNodeUpdateListener.uiElementNodeUpdated(uiElementNode, null /* state, not used */);
}
// Tell the section that the layout has changed.
layoutChanged();
}
/**
* This listener synchronizes the UI with the actual presence of the application XML node.
*/
private class AppNodeUpdateListener implements IUiUpdateListener {
public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) {
// The UiElementNode for the application XML node always exists, even
// if there is no corresponding XML node in the XML file.
//
// We enable the UI here if the XML node is not null.
Composite table = getTable();
boolean exists = (ui_node.getXmlNode() != null);
if (table != null && table.getEnabled() != exists) {
table.setEnabled(exists);
for (Control c : table.getChildren()) {
c.setEnabled(exists);
}
}
}
}
}

View File

@@ -1,120 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.pages;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
import com.android.ide.eclipse.editors.ui.tree.UiTreeBlock;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormPage;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
/**
* Page for "Application" settings, part of the AndroidManifest form editor.
* <p/>
* Useful reference:
* <a href="http://www.eclipse.org/articles/Article-Forms/article.html">
* http://www.eclipse.org/articles/Article-Forms/article.html</a>
*/
public final class ApplicationPage extends FormPage {
/** Page id used for switching tabs programmatically */
public final static String PAGE_ID = "application_page"; //$NON-NLS-1$
/** Container editor */
ManifestEditor mEditor;
/** The Application Toogle part */
private ApplicationToggle mTooglePart;
/** The Application Attributes part */
private ApplicationAttributesPart mAttrPart;
/** The tree view block */
private UiTreeBlock mTreeBlock;
public ApplicationPage(ManifestEditor editor) {
super(editor, PAGE_ID, "Application"); // tab's label, keep it short
mEditor = editor;
}
/**
* Creates the content in the form hosted in this page.
*
* @param managedForm the form hosted in this page.
*/
@Override
protected void createFormContent(IManagedForm managedForm) {
super.createFormContent(managedForm);
ScrolledForm form = managedForm.getForm();
form.setText("Android Manifest Application");
form.setImage(EditorsPlugin.getAndroidLogo());
UiElementNode appUiNode = getUiApplicationNode();
Composite body = form.getBody();
FormToolkit toolkit = managedForm.getToolkit();
// We usually prefer to have a ColumnLayout here. However
// MasterDetailsBlock.createContent() below will reset the body's layout to a grid layout.
mTooglePart = new ApplicationToggle(body, toolkit, mEditor, appUiNode);
mTooglePart.getSection().setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
managedForm.addPart(mTooglePart);
mAttrPart = new ApplicationAttributesPart(body, toolkit, mEditor, appUiNode);
mAttrPart.getSection().setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
managedForm.addPart(mAttrPart);
mTreeBlock = new UiTreeBlock(mEditor, appUiNode,
false /* autoCreateRoot */,
null /* element filters */,
"Application Nodes",
"List of all elements in the application");
mTreeBlock.createContent(managedForm);
}
/**
* Retrieves the application UI node. Since this is a mandatory node, it *always*
* exists, even if there is no matching XML node.
*/
private UiElementNode getUiApplicationNode() {
ElementDescriptor desc = AndroidManifestDescriptors.APPLICATION_ELEMENT;
return mEditor.getUiRootNode().findUiChildNode(desc.getXmlName());
}
/**
* Changes and refreshes the Application UI node handled by the sub parts.
*/
public void refreshUiApplicationNode() {
UiElementNode appUiNode = getUiApplicationNode();
if (mTooglePart != null) {
mTooglePart.setUiElementNode(appUiNode);
}
if (mAttrPart != null) {
mAttrPart.setUiElementNode(appUiNode);
}
if (mTreeBlock != null) {
mTreeBlock.changeRootAndDescriptors(appUiNode,
null /* element filters */,
true /* refresh */);
}
}
}

View File

@@ -1,304 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.pages;
import com.android.ide.eclipse.common.resources.FrameworkResourceManager;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
import com.android.ide.eclipse.editors.ui.UiElementPart;
import com.android.ide.eclipse.editors.uimodel.IUiUpdateListener;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.editors.uimodel.IUiUpdateListener.UiUpdateState;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.forms.widgets.TableWrapData;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
/**
* Appllication Toogle section part for application page.
*/
final class ApplicationToggle extends UiElementPart {
/** Checkbox indicating whether an application node is present */
private Button mCheckbox;
/** Listen to changes to the UI node for <application> and updates the checkbox */
private AppNodeUpdateListener mAppNodeUpdateListener;
/** Internal flag to know where we're programmatically modifying the checkbox and we want to
* avoid triggering the checkbox's callback. */
public boolean mInternalModification;
private FormText mTooltipFormText;
public ApplicationToggle(Composite body, FormToolkit toolkit, ManifestEditor editor,
UiElementNode applicationUiNode) {
super(body, toolkit, editor, applicationUiNode,
"Application Toggle",
null, /* description */
Section.TWISTIE | Section.EXPANDED);
}
@Override
public void dispose() {
super.dispose();
if (getUiElementNode() != null && mAppNodeUpdateListener != null) {
getUiElementNode().removeUpdateListener(mAppNodeUpdateListener);
mAppNodeUpdateListener = null;
}
}
/**
* Changes and refreshes the Application UI node handle by the this part.
*/
@Override
public void setUiElementNode(UiElementNode uiElementNode) {
super.setUiElementNode(uiElementNode);
updateTooltip();
// Set the state of the checkbox
mAppNodeUpdateListener.uiElementNodeUpdated(getUiElementNode(),
UiUpdateState.CHILDREN_CHANGED);
}
/**
* Create the controls to edit the attributes for the given ElementDescriptor.
* <p/>
* This MUST not be called by the constructor. Instead it must be called from
* <code>initialize</code> (i.e. right after the form part is added to the managed form.)
*
* @param managedForm The owner managed form
*/
@Override
protected void createFormControls(IManagedForm managedForm) {
FormToolkit toolkit = managedForm.getToolkit();
Composite table = createTableLayout(toolkit, 1 /* numColumns */);
mTooltipFormText = createFormText(table, toolkit, true, "<form></form>",
false /* setupLayoutData */);
updateTooltip();
mCheckbox = toolkit.createButton(table,
"Define an <application> tag in the AndroidManifest.xml",
SWT.CHECK);
mCheckbox.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP));
mCheckbox.setSelection(false);
mCheckbox.addSelectionListener(new CheckboxSelectionListener());
mAppNodeUpdateListener = new AppNodeUpdateListener();
getUiElementNode().addUpdateListener(mAppNodeUpdateListener);
// Initialize the state of the checkbox
mAppNodeUpdateListener.uiElementNodeUpdated(getUiElementNode(),
UiUpdateState.CHILDREN_CHANGED);
// Tell the section that the layout has changed.
layoutChanged();
}
/**
* Updates the application tooltip in the form text.
* If there is no tooltip, the form text is hidden.
*/
private void updateTooltip() {
boolean isVisible = false;
String tooltip = getUiElementNode().getDescriptor().getTooltip();
if (tooltip != null) {
tooltip = DescriptorsUtils.formatFormText(tooltip,
getUiElementNode().getDescriptor(),
FrameworkResourceManager.getInstance().getDocumentationBaseUrl());
mTooltipFormText.setText(tooltip, true /* parseTags */, true /* expandURLs */);
mTooltipFormText.setImage(DescriptorsUtils.IMAGE_KEY, EditorsPlugin.getAndroidLogo());
mTooltipFormText.addHyperlinkListener(getEditor().createHyperlinkListener());
isVisible = true;
}
mTooltipFormText.setVisible(isVisible);
}
/**
* This listener synchronizes the XML application node when the checkbox
* is changed by the user.
*/
private class CheckboxSelectionListener extends SelectionAdapter {
private Node mUndoXmlNode;
private Node mUndoXmlParent;
private Node mUndoXmlNextNode;
private Node mUndoXmlNextElement;
private Document mUndoXmlDocument;
@Override
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
if (!mInternalModification && getUiElementNode() != null) {
getUiElementNode().getEditor().editXmlModel(new Runnable() {
public void run() {
if (mCheckbox.getSelection()) {
// The user wants an <application> node. Either restore a previous one
// or create a full new one.
boolean create = true;
if (mUndoXmlNode != null) {
create = !restoreApplicationNode();
}
if (create) {
getUiElementNode().createXmlNode();
}
} else {
// Users no longer wants the <application> node.
removeApplicationNode();
}
}
});
}
}
/**
* Restore a previously "saved" application node.
*
* @return True if the node could be restored, false otherwise.
*/
private boolean restoreApplicationNode() {
if (mUndoXmlDocument == null || mUndoXmlNode == null) {
return false;
}
// Validate node references...
mUndoXmlParent = validateNode(mUndoXmlDocument, mUndoXmlParent);
mUndoXmlNextNode = validateNode(mUndoXmlDocument, mUndoXmlNextNode);
mUndoXmlNextElement = validateNode(mUndoXmlDocument, mUndoXmlNextElement);
if (mUndoXmlParent == null){
// If the parent node doesn't exist, try to find a new manifest node.
// If it doesn't exist, create it.
mUndoXmlParent = getUiElementNode().getUiParent().prepareCommit();
mUndoXmlNextNode = null;
mUndoXmlNextElement = null;
}
boolean success = false;
if (mUndoXmlParent != null) {
// If the parent is still around, reuse the same node.
// Ideally we want to insert the node before what used to be its next sibling.
// If that's not possible, we try to insert it before its next sibling element.
// If that's not possible either, it will be inserted at the end of the parent's.
Node next = mUndoXmlNextNode;
if (next == null) {
next = mUndoXmlNextElement;
}
mUndoXmlParent.insertBefore(mUndoXmlNode, next);
if (next == null) {
Text sep = mUndoXmlDocument.createTextNode("\n"); //$NON-NLS-1$
mUndoXmlParent.insertBefore(sep, null); // insert separator before end tag
}
success = true;
}
// Remove internal references to avoid using them twice
mUndoXmlParent = null;
mUndoXmlNextNode = null;
mUndoXmlNextElement = null;
mUndoXmlNode = null;
mUndoXmlDocument = null;
return success;
}
/**
* Validates that the given xml_node is still either the root node or one of its
* direct descendants.
*
* @param root_node The root of the node hierarchy to examine.
* @param xml_node The XML node to find.
* @return Returns xml_node if it is, otherwise returns null.
*/
private Node validateNode(Node root_node, Node xml_node) {
if (root_node == xml_node) {
return xml_node;
} else {
for (Node node = root_node.getFirstChild(); node != null;
node = node.getNextSibling()) {
if (root_node == xml_node || validateNode(node, xml_node) != null) {
return xml_node;
}
}
}
return null;
}
/**
* Removes the <application> node from the hierarchy.
* Before doing that, we try to remember where it was so that we can put it back
* in the same place.
*/
private void removeApplicationNode() {
// Make sure the node actually exists...
Node xml_node = getUiElementNode().getXmlNode();
if (xml_node == null) {
return;
}
// Save its parent, next sibling and next element
mUndoXmlDocument = xml_node.getOwnerDocument();
mUndoXmlParent = xml_node.getParentNode();
mUndoXmlNextNode = xml_node.getNextSibling();
mUndoXmlNextElement = mUndoXmlNextNode;
while (mUndoXmlNextElement != null &&
mUndoXmlNextElement.getNodeType() != Node.ELEMENT_NODE) {
mUndoXmlNextElement = mUndoXmlNextElement.getNextSibling();
}
// Actually remove the node from the hierarchy and keep it here.
// The returned node looses its parents/siblings pointers.
mUndoXmlNode = getUiElementNode().deleteXmlNode();
}
}
/**
* This listener synchronizes the UI (i.e. the checkbox) with the
* actual presence of the application XML node.
*/
private class AppNodeUpdateListener implements IUiUpdateListener {
public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) {
// The UiElementNode for the application XML node always exists, even
// if there is no corresponding XML node in the XML file.
//
// To update the checkbox to reflect the actual state, we just need
// to check if the XML node is null.
try {
mInternalModification = true;
boolean exists = ui_node.getXmlNode() != null;
if (mCheckbox.getSelection() != exists) {
mCheckbox.setSelection(exists);
}
} finally {
mInternalModification = false;
}
}
}
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.pages;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
import com.android.ide.eclipse.editors.ui.tree.UiTreeBlock;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormPage;
import org.eclipse.ui.forms.widgets.ScrolledForm;
/**
* Page for instrumentation settings, part of the AndroidManifest form editor.
*/
public final class InstrumentationPage extends FormPage {
/** Page id used for switching tabs programmatically */
public final static String PAGE_ID = "instrumentation_page"; //$NON-NLS-1$
/** Container editor */
ManifestEditor mEditor;
public InstrumentationPage(ManifestEditor editor) {
super(editor, PAGE_ID, "Instrumentation"); // tab's label, keep it short
mEditor = editor;
}
/**
* Creates the content in the form hosted in this page.
*
* @param managedForm the form hosted in this page.
*/
@Override
protected void createFormContent(IManagedForm managedForm) {
super.createFormContent(managedForm);
ScrolledForm form = managedForm.getForm();
form.setText("Android Manifest Instrumentation");
form.setImage(EditorsPlugin.getAndroidLogo());
UiElementNode manifest = mEditor.getUiRootNode();
UiTreeBlock block = new UiTreeBlock(mEditor, manifest,
true /* autoCreateRoot */,
new ElementDescriptor[] { AndroidManifestDescriptors.INTRUMENTATION_ELEMENT },
"Instrumentation",
"List of instrumentations defined in the manifest");
block.createContent(managedForm);
}
}

View File

@@ -1,84 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.pages;
import com.android.ide.eclipse.common.project.ExportHelper;
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
import com.android.ide.eclipse.editors.ui.SectionHelper.ManifestSectionPart;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.part.FileEditorInput;
/**
* Export section part for overview page.
*/
final class OverviewExportPart extends ManifestSectionPart {
private final OverviewPage mOverviewPage;
public OverviewExportPart(OverviewPage overviewPage, Composite body, FormToolkit toolkit, ManifestEditor editor) {
super(body, toolkit, Section.TWISTIE | Section.EXPANDED, true /* description */);
mOverviewPage = overviewPage;
Section section = getSection();
section.setText("Exporting");
section.setDescription("To export the application for distribution, you have the following options:");
Composite table = createTableLayout(toolkit, 2 /* numColumns */);
StringBuffer buf = new StringBuffer();
buf.append("<form><li><a href=\"wizard\">"); //$NON-NLS-1$
buf.append("Use the export wizard");
buf.append("</a></li><li><a href=\"manual\">"); //$NON-NLS-1$
buf.append("Export an unsigned apk");
buf.append("</a>"); //$NON-NLS-1$
buf.append(" and sign it manually");
buf.append("</li></form>"); //$NON-NLS-1$
FormText text = createFormText(table, toolkit, true, buf.toString(),
false /* setupLayoutData */);
text.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
// get the project from the editor
IEditorInput input = mOverviewPage.mEditor.getEditorInput();
if (input instanceof FileEditorInput) {
FileEditorInput fileInput = (FileEditorInput)input;
IFile file = fileInput.getFile();
IProject project = file.getProject();
if ("manual".equals(e.data)) { //$NON-NLS-1$
// now we can export an unsigned apk for the project.
ExportHelper.exportProject(project);
} else {
// call the export wizard
ExportHelper.startExportWizard(project);
}
}
}
});
layoutChanged();
}
}

View File

@@ -1,208 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.pages;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
import com.android.ide.eclipse.editors.ui.UiElementPart;
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.w3c.dom.Node;
/**
* Generic info section part for overview page
*/
final class OverviewInfoPart extends UiElementPart {
private IManagedForm mManagedForm;
public OverviewInfoPart(Composite body, FormToolkit toolkit, ManifestEditor editor) {
super(body, toolkit, editor,
null, // uiElementNode
"General Information", // section title
"Defines general information about the AndroidManifest.xml", // section description
Section.TWISTIE | Section.EXPANDED);
}
/**
* Retrieves the UiElementNode that this part will edit. The node must exist
* and can't be null, by design, because it's a mandatory node.
*/
private static UiElementNode getManifestUiNode(ManifestEditor editor) {
ElementDescriptor desc = AndroidManifestDescriptors.MANIFEST_ELEMENT;
if (editor.getUiRootNode().getDescriptor() == desc) {
return editor.getUiRootNode();
} else {
return editor.getUiRootNode().findUiChildNode(desc.getXmlName());
}
}
/**
* Retrieves the uses-sdk UI node. Since this is a mandatory node, it *always*
* exists, even if there is no matching XML node.
*/
private UiElementNode getUsesSdkUiNode(ManifestEditor editor) {
ElementDescriptor desc = AndroidManifestDescriptors.USES_SDK_ELEMENT;
return editor.getUiRootNode().findUiChildNode(desc.getXmlName());
}
/**
* Overridden in order to capture the current managed form.
*
* {@inheritDoc}
*/
@Override
protected void createFormControls(final IManagedForm managedForm) {
mManagedForm = managedForm;
super.createFormControls(managedForm);
}
/**
* Removes any existing Attribute UI widgets and recreate them if the SDK has changed.
* <p/>
* This is called by {@link OverviewPage#refreshUiApplicationNode()} when the
* SDK has changed.
*/
public void onSdkChanged() {
createUiAttributes(mManagedForm);
}
/**
* Overridden to add the description and the ui attributes of both the
* manifest and uses-sdk UI nodes.
* <p/>
* {@inheritDoc}
*/
@Override
protected void fillTable(Composite table, IManagedForm managedForm) {
int n = 0;
UiElementNode uiNode = getManifestUiNode(getEditor());
n += insertUiAttributes(uiNode, table, managedForm);
uiNode = getUsesSdkUiNode(getEditor());
n += insertUiAttributes(uiNode, table, managedForm);
if (n == 0) {
createLabel(table, managedForm.getToolkit(),
"No attributes to display, waiting for SDK to finish loading...",
null /* tooltip */ );
}
layoutChanged();
}
/**
* Overridden to tests whether either the manifest or uses-sdk nodes parts are dirty.
* <p/>
* {@inheritDoc}
*
* @return <code>true</code> if the part is dirty, <code>false</code>
* otherwise.
*/
@Override
public boolean isDirty() {
boolean dirty = super.isDirty();
if (!dirty) {
UiElementNode uiNode = getManifestUiNode(getEditor());
if (uiNode != null) {
for (UiAttributeNode ui_attr : uiNode.getUiAttributes()) {
if (ui_attr.isDirty()) {
markDirty();
dirty = true;
break;
}
}
}
}
if (!dirty) {
UiElementNode uiNode = getUsesSdkUiNode(getEditor());
if (uiNode != null) {
for (UiAttributeNode ui_attr : uiNode.getUiAttributes()) {
if (ui_attr.isDirty()) {
markDirty();
dirty = true;
break;
}
}
}
}
return dirty;
}
/**
* Overridden to save both the manifest or uses-sdk nodes.
* <p/>
* {@inheritDoc}
*/
@Override
public void commit(boolean onSave) {
final UiElementNode manifestUiNode = getManifestUiNode(getEditor());
final UiElementNode usesSdkUiNode = getUsesSdkUiNode(getEditor());
getEditor().editXmlModel(new Runnable() {
public void run() {
if (manifestUiNode != null && manifestUiNode.isDirty()) {
for (UiAttributeNode ui_attr : manifestUiNode.getUiAttributes()) {
ui_attr.commit();
}
}
if (usesSdkUiNode != null && usesSdkUiNode.isDirty()) {
for (UiAttributeNode ui_attr : usesSdkUiNode.getUiAttributes()) {
ui_attr.commit();
}
if (!usesSdkUiNode.isDirty()) {
// Remove the <uses-sdk> XML element if it is empty.
// Rather than rely on the internal UI state, actually check that the
// XML element has no attributes and no child element so that we don't
// trash some user-generated content.
Node element = usesSdkUiNode.prepareCommit();
if (element != null &&
!element.hasAttributes() &&
!element.hasChildNodes()) {
// Important note: we MUST NOT use usesSdkUiNode.deleteXmlNode()
// here, as it would clear the UiAttribute list and thus break the
// link between the controls and the ui attribute field.
// Instead what we want is simply to remove the XML node and let the
// UiElementNode node know.
Node parent = element.getParentNode();
if (parent != null) {
parent.removeChild(element);
usesSdkUiNode.loadFromXmlNode(null /*xml_node*/);
}
}
}
}
}
});
// We need to call super's commit after we synchronized the nodes to make sure we
// reset the dirty flag after all the side effects from committing have occurred.
super.commit(onSave);
}
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.pages;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
import com.android.ide.eclipse.editors.ui.SectionHelper.ManifestSectionPart;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
/**
* Links section part for overview page.
*/
final class OverviewLinksPart extends ManifestSectionPart {
public OverviewLinksPart(Composite body, FormToolkit toolkit, ManifestEditor editor) {
super(body, toolkit, Section.TWISTIE | Section.EXPANDED, true /* description */);
Section section = getSection();
section.setText("Links");
section.setDescription("The content of the Android Manifest is made up of three sections. You can also edit the XML directly.");
Composite table = createTableLayout(toolkit, 2 /* numColumns */);
StringBuffer buf = new StringBuffer();
buf.append(String.format("<form><li style=\"image\" value=\"app_img\"><a href=\"page:%1$s\">", // $NON-NLS-1$
ApplicationPage.PAGE_ID));
buf.append("Application");
buf.append("</a>"); //$NON-NLS-1$
buf.append(": Activities, intent filters, providers, services and receivers.");
buf.append("</li>"); //$NON-NLS-1$
buf.append(String.format("<li style=\"image\" value=\"perm_img\"><a href=\"page:%1$s\">", // $NON-NLS-1$
PermissionPage.PAGE_ID));
buf.append("Permission");
buf.append("</a>"); //$NON-NLS-1$
buf.append(": Permissions defined and permissions used.");
buf.append("</li>"); //$NON-NLS-1$
buf.append(String.format("<li style=\"image\" value=\"inst_img\"><a href=\"page:%1$s\">", // $NON-NLS-1$
InstrumentationPage.PAGE_ID));
buf.append("Instrumentation");
buf.append("</a>"); //$NON-NLS-1$
buf.append(": Instrumentation defined.");
buf.append("</li>"); //$NON-NLS-1$
buf.append(String.format("<li style=\"image\" value=\"android_img\"><a href=\"page:%1$s\">", // $NON-NLS-1$
ManifestEditor.TEXT_EDITOR_ID));
buf.append("XML Source");
buf.append("</a>"); //$NON-NLS-1$
buf.append(": Directly edit the AndroidManifest.xml file.");
buf.append("</li>"); //$NON-NLS-1$
buf.append("<li style=\"image\" value=\"android_img\">"); // $NON-NLS-1$
buf.append("<a href=\"http://code.google.com/android/devel/bblocks-manifest.html\">Documentation</a>: Documentation from the Android SDK for AndroidManifest.xml."); // $NON-NLS-1$
buf.append("</li>"); //$NON-NLS-1$
buf.append("</form>"); //$NON-NLS-1$
FormText text = createFormText(table, toolkit, true, buf.toString(),
false /* setupLayoutData */);
text.setImage("android_img", EditorsPlugin.getAndroidLogo());
text.setImage("app_img", getIcon(AndroidManifestDescriptors.APPLICATION_ELEMENT));
text.setImage("perm_img", getIcon(AndroidManifestDescriptors.PERMISSION_ELEMENT));
text.setImage("inst_img", getIcon(AndroidManifestDescriptors.INTRUMENTATION_ELEMENT));
text.addHyperlinkListener(editor.createHyperlinkListener());
}
private Image getIcon(ElementDescriptor desc) {
if (desc != null && desc.getIcon() != null) {
return desc.getIcon();
}
return EditorsPlugin.getAndroidLogo();
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.pages;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormPage;
import org.eclipse.ui.forms.widgets.ColumnLayout;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
/**
* Page for overview settings, part of the AndroidManifest form editor.
* <p/>
* Useful reference:
* <a href="http://www.eclipse.org/articles/Article-Forms/article.html">
* http://www.eclipse.org/articles/Article-Forms/article.html</a>
*/
public final class OverviewPage extends FormPage {
/** Page id used for switching tabs programmatically */
final static String PAGE_ID = "overview_page"; //$NON-NLS-1$
/** Container editor */
ManifestEditor mEditor;
/** Overview part (attributes for manifest) */
private OverviewInfoPart mOverviewPart;
public OverviewPage(ManifestEditor editor) {
super(editor, PAGE_ID, "Overview"); // tab's label, user visible, keep it short
mEditor = editor;
}
/**
* Creates the content in the form hosted in this page.
*
* @param managedForm the form hosted in this page.
*/
@Override
protected void createFormContent(IManagedForm managedForm) {
super.createFormContent(managedForm);
ScrolledForm form = managedForm.getForm();
form.setText("Android Manifest Overview");
form.setImage(EditorsPlugin.getAndroidLogo());
Composite body = form.getBody();
FormToolkit toolkit = managedForm.getToolkit();
ColumnLayout cl = new ColumnLayout();
cl.minNumColumns = cl.maxNumColumns = 1;
body.setLayout(cl);
mOverviewPart = new OverviewInfoPart(body, toolkit, mEditor);
managedForm.addPart(mOverviewPart);
managedForm.addPart(new OverviewExportPart(this, body, toolkit, mEditor));
managedForm.addPart(new OverviewLinksPart(body, toolkit, mEditor));
}
/**
* Changes and refreshes the Application UI node handle by the sub parts.
*/
public void refreshUiApplicationNode() {
if (mOverviewPart != null) {
mOverviewPart.onSdkChanged();
}
}
}

View File

@@ -1,74 +0,0 @@
/*
* Copyright (C) 2007 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.editors.manifest.pages;
import com.android.ide.eclipse.editors.EditorsPlugin;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
import com.android.ide.eclipse.editors.ui.tree.UiTreeBlock;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormPage;
import org.eclipse.ui.forms.widgets.ScrolledForm;
/**
* Page for permissions settings, part of the AndroidManifest form editor.
* <p/>
* Useful reference:
* <a href="http://www.eclipse.org/articles/Article-Forms/article.html">
* http://www.eclipse.org/articles/Article-Forms/article.html</a>
*/
public final class PermissionPage extends FormPage {
/** Page id used for switching tabs programmatically */
public final static String PAGE_ID = "permission_page"; //$NON-NLS-1$
/** Container editor */
ManifestEditor mEditor;
public PermissionPage(ManifestEditor editor) {
super(editor, PAGE_ID, "Permissions"); // tab label, keep it short
mEditor = editor;
}
/**
* Creates the content in the form hosted in this page.
*
* @param managedForm the form hosted in this page.
*/
@Override
protected void createFormContent(IManagedForm managedForm) {
super.createFormContent(managedForm);
ScrolledForm form = managedForm.getForm();
form.setText("Android Manifest Permissions");
form.setImage(EditorsPlugin.getAndroidLogo());
UiElementNode manifest = mEditor.getUiRootNode();
UiTreeBlock block = new UiTreeBlock(mEditor, manifest,
true /* autoCreateRoot */,
new ElementDescriptor[] {
AndroidManifestDescriptors.PERMISSION_ELEMENT,
AndroidManifestDescriptors.USES_PERMISSION_ELEMENT,
AndroidManifestDescriptors.PERMISSION_GROUP_ELEMENT,
AndroidManifestDescriptors.PERMISSION_TREE_ELEMENT
},
"Permissions",
"List of permissions defined and used by the manifest");
block.createContent(managedForm);
}
}

Some files were not shown because too many files have changed in this diff Show More