diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values')
8 files changed, 1067 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesContentAssist.java new file mode 100644 index 000000000..d0ee92ca1 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesContentAssist.java @@ -0,0 +1,242 @@ +/* + * 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.adt.internal.editors.values; + +import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX; +import static com.android.SdkConstants.ANDROID_PREFIX; +import static com.android.SdkConstants.ATTR_NAME; +import static com.android.SdkConstants.ATTR_TYPE; +import static com.android.SdkConstants.PREFIX_RESOURCE_REF; +import static com.android.SdkConstants.TAG_ITEM; +import static com.android.SdkConstants.TAG_STYLE; +import static com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor.ATTRIBUTE_ICON_FILENAME; +import static com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.DESCRIPTOR_LAYOUT; + +import com.android.annotations.VisibleForTesting; +import com.android.ide.eclipse.adt.internal.editors.AndroidContentAssist; +import com.android.ide.eclipse.adt.internal.editors.IconFactory; +import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider; +import com.android.ide.eclipse.adt.internal.editors.descriptors.SeparatorAttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiResourceAttributeNode; +import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; + +import org.eclipse.jface.text.contentassist.CompletionProposal; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Content Assist Processor for /res/values and /res/drawable XML files + * <p> + * Further enhancements: + * <ul> + * <li> Complete prefixes in the style element itself for the name attribute + * <li> Complete parent names + * </ul> + */ +@VisibleForTesting +public class ValuesContentAssist extends AndroidContentAssist { + + /** + * Constructor for ResourcesContentAssist + */ + public ValuesContentAssist() { + super(AndroidTargetData.DESCRIPTOR_RESOURCES); + } + + @Override + protected boolean computeAttributeValues(List<ICompletionProposal> proposals, int offset, + String parentTagName, String attributeName, Node node, String wordPrefix, + boolean skipEndTag, int replaceLength) { + super.computeAttributeValues(proposals, offset, parentTagName, attributeName, node, + wordPrefix, skipEndTag, replaceLength); + + if (parentTagName.equals(TAG_ITEM) && ATTR_NAME.equals(attributeName)) { + + // Special case: the user is code completing inside + // <style><item name="^"/></style> + // In this case, ALL attributes are valid so we need to synthesize + // a choice list from all the layout descriptors + + // Add in android: as a completion item? + if (startsWith(ANDROID_NS_NAME_PREFIX, wordPrefix)) { + proposals.add(new CompletionProposal(ANDROID_NS_NAME_PREFIX, + offset - wordPrefix.length(), // replacementOffset + wordPrefix.length() + replaceLength, // replacementLength + ANDROID_NS_NAME_PREFIX.length(), // cursorPosition + IconFactory.getInstance().getIcon(ATTRIBUTE_ICON_FILENAME), + null, null, null)); + } + + + String attributePrefix = wordPrefix; + if (startsWith(attributePrefix, ANDROID_NS_NAME_PREFIX)) { + attributePrefix = attributePrefix.substring(ANDROID_NS_NAME_PREFIX.length()); + } + + AndroidTargetData data = mEditor.getTargetData(); + if (data != null) { + IDescriptorProvider descriptorProvider = + data.getDescriptorProvider( + AndroidTargetData.DESCRIPTOR_LAYOUT); + if (descriptorProvider != null) { + ElementDescriptor[] rootElementDescriptors = + descriptorProvider.getRootElementDescriptors(); + Map<String, AttributeDescriptor> matches = + new HashMap<String, AttributeDescriptor>(180); + for (ElementDescriptor elementDesc : rootElementDescriptors) { + for (AttributeDescriptor desc : elementDesc.getAttributes()) { + if (desc instanceof SeparatorAttributeDescriptor) { + continue; + } + String name = desc.getXmlLocalName(); + if (startsWith(name, attributePrefix)) { + matches.put(name, desc); + } + } + } + + List<AttributeDescriptor> sorted = + new ArrayList<AttributeDescriptor>(matches.size()); + sorted.addAll(matches.values()); + Collections.sort(sorted); + char needTag = 0; + addMatchingProposals(proposals, sorted.toArray(), offset, node, wordPrefix, + needTag, true /* isAttribute */, false /* isNew */, + skipEndTag /* skipEndTag */, replaceLength); + return true; + } + } + } + + return false; + } + + @Override + protected void computeTextValues(List<ICompletionProposal> proposals, int offset, + Node parentNode, Node currentNode, UiElementNode uiParent, + String prefix) { + super.computeTextValues(proposals, offset, parentNode, currentNode, uiParent, + prefix); + + if (parentNode.getNodeName().equals(TAG_ITEM) && + parentNode.getParentNode() != null && + TAG_STYLE.equals(parentNode.getParentNode().getNodeName())) { + + // Special case: the user is code completing inside + // <style><item name="android:foo"/>|</style> + // In this case, we need to find the right AttributeDescriptor + // for the given attribute and offer its values + + AndroidTargetData data = mEditor.getTargetData(); + if (data != null) { + IDescriptorProvider descriptorProvider = + data.getDescriptorProvider(DESCRIPTOR_LAYOUT); + if (descriptorProvider != null) { + + Element element = (Element) parentNode; + String attrName = element.getAttribute(ATTR_NAME); + int pos = attrName.indexOf(':'); + if (pos >= 0) { + attrName = attrName.substring(pos + 1); + } + + // Search for an attribute match + ElementDescriptor[] rootElementDescriptors = + descriptorProvider.getRootElementDescriptors(); + for (ElementDescriptor elementDesc : rootElementDescriptors) { + for (AttributeDescriptor desc : elementDesc.getAttributes()) { + if (desc.getXmlLocalName().equals(attrName)) { + // Make a ui parent node such that we can attach our + // newfound attribute node to something (the code we delegate + // to for looking up attribute completions will look at the + // parent node and ask for its editor etc.) + if (uiParent == null) { + DocumentDescriptor documentDescriptor = + data.getLayoutDescriptors().getDescriptor(); + uiParent = documentDescriptor.createUiNode(); + uiParent.setEditor(mEditor); + } + + UiAttributeNode currAttrNode = desc.createUiNode(uiParent); + AttribInfo attrInfo = new AttribInfo(); + Object[] values = getAttributeValueChoices(currAttrNode, attrInfo, + prefix); + char needTag = attrInfo.needTag; + if (attrInfo.correctedPrefix != null) { + prefix = attrInfo.correctedPrefix; + } + boolean isAttribute = true; + boolean isNew = false; + int replaceLength = computeTextReplaceLength(currentNode, offset); + addMatchingProposals(proposals, values, offset, currentNode, + prefix, needTag, isAttribute, isNew, + false /* skipEndTag */, replaceLength); + return; + } + } + } + } + } + } + + if (parentNode.getNodeName().equals(TAG_ITEM)) { + // Completing text content inside an <item> tag: offer @resource completion. + if (prefix.startsWith(PREFIX_RESOURCE_REF) || prefix.trim().length() == 0) { + String[] choices = UiResourceAttributeNode.computeResourceStringMatches( + mEditor, null /*attributeDescriptor*/, prefix); + if (choices == null || choices.length == 0) { + return; + } + + // If the parent item tag specifies a type, filter the results + Node typeNode = parentNode.getAttributes().getNamedItem(ATTR_TYPE); + if (typeNode != null) { + String value = typeNode.getNodeValue(); + List<String> filtered = new ArrayList<String>(); + for (String s : choices) { + if (s.startsWith(ANDROID_PREFIX) || + s.startsWith(PREFIX_RESOURCE_REF+ value)) { + filtered.add(s); + } + } + if (filtered.size() > 0) { + choices = filtered.toArray(new String[filtered.size()]); + } + } + + int replaceLength = computeTextReplaceLength(currentNode, offset); + addMatchingProposals(proposals, choices, offset, currentNode, + prefix, (char) 0 /*needTag*/, true /* isAttribute */, false /*isNew*/, + false /* skipEndTag*/, + replaceLength); + + } + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesEditorDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesEditorDelegate.java new file mode 100644 index 000000000..10f105f85 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesEditorDelegate.java @@ -0,0 +1,144 @@ +/* + * 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.adt.internal.editors.values; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +import com.android.ide.eclipse.adt.AdtConstants; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlDelegate; +import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.values.descriptors.ValuesDescriptors; +import com.android.resources.ResourceFolderType; +import com.android.xml.AndroidXPathFactory; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.ui.PartInitException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; + +/** + * Multi-page form editor for /res/values XML files. + */ +public class ValuesEditorDelegate extends CommonXmlDelegate { + + public static class Creator implements IDelegateCreator { + @Override + @SuppressWarnings("unchecked") + public ValuesEditorDelegate createForFile( + @NonNull CommonXmlEditor delegator, + @Nullable ResourceFolderType type) { + if (ResourceFolderType.VALUES == type) { + return new ValuesEditorDelegate(delegator); + } + + return null; + } + } + + /** + * Old standalone-editor ID. + * Use {@link CommonXmlEditor#ID} instead. + */ + public static final String LEGACY_EDITOR_ID = + AdtConstants.EDITORS_NAMESPACE + ".resources.ResourcesEditor"; //$NON-NLS-1$ + + + /** + * Creates the form editor for resources XML files. + */ + private ValuesEditorDelegate(CommonXmlEditor editor) { + super(editor, new ValuesContentAssist()); + editor.addDefaultTargetListener(); + } + + // ---- Base Class Overrides ---- + + /** + * Create the various form pages. + */ + @Override + public void delegateCreateFormPages() { + try { + getEditor().addPage(new ValuesTreePage(getEditor())); + } catch (PartInitException e) { + AdtPlugin.log(IStatus.ERROR, "Error creating nested page"); //$NON-NLS-1$ + AdtPlugin.getDefault().getLog().log(e.getStatus()); + } + } + + /** + * 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 + public void delegateXmlModelChanged(Document xml_doc) { + // init the ui root on demand + delegateInitUiRootNode(false /*force*/); + + getUiRootNode().setXmlDocument(xml_doc); + if (xml_doc != null) { + ElementDescriptor resources_desc = + ValuesDescriptors.getInstance().getElementDescriptor(); + try { + XPath xpath = AndroidXPathFactory.newXPath(); + Node node = (Node) xpath.evaluate("/" + resources_desc.getXmlName(), //$NON-NLS-1$ + xml_doc, + XPathConstants.NODE); + // Node can be null _or_ it must be the element we searched for. + assert node == null || node.getNodeName().equals(resources_desc.getXmlName()); + + // Refresh the manifest UI node and all its descendants + getUiRootNode().loadFromXmlNode(node); + } catch (XPathExpressionException e) { + AdtPlugin.log(e, "XPath error when trying to find '%s' element in XML.", //$NON-NLS-1$ + resources_desc.getXmlName()); + } + } + } + + /** + * Creates the initial UI Root Node, including the known mandatory elements. + * @param force if true, a new UiRootNode is recreated even if it already exists. + */ + @Override + public void delegateInitUiRootNode(boolean force) { + // The manifest UI node is always created, even if there's no corresponding XML node. + if (getUiRootNode() == null || force) { + ElementDescriptor resources_desc = + ValuesDescriptors.getInstance().getElementDescriptor(); + setUiRootNode(resources_desc.createUiNode()); + getUiRootNode().setEditor(getEditor()); + + onDescriptorsChanged(); + } + } + + // ---- Local methods ---- + + private void onDescriptorsChanged() { + // nothing to be done, as the descriptor are static for now. + // FIXME Update when the descriptors are not static + } +} + diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesTreePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesTreePage.java new file mode 100644 index 000000000..224eb7301 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesTreePage.java @@ -0,0 +1,108 @@ +/* + * 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.adt.internal.editors.values; + +import com.android.ide.common.resources.ResourceFolder; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.editors.IPageImageProvider; +import com.android.ide.eclipse.adt.internal.editors.IconFactory; +import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; +import com.android.ide.eclipse.adt.internal.editors.layout.configuration.FlagManager; +import com.android.ide.eclipse.adt.internal.editors.ui.tree.UiTreeBlock; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; +import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.swt.graphics.Image; +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 instrumentation settings, part of the AndroidManifest form editor. + */ +public final class ValuesTreePage extends FormPage implements IPageImageProvider { + /** Page id used for switching tabs programmatically */ + public final static String PAGE_ID = "res_tree_page"; //$NON-NLS-1$ + + /** Container editor */ + CommonXmlEditor mEditor; + + public ValuesTreePage(CommonXmlEditor editor) { + super(editor, PAGE_ID, "Resources"); // tab's label, keep it short + mEditor = editor; + } + + @Override + public Image getPageImage() { + // See if we should use a flag icon if this is a language-specific configuration + IFile file = mEditor.getInputFile(); + if (file != null) { + IContainer parent = file.getParent(); + if (parent != null) { + Image flag = FlagManager.get().getFlagForFolderName(parent.getName()); + if (flag != null) { + return flag; + } + } + } + + return IconFactory.getInstance().getIcon("editor_page_design"); //$NON-NLS-1$ + } + + /** + * 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 Resources (%1$s)", configText)); + } else { + form.setText("Android Resources"); + } + + form.setImage(AdtPlugin.getAndroidLogo()); + + UiElementNode resources = mEditor.getUiRootNode(); + UiTreeBlock block = new UiTreeBlock(mEditor, resources, + true /* autoCreateRoot */, + null /* no element filters */, + "Resources Elements", + "List of all resources elements in this XML file."); + block.createContent(managedForm); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/descriptors/ColorValueDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/descriptors/ColorValueDescriptor.java new file mode 100644 index 000000000..012785020 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/descriptors/ColorValueDescriptor.java @@ -0,0 +1,41 @@ +/* + * 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.adt.internal.editors.values.descriptors; + +import com.android.ide.eclipse.adt.internal.editors.descriptors.TextValueDescriptor; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiResourceAttributeNode; +import com.android.ide.eclipse.adt.internal.editors.values.uimodel.UiColorValueNode; + +/** + * Describes a Color XML element value displayed by an {@link UiColorValueNode}. + */ +public final class ColorValueDescriptor extends TextValueDescriptor { + + public ColorValueDescriptor(String uiName, String tooltip) { + super(uiName, tooltip); + } + + /** + * @return A new {@link UiResourceAttributeNode} linked to this theme descriptor. + */ + @Override + public UiAttributeNode createUiNode(UiElementNode uiParent) { + return new UiColorValueNode(this, uiParent); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/descriptors/ItemElementDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/descriptors/ItemElementDescriptor.java new file mode 100644 index 000000000..58ed36e45 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/descriptors/ItemElementDescriptor.java @@ -0,0 +1,55 @@ +/* + * 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.adt.internal.editors.values.descriptors; + +import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; +import com.android.ide.eclipse.adt.internal.editors.values.uimodel.UiItemElementNode; + +/** + * {@link ItemElementDescriptor} is a special version of {@link ElementDescriptor} that + * uses a specialized {@link UiItemElementNode} for display. + */ +public class ItemElementDescriptor extends ElementDescriptor { + + /** + * Constructs a new {@link ItemElementDescriptor} 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 ItemElementDescriptor(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); + } + + @Override + public UiElementNode createUiNode() { + return new UiItemElementNode(this); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/descriptors/ValuesDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/descriptors/ValuesDescriptors.java new file mode 100644 index 000000000..724e01932 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/descriptors/ValuesDescriptors.java @@ -0,0 +1,337 @@ +/* + * 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.adt.internal.editors.values.descriptors; + +import static com.android.SdkConstants.ATTR_NAME; +import static com.android.SdkConstants.ATTR_TYPE; +import static com.android.SdkConstants.TAG_COLOR; +import static com.android.SdkConstants.TAG_DIMEN; +import static com.android.SdkConstants.TAG_DRAWABLE; +import static com.android.SdkConstants.TAG_INTEGER_ARRAY; +import static com.android.SdkConstants.TAG_ITEM; +import static com.android.SdkConstants.TAG_PLURALS; +import static com.android.SdkConstants.TAG_RESOURCES; +import static com.android.SdkConstants.TAG_STRING; +import static com.android.SdkConstants.TAG_STRING_ARRAY; +import static com.android.SdkConstants.TAG_STYLE; + +import com.android.ide.common.api.IAttributeInfo.Format; +import com.android.ide.common.resources.platform.AttributeInfo; +import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.EnumAttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.FlagAttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider; +import com.android.ide.eclipse.adt.internal.editors.descriptors.ListAttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.TextValueDescriptor; +import com.android.resources.ResourceType; + +import java.util.EnumSet; + + +/** + * Complete description of the structure for resources XML files (under res/values/) + */ +public final class ValuesDescriptors implements IDescriptorProvider { + private static final ValuesDescriptors sThis = new ValuesDescriptors(); + + /** The {@link ElementDescriptor} for the root Resources element. */ + public final ElementDescriptor mResourcesElement; + + public static ValuesDescriptors getInstance() { + return sThis; + } + + /* + * @see com.android.ide.eclipse.editors.descriptors.IDescriptorProvider#getRootElementDescriptors() + */ + @Override + public ElementDescriptor[] getRootElementDescriptors() { + return new ElementDescriptor[] { mResourcesElement }; + } + + @Override + public ElementDescriptor getDescriptor() { + return mResourcesElement; + } + + public ElementDescriptor getElementDescriptor() { + return mResourcesElement; + } + + private ValuesDescriptors() { + + // Common attributes used in many placed + + // Elements + + AttributeInfo nameAttrInfo = new AttributeInfo(ATTR_NAME, Format.STRING_SET); + + ElementDescriptor color_element = new ElementDescriptor( + TAG_COLOR, + "Color", + "A @color@ value specifies an RGB value with an alpha channel, which can be used in various places such as specifying a solid color for a Drawable or the color to use for text. It always begins with a # character and then is followed by the alpha-red-green-blue information in one of the following formats: #RGB, #ARGB, #RRGGBB or #AARRGGBB.", + "http://code.google.com/android/reference/available-resources.html#colorvals", //$NON-NLS-1$ + new AttributeDescriptor[] { + new TextAttributeDescriptor(ATTR_NAME, + null /* nsUri */, + nameAttrInfo), + new ColorValueDescriptor( + "Value*", + "A mandatory color value.") + .setTooltip("The mandatory name used in referring to this color.") + }, + null, // no child nodes + false /* not mandatory */); + + ElementDescriptor string_element = new ElementDescriptor( + TAG_STRING, + "String", + "@Strings@, with optional simple formatting, can be stored and retrieved as resources. You can add formatting to your string by using three standard HTML tags: b, i, and u. If you use an apostrophe or a quote in your string, you must either escape it or enclose the whole string in the other kind of enclosing quotes.", + "http://code.google.com/android/reference/available-resources.html#stringresources", //$NON-NLS-1$ + new AttributeDescriptor[] { + new TextAttributeDescriptor(ATTR_NAME, + null /* nsUri */, + nameAttrInfo) + .setTooltip("The mandatory name used in referring to this string."), + new TextValueDescriptor( + "Value*", + "A mandatory string value.") + }, + null, // no child nodes + false /* not mandatory */); + + ElementDescriptor item_element = new ItemElementDescriptor( + TAG_ITEM, + "Item", + null, // TODO find javadoc + null, // TODO find link to javadoc + new AttributeDescriptor[] { + new TextAttributeDescriptor(ATTR_NAME, + null /* nsUri */, + nameAttrInfo) + .setTooltip("The mandatory name used in referring to this resource."), + new ListAttributeDescriptor(ATTR_TYPE, + null /* nsUri */, + new AttributeInfo(ATTR_TYPE, + EnumSet.of(Format.STRING, Format.ENUM) + ).setEnumValues(ResourceType.getNames()) + ).setTooltip("The mandatory type of this resource."), + new FlagAttributeDescriptor("format", //$NON-NLS-1$ + null /* nsUri */, + new AttributeInfo("format", + EnumSet.of(Format.STRING, Format.FLAG) + ).setFlagValues( + new String[] { + "boolean", //$NON-NLS-1$ + TAG_COLOR, + "dimension", //$NON-NLS-1$ + "float", //$NON-NLS-1$ + "fraction", //$NON-NLS-1$ + "integer", //$NON-NLS-1$ + "reference", //$NON-NLS-1$ + "string" //$NON-NLS-1$ + } ) + ).setTooltip("The optional format of this resource."), + new TextValueDescriptor( + "Value", + "A standard string, hex color value, or reference to any other resource type.") + }, + null, // no child nodes + false /* not mandatory */); + + ElementDescriptor drawable_element = new ElementDescriptor( + TAG_DRAWABLE, + "Drawable", + "A @drawable@ defines a rectangle of color. Android accepts color values written in various web-style formats -- a hexadecimal constant in any of the following forms: #RGB, #ARGB, #RRGGBB, #AARRGGBB. Zero in the alpha channel means transparent. The default value is opaque.", + "http://code.google.com/android/reference/available-resources.html#colordrawableresources", //$NON-NLS-1$ + new AttributeDescriptor[] { + new TextAttributeDescriptor(ATTR_NAME, + null /* nsUri */, + nameAttrInfo) + .setTooltip("The mandatory name used in referring to this drawable."), + new TextValueDescriptor( + "Value*", + "A mandatory color value in the form #RGB, #ARGB, #RRGGBB or #AARRGGBB.") + }, + null, // no child nodes + false /* not mandatory */); + + ElementDescriptor dimen_element = new ElementDescriptor( + TAG_DIMEN, + "Dimension", + "You can create common dimensions to use for various screen elements by defining @dimension@ values in XML. A dimension resource is a number followed by a unit of measurement. Supported units are px (pixels), in (inches), mm (millimeters), pt (points at 72 DPI), dp (density-independent pixels) and sp (scale-independent pixels)", + "http://code.google.com/android/reference/available-resources.html#dimension", //$NON-NLS-1$ + new AttributeDescriptor[] { + new TextAttributeDescriptor(ATTR_NAME, + null /* nsUri */, + nameAttrInfo) + .setTooltip("The mandatory name used in referring to this dimension."), + new TextValueDescriptor( + "Value*", + "A mandatory dimension value is a number followed by a unit of measurement. For example: 10px, 2in, 5sp.") + }, + null, // no child nodes + false /* not mandatory */); + + ElementDescriptor style_element = new ElementDescriptor( + TAG_STYLE, + "Style/Theme", + "Both @styles and themes@ are defined in a style block containing one or more string or numerical values (typically color values), or references to other resources (drawables and so on).", + "http://code.google.com/android/reference/available-resources.html#stylesandthemes", //$NON-NLS-1$ + new AttributeDescriptor[] { + new TextAttributeDescriptor(ATTR_NAME, + null /* nsUri */, + nameAttrInfo) + .setTooltip("The mandatory name used in referring to this theme."), + new TextAttributeDescriptor("parent", //$NON-NLS-1$ + null /* nsUri */, + new AttributeInfo("parent", //$NON-NLS-1$ + Format.STRING_SET)) + .setTooltip("An optional parent theme. All values from the specified theme will be inherited into this theme. Any values with identical names that you specify will override inherited values."), + }, + new ElementDescriptor[] { + new ElementDescriptor( + TAG_ITEM, + "Item", + "A value to use in this @theme@. It can be a standard string, a hex color value, or a reference to any other resource type.", + "http://code.google.com/android/reference/available-resources.html#stylesandthemes", //$NON-NLS-1$ + new AttributeDescriptor[] { + new TextAttributeDescriptor(ATTR_NAME, + null /* nsUri */, + nameAttrInfo) + .setTooltip("The mandatory name used in referring to this item."), + new TextValueDescriptor( + "Value*", + "A mandatory standard string, hex color value, or reference to any other resource type.") + }, + null, // no child nodes + false /* not mandatory */) + }, + false /* not mandatory */); + + ElementDescriptor string_array_element = new ElementDescriptor( + TAG_STRING_ARRAY, + "String Array", + "An array of strings. Strings are added as underlying item elements to the array.", + null, // tooltips + new AttributeDescriptor[] { + new TextAttributeDescriptor(ATTR_NAME, + null /* nsUri */, + nameAttrInfo) + .setTooltip("The mandatory name used in referring to this string array."), + }, + new ElementDescriptor[] { + new ElementDescriptor( + TAG_ITEM, + "Item", + "A string value to use in this string array.", + null, // tooltip + new AttributeDescriptor[] { + new TextValueDescriptor( + "Value*", + "A mandatory string.") + }, + null, // no child nodes + false /* not mandatory */) + }, + false /* not mandatory */); + + ElementDescriptor plurals_element = new ElementDescriptor( + TAG_PLURALS, + "Quantity Strings (Plurals)", + "A quantity string", + null, // tooltips + new AttributeDescriptor[] { + new TextAttributeDescriptor(ATTR_NAME, + null /* nsUri */, + nameAttrInfo) + .setTooltip("A name for the pair of strings. This name will be used as the resource ID."), + }, + new ElementDescriptor[] { + new ElementDescriptor( + TAG_ITEM, + "Item", + "A plural or singular string", + null, // tooltip + new AttributeDescriptor[] { + new EnumAttributeDescriptor( + "quantity", "Quantity", null, + "A keyword value indicating when this string should be used", + new AttributeInfo("quantity", Format.ENUM_SET) + .setEnumValues(new String[] { + "zero", //$NON-NLS-1$ + "one", //$NON-NLS-1$ + "two", //$NON-NLS-1$ + "few", //$NON-NLS-1$ + "many", //$NON-NLS-1$ + "other" //$NON-NLS-1$ + })) + }, + null, // no child nodes + false /* not mandatory */) + }, + false /* not mandatory */); + + ElementDescriptor integer_array_element = new ElementDescriptor( + TAG_INTEGER_ARRAY, + "Integer Array", + "An array of integers. Integers are added as underlying item elements to the array.", + null, // tooltips + new AttributeDescriptor[] { + new TextAttributeDescriptor(ATTR_NAME, + null /* nsUri */, + nameAttrInfo) + .setTooltip("The mandatory name used in referring to this integer array.") + }, + new ElementDescriptor[] { + new ElementDescriptor( + TAG_ITEM, + "Item", + "An integer value to use in this integer array.", + null, // tooltip + new AttributeDescriptor[] { + new TextValueDescriptor( + "Value*", + "A mandatory integer.") + }, + null, // no child nodes + false /* not mandatory */) + }, + false /* not mandatory */); + + mResourcesElement = new ElementDescriptor( + TAG_RESOURCES, + "Resources", + null, + "http://code.google.com/android/reference/available-resources.html", //$NON-NLS-1$ + null, // no attributes + new ElementDescriptor[] { + string_element, + color_element, + dimen_element, + drawable_element, + style_element, + item_element, + string_array_element, + integer_array_element, + plurals_element, + }, + true /* mandatory */); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/uimodel/UiColorValueNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/uimodel/UiColorValueNode.java new file mode 100644 index 000000000..9c84b36f9 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/uimodel/UiColorValueNode.java @@ -0,0 +1,82 @@ +/* + * 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.adt.internal.editors.values.uimodel; + +import com.android.ide.eclipse.adt.internal.editors.descriptors.TextValueDescriptor; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiTextValueNode; + +import org.eclipse.jface.dialogs.IMessageProvider; +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.widgets.Text; + +import java.util.regex.Pattern; + +/** + * Displays and edits a color XML element value with a custom validator. + * <p/> + * See {@link UiAttributeNode} for more information. + */ +public class UiColorValueNode extends UiTextValueNode { + + /** Accepted RGBA formats are one of #RGB, #ARGB, #RRGGBB or #AARRGGBB. */ + private static final Pattern RGBA_REGEXP = Pattern.compile( + "#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})"); //$NON-NLS-1$ + + public UiColorValueNode(TextValueDescriptor attributeDescriptor, UiElementNode uiParent) { + super(attributeDescriptor, uiParent); + } + + /* (non-java doc) + * + * Add a modify listener that will check colors have the proper format, + * that is one of #RGB, #ARGB, #RRGGBB or #AARRGGBB. + */ + @Override + protected void onAddValidators(final Text text) { + ModifyListener listener = new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + String color = text.getText(); + if (RGBA_REGEXP.matcher(color).matches()) { + getManagedForm().getMessageManager().removeMessage(text, text); + } else { + getManagedForm().getMessageManager().addMessage(text, + "Accepted color formats are one of #RGB, #ARGB, #RRGGBB or #AARRGGBB.", + null /* data */, IMessageProvider.ERROR, text); + } + } + }; + + text.addModifyListener(listener); + + // Make sure the validator removes its message(s) when the widget is disposed + text.addDisposeListener(new DisposeListener() { + @Override + 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); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/uimodel/UiItemElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/uimodel/UiItemElementNode.java new file mode 100644 index 000000000..88ac3e141 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/uimodel/UiItemElementNode.java @@ -0,0 +1,58 @@ +/* + * 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.adt.internal.editors.values.uimodel; + +import com.android.SdkConstants; +import com.android.ide.eclipse.adt.AdtUtils; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; +import com.android.ide.eclipse.adt.internal.editors.values.descriptors.ItemElementDescriptor; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * {@link UiItemElementNode} is a special version of {@link UiElementNode} that + * customizes the element display to include the item type attribute if present. + */ +public class UiItemElementNode extends UiElementNode { + + /** + * Creates a new {@link UiElementNode} described by a given {@link ItemElementDescriptor}. + * + * @param elementDescriptor The {@link ItemElementDescriptor} for the XML node. Cannot be null. + */ + public UiItemElementNode(ItemElementDescriptor elementDescriptor) { + super(elementDescriptor); + } + + @Override + public String getShortDescription() { + Node xmlNode = getXmlNode(); + if (xmlNode != null && xmlNode instanceof Element && xmlNode.hasAttributes()) { + + Element elem = (Element) xmlNode; + String type = elem.getAttribute(SdkConstants.ATTR_TYPE); + String name = elem.getAttribute(SdkConstants.ATTR_NAME); + if (type != null && name != null && type.length() > 0 && name.length() > 0) { + type = AdtUtils.capitalize(type); + return String.format("%1$s (%2$s %3$s)", name, type, getDescriptor().getUiName()); + } + } + + return super.getShortDescription(); + } +} |