aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java996
1 files changed, 0 insertions, 996 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java
deleted file mode 100644
index 83ce9ef8f..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java
+++ /dev/null
@@ -1,996 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.common.layout;
-
-import static com.android.SdkConstants.ANDROID_URI;
-import static com.android.SdkConstants.ATTR_CLASS;
-import static com.android.SdkConstants.ATTR_HINT;
-import static com.android.SdkConstants.ATTR_ID;
-import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-import static com.android.SdkConstants.ATTR_STYLE;
-import static com.android.SdkConstants.ATTR_TEXT;
-import static com.android.SdkConstants.DOT_LAYOUT_PARAMS;
-import static com.android.SdkConstants.ID_PREFIX;
-import static com.android.SdkConstants.NEW_ID_PREFIX;
-import static com.android.SdkConstants.VALUE_FALSE;
-import static com.android.SdkConstants.VALUE_FILL_PARENT;
-import static com.android.SdkConstants.VALUE_MATCH_PARENT;
-import static com.android.SdkConstants.VALUE_TRUE;
-import static com.android.SdkConstants.VALUE_WRAP_CONTENT;
-import static com.android.SdkConstants.VIEW_FRAGMENT;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.AbstractViewRule;
-import com.android.ide.common.api.IAttributeInfo;
-import com.android.ide.common.api.IAttributeInfo.Format;
-import com.android.ide.common.api.IClientRulesEngine;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IMenuCallback;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewMetadata;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.RuleAction;
-import com.android.ide.common.api.RuleAction.ActionProvider;
-import com.android.ide.common.api.RuleAction.ChoiceProvider;
-import com.android.resources.ResourceType;
-import com.android.utils.Pair;
-
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-/**
- * Common IViewRule processing to all view and layout classes.
- */
-public class BaseViewRule extends AbstractViewRule {
- /** List of recently edited properties */
- private static List<String> sRecent = new LinkedList<String>();
-
- /** Maximum number of recent properties to track and list */
- private final static int MAX_RECENT_COUNT = 12;
-
- // Strings used as internal ids, group ids and prefixes for actions
- private static final String FALSE_ID = "false"; //$NON-NLS-1$
- private static final String TRUE_ID = "true"; //$NON-NLS-1$
- private static final String PROP_PREFIX = "@prop@"; //$NON-NLS-1$
- private static final String CLEAR_ID = "clear"; //$NON-NLS-1$
- private static final String ZCUSTOM = "zcustom"; //$NON-NLS-1$
-
- protected IClientRulesEngine mRulesEngine;
-
- // Cache of attributes. Key is FQCN of a node mixed with its view hierarchy
- // parent. Values are a custom map as needed by getContextMenu.
- private Map<String, Map<String, Prop>> mAttributesMap =
- new HashMap<String, Map<String, Prop>>();
-
- @Override
- public boolean onInitialize(@NonNull String fqcn, @NonNull IClientRulesEngine engine) {
- mRulesEngine = engine;
-
- // This base rule can handle any class so we don't need to filter on
- // FQCN. Derived classes should do so if they can handle some
- // subclasses.
-
- // If onInitialize returns false, it means it can't handle the given
- // FQCN and will be unloaded.
-
- return true;
- }
-
- /**
- * Returns the {@link IClientRulesEngine} associated with this {@link IViewRule}
- *
- * @return the {@link IClientRulesEngine} associated with this {@link IViewRule}
- */
- public IClientRulesEngine getRulesEngine() {
- return mRulesEngine;
- }
-
- // === Context Menu ===
-
- /**
- * Generate custom actions for the context menu: <br/>
- * - Explicit layout_width and layout_height attributes.
- * - List of all other simple toggle attributes.
- */
- @Override
- public void addContextMenuActions(@NonNull List<RuleAction> actions,
- final @NonNull INode selectedNode) {
- String width = null;
- String currentWidth = selectedNode.getStringAttr(ANDROID_URI, ATTR_LAYOUT_WIDTH);
-
- String fillParent = getFillParentValueName();
- boolean canMatchParent = supportsMatchParent();
- if (canMatchParent && VALUE_FILL_PARENT.equals(currentWidth)) {
- currentWidth = VALUE_MATCH_PARENT;
- } else if (!canMatchParent && VALUE_MATCH_PARENT.equals(currentWidth)) {
- currentWidth = VALUE_FILL_PARENT;
- } else if (!VALUE_WRAP_CONTENT.equals(currentWidth) && !fillParent.equals(currentWidth)) {
- width = currentWidth;
- }
-
- String height = null;
- String currentHeight = selectedNode.getStringAttr(ANDROID_URI, ATTR_LAYOUT_HEIGHT);
-
- if (canMatchParent && VALUE_FILL_PARENT.equals(currentHeight)) {
- currentHeight = VALUE_MATCH_PARENT;
- } else if (!canMatchParent && VALUE_MATCH_PARENT.equals(currentHeight)) {
- currentHeight = VALUE_FILL_PARENT;
- } else if (!VALUE_WRAP_CONTENT.equals(currentHeight)
- && !fillParent.equals(currentHeight)) {
- height = currentHeight;
- }
- final String newWidth = width;
- final String newHeight = height;
-
- final IMenuCallback onChange = new IMenuCallback() {
- @Override
- public void action(
- final @NonNull RuleAction action,
- final @NonNull List<? extends INode> selectedNodes,
- final @Nullable String valueId, final @Nullable Boolean newValue) {
- String fullActionId = action.getId();
- boolean isProp = fullActionId.startsWith(PROP_PREFIX);
- final String actionId = isProp ?
- fullActionId.substring(PROP_PREFIX.length()) : fullActionId;
-
- if (fullActionId.equals(ATTR_LAYOUT_WIDTH)) {
- final String newAttrValue = getValue(valueId, newWidth);
- if (newAttrValue != null) {
- for (INode node : selectedNodes) {
- node.editXml("Change Attribute " + ATTR_LAYOUT_WIDTH,
- new PropertySettingNodeHandler(ANDROID_URI,
- ATTR_LAYOUT_WIDTH, newAttrValue));
- }
- editedProperty(ATTR_LAYOUT_WIDTH);
- }
- return;
- } else if (fullActionId.equals(ATTR_LAYOUT_HEIGHT)) {
- // Ask the user
- final String newAttrValue = getValue(valueId, newHeight);
- if (newAttrValue != null) {
- for (INode node : selectedNodes) {
- node.editXml("Change Attribute " + ATTR_LAYOUT_HEIGHT,
- new PropertySettingNodeHandler(ANDROID_URI,
- ATTR_LAYOUT_HEIGHT, newAttrValue));
- }
- editedProperty(ATTR_LAYOUT_HEIGHT);
- }
- return;
- } else if (fullActionId.equals(ATTR_ID)) {
- // Ids must be set individually so open the id dialog for each
- // selected node (though allow cancel to break the loop)
- for (INode node : selectedNodes) {
- if (!mRulesEngine.rename(node)) {
- break;
- }
- }
- editedProperty(ATTR_ID);
- return;
- } else if (isProp) {
- INode firstNode = selectedNodes.get(0);
- String key = getPropertyMapKey(selectedNode);
- Map<String, Prop> props = mAttributesMap.get(key);
- final Prop prop = (props != null) ? props.get(actionId) : null;
-
- if (prop != null) {
- editedProperty(actionId);
-
- // For custom values (requiring an input dialog) input the
- // value outside the undo-block.
- // Input the value as a text, unless we know it's the "text" or
- // "style" attributes (where we know we want to ask for specific
- // resource types).
- String uri = ANDROID_URI;
- String v = null;
- if (prop.isStringEdit()) {
- boolean isStyle = actionId.equals(ATTR_STYLE);
- boolean isText = actionId.equals(ATTR_TEXT);
- boolean isHint = actionId.equals(ATTR_HINT);
- if (isStyle || isText || isHint) {
- String resourceTypeName = isStyle
- ? ResourceType.STYLE.getName()
- : ResourceType.STRING.getName();
- String oldValue = selectedNodes.size() == 1
- ? (isStyle ? firstNode.getStringAttr(ATTR_STYLE, actionId)
- : firstNode.getStringAttr(ANDROID_URI, actionId))
- : ""; //$NON-NLS-1$
- oldValue = ensureValidString(oldValue);
- v = mRulesEngine.displayResourceInput(resourceTypeName, oldValue);
- if (isStyle) {
- uri = null;
- }
- } else if (actionId.equals(ATTR_CLASS) && selectedNodes.size() >= 1 &&
- VIEW_FRAGMENT.equals(selectedNodes.get(0).getFqcn())) {
- v = mRulesEngine.displayFragmentSourceInput();
- uri = null;
- } else {
- v = inputAttributeValue(firstNode, actionId);
- }
- }
- final String customValue = v;
-
- for (INode n : selectedNodes) {
- if (prop.isToggle()) {
- // case of toggle
- String value = ""; //$NON-NLS-1$
- if (valueId.equals(TRUE_ID)) {
- value = newValue ? "true" : ""; //$NON-NLS-1$ //$NON-NLS-2$
- } else if (valueId.equals(FALSE_ID)) {
- value = newValue ? "false" : "";//$NON-NLS-1$ //$NON-NLS-2$
- }
- n.setAttribute(uri, actionId, value);
- } else if (prop.isFlag()) {
- // case of a flag
- String values = ""; //$NON-NLS-1$
- if (!valueId.equals(CLEAR_ID)) {
- values = n.getStringAttr(ANDROID_URI, actionId);
- Set<String> newValues = new HashSet<String>();
- if (values != null) {
- newValues.addAll(Arrays.asList(
- values.split("\\|"))); //$NON-NLS-1$
- }
- if (newValue) {
- newValues.add(valueId);
- } else {
- newValues.remove(valueId);
- }
-
- List<String> sorted = new ArrayList<String>(newValues);
- Collections.sort(sorted);
- values = join('|', sorted);
-
- // Special case
- if (valueId.equals("normal")) { //$NON-NLS-1$
- // For textStyle for example, if you have "bold|italic"
- // and you select the "normal" property, this should
- // not behave in the normal flag way and "or" itself in;
- // it should replace the other two.
- // This also applies to imeOptions.
- values = valueId;
- }
- }
- n.setAttribute(uri, actionId, values);
- } else if (prop.isEnum()) {
- // case of an enum
- String value = ""; //$NON-NLS-1$
- if (!valueId.equals(CLEAR_ID)) {
- value = newValue ? valueId : ""; //$NON-NLS-1$
- }
- n.setAttribute(uri, actionId, value);
- } else {
- assert prop.isStringEdit();
- // We've already received the value outside the undo block
- if (customValue != null) {
- n.setAttribute(uri, actionId, customValue);
- }
- }
- }
- }
- }
- }
-
- /**
- * Input the custom value for the given attribute. This will use the Reference
- * Chooser if it is a reference value, otherwise a plain text editor.
- */
- private String inputAttributeValue(final INode node, final String attribute) {
- String oldValue = node.getStringAttr(ANDROID_URI, attribute);
- oldValue = ensureValidString(oldValue);
- IAttributeInfo attributeInfo = node.getAttributeInfo(ANDROID_URI, attribute);
- if (attributeInfo != null
- && attributeInfo.getFormats().contains(Format.REFERENCE)) {
- return mRulesEngine.displayReferenceInput(oldValue);
- } else {
- // A single resource type? If so use a resource chooser initialized
- // to this specific type
- /* This does not work well, because the metadata is a bit misleading:
- * for example a Button's "text" property and a Button's "onClick" property
- * both claim to be of type [string], but @string/ is NOT valid for
- * onClick..
- if (attributeInfo != null && attributeInfo.getFormats().length == 1) {
- // Resource chooser
- Format format = attributeInfo.getFormats()[0];
- return mRulesEngine.displayResourceInput(format.name(), oldValue);
- }
- */
-
- // Fallback: just edit the raw XML string
- String message = String.format("New %1$s Value:", attribute);
- return mRulesEngine.displayInput(message, oldValue, null);
- }
- }
-
- /**
- * Returns the value (which will ask the user if the value is the special
- * {@link #ZCUSTOM} marker
- */
- private String getValue(String valueId, String defaultValue) {
- if (valueId.equals(ZCUSTOM)) {
- if (defaultValue == null) {
- defaultValue = "";
- }
- String value = mRulesEngine.displayInput(
- "Set custom layout attribute value (example: 50dp)",
- defaultValue, null);
- if (value != null && value.trim().length() > 0) {
- return value.trim();
- } else {
- return null;
- }
- }
-
- return valueId;
- }
- };
-
- IAttributeInfo textAttribute = selectedNode.getAttributeInfo(ANDROID_URI, ATTR_TEXT);
- if (textAttribute != null) {
- actions.add(RuleAction.createAction(PROP_PREFIX + ATTR_TEXT, "Edit Text...", onChange,
- null, 10, true));
- }
-
- String editIdLabel = selectedNode.getStringAttr(ANDROID_URI, ATTR_ID) != null ?
- "Edit ID..." : "Assign ID...";
- actions.add(RuleAction.createAction(ATTR_ID, editIdLabel, onChange, null, 20, true));
-
- addCommonPropertyActions(actions, selectedNode, onChange, 21);
-
- // Create width choice submenu
- actions.add(RuleAction.createSeparator(32));
- List<Pair<String, String>> widthChoices = new ArrayList<Pair<String,String>>(4);
- widthChoices.add(Pair.of(VALUE_WRAP_CONTENT, "Wrap Content"));
- if (canMatchParent) {
- widthChoices.add(Pair.of(VALUE_MATCH_PARENT, "Match Parent"));
- } else {
- widthChoices.add(Pair.of(VALUE_FILL_PARENT, "Fill Parent"));
- }
- if (width != null) {
- widthChoices.add(Pair.of(width, width));
- }
- widthChoices.add(Pair.of(ZCUSTOM, "Other..."));
- actions.add(RuleAction.createChoices(
- ATTR_LAYOUT_WIDTH, "Layout Width",
- onChange,
- null /* iconUrls */,
- currentWidth,
- null, 35,
- true, // supportsMultipleNodes
- widthChoices));
-
- // Create height choice submenu
- List<Pair<String, String>> heightChoices = new ArrayList<Pair<String,String>>(4);
- heightChoices.add(Pair.of(VALUE_WRAP_CONTENT, "Wrap Content"));
- if (canMatchParent) {
- heightChoices.add(Pair.of(VALUE_MATCH_PARENT, "Match Parent"));
- } else {
- heightChoices.add(Pair.of(VALUE_FILL_PARENT, "Fill Parent"));
- }
- if (height != null) {
- heightChoices.add(Pair.of(height, height));
- }
- heightChoices.add(Pair.of(ZCUSTOM, "Other..."));
- actions.add(RuleAction.createChoices(
- ATTR_LAYOUT_HEIGHT, "Layout Height",
- onChange,
- null /* iconUrls */,
- currentHeight,
- null, 40,
- true,
- heightChoices));
-
- actions.add(RuleAction.createSeparator(45));
- RuleAction properties = RuleAction.createChoices("properties", "Other Properties", //$NON-NLS-1$
- onChange /*callback*/, null /*icon*/, 50,
- true /*supportsMultipleNodes*/, new ActionProvider() {
- @Override
- public @NonNull List<RuleAction> getNestedActions(@NonNull INode node) {
- List<RuleAction> propertyActionTypes = new ArrayList<RuleAction>();
- propertyActionTypes.add(RuleAction.createChoices(
- "recent", "Recent", //$NON-NLS-1$
- onChange /*callback*/, null /*icon*/, 10,
- true /*supportsMultipleNodes*/, new ActionProvider() {
- @Override
- public @NonNull List<RuleAction> getNestedActions(@NonNull INode n) {
- List<RuleAction> propertyActions = new ArrayList<RuleAction>();
- addRecentPropertyActions(propertyActions, n, onChange);
- return propertyActions;
- }
- }));
-
- propertyActionTypes.add(RuleAction.createSeparator(20));
-
- addInheritedProperties(propertyActionTypes, node, onChange, 30);
-
- propertyActionTypes.add(RuleAction.createSeparator(50));
- propertyActionTypes.add(RuleAction.createChoices(
- "layoutparams", "Layout Parameters", //$NON-NLS-1$
- onChange /*callback*/, null /*icon*/, 60,
- true /*supportsMultipleNodes*/, new ActionProvider() {
- @Override
- public @NonNull List<RuleAction> getNestedActions(@NonNull INode n) {
- List<RuleAction> propertyActions = new ArrayList<RuleAction>();
- addPropertyActions(propertyActions, n, onChange, null, true);
- return propertyActions;
- }
- }));
-
- propertyActionTypes.add(RuleAction.createSeparator(70));
-
- propertyActionTypes.add(RuleAction.createChoices(
- "allprops", "All By Name", //$NON-NLS-1$
- onChange /*callback*/, null /*icon*/, 80,
- true /*supportsMultipleNodes*/, new ActionProvider() {
- @Override
- public @NonNull List<RuleAction> getNestedActions(@NonNull INode n) {
- List<RuleAction> propertyActions = new ArrayList<RuleAction>();
- addPropertyActions(propertyActions, n, onChange, null, false);
- return propertyActions;
- }
- }));
-
- return propertyActionTypes;
- }
- });
-
- actions.add(properties);
- }
-
- @Override
- @Nullable
- public String getDefaultActionId(@NonNull final INode selectedNode) {
- IAttributeInfo textAttribute = selectedNode.getAttributeInfo(ANDROID_URI, ATTR_TEXT);
- if (textAttribute != null) {
- return PROP_PREFIX + ATTR_TEXT;
- }
-
- return null;
- }
-
- private static String getPropertyMapKey(INode node) {
- // Compute the key for mAttributesMap. This depends on the type of this
- // node and its parent in the view hierarchy.
- StringBuilder sb = new StringBuilder();
- sb.append(node.getFqcn());
- sb.append('_');
- INode parent = node.getParent();
- if (parent != null) {
- sb.append(parent.getFqcn());
- }
- return sb.toString();
- }
-
- /**
- * Adds menu items for the inherited attributes, one pull-right menu for each super class
- * that defines attributes.
- *
- * @param propertyActionTypes the actions list to add into
- * @param node the node to apply the attributes to
- * @param onChange the callback to use for setting attributes
- * @param sortPriority the initial sort attribute for the first menu item
- */
- private void addInheritedProperties(List<RuleAction> propertyActionTypes, INode node,
- final IMenuCallback onChange, int sortPriority) {
- List<String> attributeSources = node.getAttributeSources();
- for (final String definedBy : attributeSources) {
- String sourceClass = definedBy;
-
- // Strip package prefixes when necessary
- int index = sourceClass.length();
- if (sourceClass.endsWith(DOT_LAYOUT_PARAMS)) {
- index = sourceClass.length() - DOT_LAYOUT_PARAMS.length() - 1;
- }
- int lastDot = sourceClass.lastIndexOf('.', index);
- if (lastDot != -1) {
- sourceClass = sourceClass.substring(lastDot + 1);
- }
-
- String label;
- if (definedBy.equals(node.getFqcn())) {
- label = String.format("Defined by %1$s", sourceClass);
- } else {
- label = String.format("Inherited from %1$s", sourceClass);
- }
-
- propertyActionTypes.add(RuleAction.createChoices("def_" + definedBy,
- label,
- onChange /*callback*/, null /*icon*/, sortPriority++,
- true /*supportsMultipleNodes*/, new ActionProvider() {
- @Override
- public @NonNull List<RuleAction> getNestedActions(@NonNull INode n) {
- List<RuleAction> propertyActions = new ArrayList<RuleAction>();
- addPropertyActions(propertyActions, n, onChange, definedBy, false);
- return propertyActions;
- }
- }));
- }
- }
-
- /**
- * Creates a list of properties that are commonly edited for views of the
- * selected node's type
- */
- private void addCommonPropertyActions(List<RuleAction> actions, INode selectedNode,
- IMenuCallback onChange, int sortPriority) {
- Map<String, Prop> properties = getPropertyMetadata(selectedNode);
- IViewMetadata metadata = mRulesEngine.getMetadata(selectedNode.getFqcn());
- if (metadata != null) {
- List<String> attributes = metadata.getTopAttributes();
- if (attributes.size() > 0) {
- for (String attribute : attributes) {
- // Text and ID are handled manually in the menu construction code because
- // we want to place them consistently and customize the action label
- if (ATTR_TEXT.equals(attribute) || ATTR_ID.equals(attribute)) {
- continue;
- }
-
- Prop property = properties.get(attribute);
- if (property != null) {
- String title = property.getTitle();
- if (title.endsWith("...")) {
- title = String.format("Edit %1$s", property.getTitle());
- }
- actions.add(createPropertyAction(property, attribute, title,
- selectedNode, onChange, sortPriority));
- sortPriority++;
- }
- }
- }
- }
- }
-
- /**
- * Record that the given property was just edited; adds it to the front of
- * the recently edited property list
- *
- * @param property the name of the property
- */
- static void editedProperty(String property) {
- if (sRecent.contains(property)) {
- sRecent.remove(property);
- } else if (sRecent.size() > MAX_RECENT_COUNT) {
- sRecent.remove(sRecent.size() - 1);
- }
- sRecent.add(0, property);
- }
-
- /**
- * Creates a list of recently modified properties that apply to the given selected node
- */
- private void addRecentPropertyActions(List<RuleAction> actions, INode selectedNode,
- IMenuCallback onChange) {
- int sortPriority = 10;
- Map<String, Prop> properties = getPropertyMetadata(selectedNode);
- for (String attribute : sRecent) {
- Prop property = properties.get(attribute);
- if (property != null) {
- actions.add(createPropertyAction(property, attribute, property.getTitle(),
- selectedNode, onChange, sortPriority));
- sortPriority += 10;
- }
- }
- }
-
- /**
- * Creates a list of nested actions representing the property-setting
- * actions for the given selected node
- */
- private void addPropertyActions(List<RuleAction> actions, INode selectedNode,
- IMenuCallback onChange, String definedBy, boolean layoutParamsOnly) {
-
- Map<String, Prop> properties = getPropertyMetadata(selectedNode);
-
- int sortPriority = 10;
- for (Map.Entry<String, Prop> entry : properties.entrySet()) {
- String id = entry.getKey();
- Prop property = entry.getValue();
- if (layoutParamsOnly) {
- // If we have definedBy information, that is most accurate; all layout
- // params will be defined by a class whose name ends with
- // .LayoutParams:
- if (definedBy != null) {
- if (!definedBy.endsWith(DOT_LAYOUT_PARAMS)) {
- continue;
- }
- } else if (!id.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX)) {
- continue;
- }
- }
- if (definedBy != null && !definedBy.equals(property.getDefinedBy())) {
- continue;
- }
- actions.add(createPropertyAction(property, id, property.getTitle(),
- selectedNode, onChange, sortPriority));
- sortPriority += 10;
- }
-
- // The properties are coming out of map key order which isn't right, so sort
- // alphabetically instead
- Collections.sort(actions, new Comparator<RuleAction>() {
- @Override
- public int compare(RuleAction action1, RuleAction action2) {
- return action1.getTitle().compareTo(action2.getTitle());
- }
- });
- }
-
- private RuleAction createPropertyAction(Prop p, String id, String title, INode selectedNode,
- IMenuCallback onChange, int sortPriority) {
- if (p.isToggle()) {
- // Toggles are handled as a multiple-choice between true, false
- // and nothing (clear)
- String value = selectedNode.getStringAttr(ANDROID_URI, id);
- if (value != null) {
- value = value.toLowerCase(Locale.US);
- }
- if (VALUE_TRUE.equals(value)) {
- value = TRUE_ID;
- } else if (VALUE_FALSE.equals(value)) {
- value = FALSE_ID;
- } else {
- value = CLEAR_ID;
- }
- return RuleAction.createChoices(PROP_PREFIX + id, title,
- onChange, BOOLEAN_CHOICE_PROVIDER,
- value,
- null, sortPriority,
- true);
- } else if (p.getChoices() != null) {
- // Enum or flags. Their possible values are the multiple-choice
- // items, with an extra "clear" option to remove everything.
- String current = selectedNode.getStringAttr(ANDROID_URI, id);
- if (current == null || current.length() == 0) {
- current = CLEAR_ID;
- }
- return RuleAction.createChoices(PROP_PREFIX + id, title,
- onChange, new EnumPropertyChoiceProvider(p),
- current,
- null, sortPriority,
- true);
- } else {
- return RuleAction.createAction(
- PROP_PREFIX + id,
- title,
- onChange,
- null, sortPriority,
- true);
- }
- }
-
- private Map<String, Prop> getPropertyMetadata(final INode selectedNode) {
- String key = getPropertyMapKey(selectedNode);
- Map<String, Prop> props = mAttributesMap.get(key);
- if (props == null) {
- // Prepare the property map
- props = new HashMap<String, Prop>();
- for (IAttributeInfo attrInfo : selectedNode.getDeclaredAttributes()) {
- String id = attrInfo != null ? attrInfo.getName() : null;
- if (id == null || id.equals(ATTR_LAYOUT_WIDTH) || id.equals(ATTR_LAYOUT_HEIGHT)) {
- // Layout width/height are already handled at the root level
- continue;
- }
- if (attrInfo == null) {
- continue;
- }
- EnumSet<Format> formats = attrInfo.getFormats();
-
- String title = getAttributeDisplayName(id);
-
- String definedBy = attrInfo != null ? attrInfo.getDefinedBy() : null;
- if (formats.contains(IAttributeInfo.Format.BOOLEAN)) {
- props.put(id, new Prop(title, true, definedBy));
- } else if (formats.contains(IAttributeInfo.Format.ENUM)) {
- // Convert each enum into a map id=>title
- Map<String, String> values = new HashMap<String, String>();
- if (attrInfo != null) {
- for (String e : attrInfo.getEnumValues()) {
- values.put(e, getAttributeDisplayName(e));
- }
- }
-
- props.put(id, new Prop(title, false, false, values, definedBy));
- } else if (formats.contains(IAttributeInfo.Format.FLAG)) {
- // Convert each flag into a map id=>title
- Map<String, String> values = new HashMap<String, String>();
- if (attrInfo != null) {
- for (String e : attrInfo.getFlagValues()) {
- values.put(e, getAttributeDisplayName(e));
- }
- }
-
- props.put(id, new Prop(title, false, true, values, definedBy));
- } else {
- props.put(id, new Prop(title + "...", false, definedBy));
- }
- }
- mAttributesMap.put(key, props);
- }
- return props;
- }
-
- /**
- * A {@link ChoiceProvder} which provides alternatives suitable for choosing
- * values for a boolean property: true, false, or "default".
- */
- private static ChoiceProvider BOOLEAN_CHOICE_PROVIDER = new ChoiceProvider() {
- @Override
- public void addChoices(@NonNull List<String> titles, @NonNull List<URL> iconUrls,
- @NonNull List<String> ids) {
- titles.add("True");
- ids.add(TRUE_ID);
-
- titles.add("False");
- ids.add(FALSE_ID);
-
- titles.add(RuleAction.SEPARATOR);
- ids.add(RuleAction.SEPARATOR);
-
- titles.add("Default");
- ids.add(CLEAR_ID);
- }
- };
-
- /**
- * A {@link ChoiceProvider} which provides the various available
- * attribute values available for a given {@link Prop} property descriptor.
- */
- private static class EnumPropertyChoiceProvider implements ChoiceProvider {
- private Prop mProperty;
-
- public EnumPropertyChoiceProvider(Prop property) {
- super();
- mProperty = property;
- }
-
- @Override
- public void addChoices(@NonNull List<String> titles, @NonNull List<URL> iconUrls,
- @NonNull List<String> ids) {
- for (Entry<String, String> entry : mProperty.getChoices().entrySet()) {
- ids.add(entry.getKey());
- titles.add(entry.getValue());
- }
-
- titles.add(RuleAction.SEPARATOR);
- ids.add(RuleAction.SEPARATOR);
-
- titles.add("Default");
- ids.add(CLEAR_ID);
- }
- }
-
- /**
- * Returns true if the given node is "filled" (e.g. has layout width set to match
- * parent or fill parent
- */
- protected final boolean isFilled(INode node, String attribute) {
- String value = node.getStringAttr(ANDROID_URI, attribute);
- return VALUE_MATCH_PARENT.equals(value) || VALUE_FILL_PARENT.equals(value);
- }
-
- /**
- * Returns fill_parent or match_parent, depending on whether the minimum supported
- * platform supports match_parent or not
- *
- * @return match_parent or fill_parent depending on which is supported by the project
- */
- protected final String getFillParentValueName() {
- return supportsMatchParent() ? VALUE_MATCH_PARENT : VALUE_FILL_PARENT;
- }
-
- /**
- * Returns true if the project supports match_parent instead of just fill_parent
- *
- * @return true if the project supports match_parent instead of just fill_parent
- */
- protected final boolean supportsMatchParent() {
- // fill_parent was renamed match_parent in API level 8
- return mRulesEngine.getMinApiLevel() >= 8;
- }
-
- /** Join strings into a single string with the given delimiter */
- static String join(char delimiter, Collection<String> strings) {
- StringBuilder sb = new StringBuilder(100);
- for (String s : strings) {
- if (sb.length() > 0) {
- sb.append(delimiter);
- }
- sb.append(s);
- }
- return sb.toString();
- }
-
- static Map<String, String> concatenate(Map<String, String> pre, Map<String, String> post) {
- Map<String, String> result = new HashMap<String, String>(pre.size() + post.size());
- result.putAll(pre);
- result.putAll(post);
- return result;
- }
-
- // Quick utility for building up maps declaratively to minimize the diffs
- static Map<String, String> mapify(String... values) {
- Map<String, String> map = new HashMap<String, String>(values.length / 2);
- for (int i = 0; i < values.length; i += 2) {
- String key = values[i];
- if (key == null) {
- continue;
- }
- String value = values[i + 1];
- map.put(key, value);
- }
-
- return map;
- }
-
- /**
- * Produces a display name for an attribute, usually capitalizing the attribute name
- * and splitting up underscores into new words
- *
- * @param name the attribute name to convert
- * @return a display name for the attribute name
- */
- public static String getAttributeDisplayName(String name) {
- if (name != null && name.length() > 0) {
- StringBuilder sb = new StringBuilder();
- boolean capitalizeNext = true;
- for (int i = 0, n = name.length(); i < n; i++) {
- char c = name.charAt(i);
- if (capitalizeNext) {
- c = Character.toUpperCase(c);
- }
- capitalizeNext = false;
- if (c == '_') {
- c = ' ';
- capitalizeNext = true;
- }
- sb.append(c);
- }
-
- return sb.toString();
- }
-
- return name;
- }
-
-
- // ==== Paste support ====
-
- /**
- * Most views can't accept children so there's nothing to paste on them. In
- * this case, defer the call to the parent layout and use the target node as
- * an indication of where to paste.
- */
- @Override
- public void onPaste(@NonNull INode targetNode, @Nullable Object targetView,
- @NonNull IDragElement[] elements) {
- //
- INode parent = targetNode.getParent();
- if (parent != null) {
- String parentFqcn = parent.getFqcn();
- IViewRule parentRule = mRulesEngine.loadRule(parentFqcn);
-
- if (parentRule instanceof BaseLayoutRule) {
- ((BaseLayoutRule) parentRule).onPasteBeforeChild(parent, targetView, targetNode,
- elements);
- }
- }
- }
-
- /**
- * Support class for the context menu code. Stores state about properties in
- * the context menu.
- */
- private static class Prop {
- private final boolean mToggle;
- private final boolean mFlag;
- private final String mTitle;
- private final Map<String, String> mChoices;
- private String mDefinedBy;
-
- public Prop(String title, boolean isToggle, boolean isFlag, Map<String, String> choices,
- String definedBy) {
- mTitle = title;
- mToggle = isToggle;
- mFlag = isFlag;
- mChoices = choices;
- mDefinedBy = definedBy;
- }
-
- public String getDefinedBy() {
- return mDefinedBy;
- }
-
- public Prop(String title, boolean isToggle, String definedBy) {
- this(title, isToggle, false, null, definedBy);
- }
-
- private boolean isToggle() {
- return mToggle;
- }
-
- private boolean isFlag() {
- return mFlag && mChoices != null;
- }
-
- private boolean isEnum() {
- return !mFlag && mChoices != null;
- }
-
- private String getTitle() {
- return mTitle;
- }
-
- private Map<String, String> getChoices() {
- return mChoices;
- }
-
- private boolean isStringEdit() {
- return mChoices == null && !mToggle;
- }
- }
-
- /**
- * Returns a source attribute value which points to a sample image. This is typically
- * used to provide an initial image shown on ImageButtons, etc. There is no guarantee
- * that the source pointed to by this method actually exists.
- *
- * @return a source attribute to use for sample images, never null
- */
- protected final String getSampleImageSrc() {
- // Builtin graphics available since v1:
- return "@android:drawable/btn_star"; //$NON-NLS-1$
- }
-
- /**
- * Strips the {@code @+id} or {@code @id} prefix off of the given id
- *
- * @param id attribute to be stripped
- * @return the id name without the {@code @+id} or {@code @id} prefix
- */
- @NonNull
- public static String stripIdPrefix(@Nullable String id) {
- if (id == null) {
- return ""; //$NON-NLS-1$
- } else if (id.startsWith(NEW_ID_PREFIX)) {
- return id.substring(NEW_ID_PREFIX.length());
- } else if (id.startsWith(ID_PREFIX)) {
- return id.substring(ID_PREFIX.length());
- }
- return id;
- }
-
- private static String ensureValidString(String value) {
- if (value == null) {
- value = ""; //$NON-NLS-1$
- }
- return value;
- }
- }