From 3e75d4f79a8328ed18505830c786402369082efa Mon Sep 17 00:00:00 2001 From: Tor Norbye Date: Tue, 6 Mar 2012 14:22:37 -0800 Subject: Make GridLayout support work with the support library Also fix the paste operation to target the parent if the paste target does not accept children. Change-Id: Id084db376e5ff9b4a374e6d2145bc890a925a078 --- .../icons/GridLayout.png | Bin 466 -> 248 bytes .../com.android.ide.eclipse.adt/icons/Space.png | Bin 468 -> 248 bytes .../android/ide/common/layout/BaseLayoutRule.java | 29 --- .../android/ide/common/layout/GridLayoutRule.java | 45 +++- .../ide/common/layout/grid/GridDropHandler.java | 42 ++-- .../android/ide/common/layout/grid/GridModel.java | 274 +++++++++++---------- .../eclipse/adt/internal/editors/IconFactory.java | 57 ++++- .../editors/descriptors/DescriptorsUtils.java | 4 +- .../descriptors/CustomViewDescriptorService.java | 10 +- .../editors/layout/gle2/CanvasViewInfo.java | 3 +- .../editors/layout/gle2/ClipboardSupport.java | 5 +- .../editors/layout/gle2/GraphicalEditorPart.java | 1 + .../internal/editors/layout/gle2/OutlinePage.java | 72 +++++- .../editors/layout/gle2/SelectionManager.java | 4 +- .../editors/layout/gre/ClientRulesEngine.java | 14 +- .../internal/editors/layout/gre/RulesEngine.java | 25 +- .../layout/refactoring/GridLayoutConverter.java | 13 +- .../internal/editors/uimodel/UiElementNode.java | 8 +- 18 files changed, 387 insertions(+), 219 deletions(-) (limited to 'eclipse') diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/GridLayout.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/GridLayout.png index 1aa4165..ca64e8c 100644 Binary files a/eclipse/plugins/com.android.ide.eclipse.adt/icons/GridLayout.png and b/eclipse/plugins/com.android.ide.eclipse.adt/icons/GridLayout.png differ diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/Space.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/Space.png index 58afbe4..28b2553 100644 Binary files a/eclipse/plugins/com.android.ide.eclipse.adt/icons/Space.png and b/eclipse/plugins/com.android.ide.eclipse.adt/icons/Space.png differ diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseLayoutRule.java index b9c2290..47f3a58 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseLayoutRule.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseLayoutRule.java @@ -49,7 +49,6 @@ import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_TO_RIGHT import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_X; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_Y; -import static com.android.ide.common.layout.LayoutConstants.ATTR_TEXT; import static com.android.ide.common.layout.LayoutConstants.VALUE_FILL_PARENT; import static com.android.ide.common.layout.LayoutConstants.VALUE_MATCH_PARENT; import static com.android.ide.common.layout.LayoutConstants.VALUE_WRAP_CONTENT; @@ -518,30 +517,11 @@ public class BaseLayoutRule extends BaseViewRule { protected static void addAttributes(INode newNode, IDragElement oldElement, Map> idMap, AttributeFilter filter) { - // A little trick here: when creating new UI widgets by dropping them - // from the palette, we assign them a new id and then set the text - // attribute to that id, so for example a Button will have - // android:text="@+id/Button01". - // Here we detect if such an id is being remapped to a new id and if - // there's a text attribute with exactly the same id name, we update it - // too. - String oldText = null; - String oldId = null; - String newId = null; - for (IDragAttribute attr : oldElement.getAttributes()) { String uri = attr.getUri(); String name = attr.getName(); String value = attr.getValue(); - if (uri.equals(ANDROID_URI)) { - if (name.equals(ATTR_ID)) { - oldId = value; - } else if (name.equals(ATTR_TEXT)) { - oldText = value; - } - } - IAttributeInfo attrInfo = newNode.getAttributeInfo(uri, name); if (attrInfo != null) { Format[] formats = attrInfo.getFormats(); @@ -557,17 +537,8 @@ public class BaseLayoutRule extends BaseViewRule { } if (value != null && value.length() > 0) { newNode.setAttribute(uri, name, value); - - if (uri.equals(ANDROID_URI) && name.equals(ATTR_ID) && - oldId != null && !oldId.equals(value)) { - newId = value; - } } } - - if (newId != null && oldText != null && oldText.equals(oldId)) { - newNode.setAttribute(ANDROID_URI, ATTR_TEXT, newId); - } } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridLayoutRule.java index 4fda13d..f8f0a8c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridLayoutRule.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridLayoutRule.java @@ -21,7 +21,9 @@ import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_COLUMN; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_GRAVITY; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ROW; import static com.android.ide.common.layout.LayoutConstants.ATTR_ORIENTATION; +import static com.android.ide.common.layout.LayoutConstants.FQCN_GRID_LAYOUT; import static com.android.ide.common.layout.LayoutConstants.FQCN_SPACE; +import static com.android.ide.common.layout.LayoutConstants.FQCN_SPACE_V7; import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_FILL; import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_FILL_HORIZONTAL; import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_FILL_VERTICAL; @@ -150,11 +152,12 @@ public class GridLayoutRule extends BaseLayoutRule { final List children) { super.addLayoutActions(actions, parentNode, children); + String namespace = getNamespace(parentNode); Choices orientationAction = RuleAction.createChoices( ACTION_ORIENTATION, "Orientation", //$NON-NLS-1$ new PropertyCallback(Collections.singletonList(parentNode), - "Change LinearLayout Orientation", ANDROID_URI, ATTR_ORIENTATION), Arrays + "Change LinearLayout Orientation", namespace, ATTR_ORIENTATION), Arrays . asList("Set Horizontal Orientation", "Set Vertical Orientation"), Arrays. asList(ICON_HORIZONTAL, ICON_VERTICAL), Arrays. asList( "horizontal", "vertical"), getCurrentOrientation(parentNode), @@ -256,8 +259,8 @@ public class GridLayoutRule extends BaseLayoutRule { * Returns the orientation attribute value currently used by the node (even if not * defined, in which case the default horizontal value is returned) */ - private static String getCurrentOrientation(final INode node) { - String orientation = node.getStringAttr(ANDROID_URI, ATTR_ORIENTATION); + private String getCurrentOrientation(final INode node) { + String orientation = node.getStringAttr(getNamespace(node), ATTR_ORIENTATION); if (orientation == null || orientation.length() == 0) { orientation = VALUE_HORIZONTAL; } @@ -332,11 +335,28 @@ public class GridLayoutRule extends BaseLayoutRule { FillPreference fill = metadata.getFillPreference(); String gravity = computeDefaultGravity(fill); if (gravity != null) { - node.setAttribute(ANDROID_URI, ATTR_LAYOUT_GRAVITY, gravity); + node.setAttribute(getNamespace(parent), ATTR_LAYOUT_GRAVITY, gravity); } } /** + * Returns the namespace URI to use for GridLayout-specific attributes, such + * as columnCount, layout_column, layout_column_span, layout_gravity etc. + * + * @param layout the GridLayout instance to look up the namespace for + * @return the namespace, never null + */ + public String getNamespace(INode layout) { + String namespace = ANDROID_URI; + + if (!layout.getFqcn().equals(FQCN_GRID_LAYOUT)) { + namespace = mRulesEngine.getAppNameSpace(); + } + + return namespace; + } + + /** * Computes the default gravity to be used for a widget of the given fill * preference when added to a grid layout * @@ -374,7 +394,8 @@ public class GridLayoutRule extends BaseLayoutRule { GridModel grid = new GridModel(mRulesEngine, parent, null); for (INode child : deleted) { // We don't care about deletion of spacers - if (child.getFqcn().equals(FQCN_SPACE)) { + String fqcn = child.getFqcn(); + if (fqcn.equals(FQCN_SPACE) || fqcn.equals(FQCN_SPACE_V7)) { continue; } grid.markDeleted(child); @@ -430,8 +451,9 @@ public class GridLayoutRule extends BaseLayoutRule { Pair spans = computeResizeSpans(state); int rowSpan = spans.getFirst(); int columnSpan = spans.getSecond(); - GridModel.setColumnSpanAttribute(node, columnSpan); - GridModel.setRowSpanAttribute(node, rowSpan); + GridModel grid = getGrid(state); + grid.setColumnSpanAttribute(node, columnSpan); + grid.setRowSpanAttribute(node, rowSpan); } } @@ -561,7 +583,8 @@ public class GridLayoutRule extends BaseLayoutRule { for (IDragElement element : elements) { // Skip elements and only insert the real elements being // copied - if (elements.length > 1 && FQCN_SPACE.equals(element.getFqcn())) { + if (elements.length > 1 && (FQCN_SPACE.equals(element.getFqcn()) + || FQCN_SPACE_V7.equals(element.getFqcn()))) { continue; } @@ -571,8 +594,10 @@ public class GridLayoutRule extends BaseLayoutRule { // Ensure that we reset any potential row/column attributes from a different // grid layout being copied from - newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN, null); - newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, null); + GridDropHandler handler = (GridDropHandler) feedback.userData; + GridModel grid = handler.getGrid(); + grid.setGridAttribute(newChild, ATTR_LAYOUT_COLUMN, null); + grid.setGridAttribute(newChild, ATTR_LAYOUT_ROW, null); // TODO: Set columnSpans to avoid making these widgets completely // break the layout diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridDropHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridDropHandler.java index 0c7ed9f..74c1b59 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridDropHandler.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridDropHandler.java @@ -19,14 +19,12 @@ import static com.android.ide.common.layout.GridLayoutRule.GRID_SIZE; import static com.android.ide.common.layout.GridLayoutRule.MARGIN_SIZE; import static com.android.ide.common.layout.GridLayoutRule.MAX_CELL_DIFFERENCE; import static com.android.ide.common.layout.GridLayoutRule.SHORT_GAP_DP; -import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI; import static com.android.ide.common.layout.LayoutConstants.ATTR_COLUMN_COUNT; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_COLUMN; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_COLUMN_SPAN; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_GRAVITY; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ROW; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ROW_SPAN; -import static com.android.ide.common.layout.LayoutConstants.VALUE_1; import static com.android.ide.common.layout.LayoutConstants.VALUE_BOTTOM; import static com.android.ide.common.layout.LayoutConstants.VALUE_CENTER_HORIZONTAL; import static com.android.ide.common.layout.LayoutConstants.VALUE_RIGHT; @@ -478,20 +476,18 @@ public class GridDropHandler { // bottom half! - int columnX = mGrid.getColumnX(column); - int rowY = mGrid.getRowY(row); + //int columnX = mGrid.getColumnX(column); + //int rowY = mGrid.getRowY(row); - targetNode.setAttribute(ANDROID_URI, ATTR_COLUMN_COUNT, VALUE_1); - //targetNode.setAttribute(ANDROID_URI, ATTR_COLUMN_COUNT, "3"); + mGrid.setGridAttribute(targetNode, ATTR_COLUMN_COUNT, 2); + //mGrid.setGridAttribute(targetNode, ATTR_COLUMN_COUNT, 3); //INode scr0 = addSpacer(targetNode, -1, 0, 0, 1, 1); //INode sc1 = addSpacer(targetNode, -1, 0, 1, 0, 0); //INode sc2 = addSpacer(targetNode, -1, 0, 2, 1, 0); //INode sr1 = addSpacer(targetNode, -1, 1, 0, 0, 0); //INode sr2 = addSpacer(targetNode, -1, 2, 0, 0, 1); - //sc1.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN_WEIGHT, VALUE_1); - //sr1.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW_WEIGHT, VALUE_1); - //sc1.setAttribute(ANDROID_URI, ATTR_LAYOUT_GRAVITY, VALUE_FILL_HORIZONTAL); - //sr1.setAttribute(ANDROID_URI, ATTR_LAYOUT_GRAVITY, VALUE_FILL_VERTICAL); + //mGrid.setGridAttribute(sc1, ATTR_LAYOUT_GRAVITY, VALUE_FILL_HORIZONTAL); + //mGrid.setGridAttribute(sr1, ATTR_LAYOUT_GRAVITY, VALUE_FILL_VERTICAL); // //mGrid.loadFromXml(); //column = mGrid.getColumn(columnX); @@ -655,33 +651,33 @@ public class GridDropHandler { // Set the cell position of the new widget if (mColumnMatch.type == SegmentType.RIGHT) { - newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_GRAVITY, VALUE_RIGHT); + mGrid.setGridAttribute(newChild, ATTR_LAYOUT_GRAVITY, VALUE_RIGHT); } else if (mColumnMatch.type == SegmentType.CENTER_HORIZONTAL) { - newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_GRAVITY, VALUE_CENTER_HORIZONTAL); + mGrid.setGridAttribute(newChild, ATTR_LAYOUT_GRAVITY, VALUE_CENTER_HORIZONTAL); } - newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN, Integer.toString(column)); + mGrid.setGridAttribute(newChild, ATTR_LAYOUT_COLUMN, column); if (mRowMatch.type == SegmentType.BOTTOM) { String value = VALUE_BOTTOM; if (mColumnMatch.type == SegmentType.RIGHT) { value = value + '|' + VALUE_RIGHT; + } else if (mColumnMatch.type == SegmentType.CENTER_HORIZONTAL) { + value = value + '|' + VALUE_CENTER_HORIZONTAL; } - newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_GRAVITY, value); + mGrid.setGridAttribute(newChild, ATTR_LAYOUT_GRAVITY, value); } - newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, Integer.toString(row)); + mGrid.setGridAttribute(newChild, ATTR_LAYOUT_ROW, row); // Apply spans to ensure that the widget can fit without pushing columns if (columnSpan > 1) { - newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN_SPAN, - Integer.toString(columnSpan)); + mGrid.setGridAttribute(newChild, ATTR_LAYOUT_COLUMN_SPAN, columnSpan); } if (rowSpan > 1) { - newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW_SPAN, Integer.toString(rowSpan)); + mGrid.setGridAttribute(newChild, ATTR_LAYOUT_ROW_SPAN, rowSpan); } // Ensure that we don't store columnCount=0 if (mGrid.actualColumnCount == 0) { - mGrid.layout.setAttribute(ANDROID_URI, ATTR_COLUMN_COUNT, - Integer.toString(Math.max(1, column + 1))); + mGrid.setGridAttribute(mGrid.layout, ATTR_COLUMN_COUNT, Math.max(1, column + 1)); } return newChild; @@ -708,10 +704,8 @@ public class GridDropHandler { mGrid.addRow(mRowMatch.cellIndex, newChild, UNDEFINED, false, UNDEFINED, UNDEFINED); } - newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN, - Integer.toString(mColumnMatch.cellIndex)); - newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, - Integer.toString(mRowMatch.cellIndex)); + mGrid.setGridAttribute(newChild, ATTR_LAYOUT_COLUMN, mColumnMatch.cellIndex); + mGrid.setGridAttribute(newChild, ATTR_LAYOUT_ROW, mRowMatch.cellIndex); return newChild; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridModel.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridModel.java index 1e4d5cd..9ef247e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridModel.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridModel.java @@ -31,8 +31,12 @@ import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ROW_SPAN import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH; import static com.android.ide.common.layout.LayoutConstants.ATTR_ORIENTATION; import static com.android.ide.common.layout.LayoutConstants.ATTR_ROW_COUNT; +import static com.android.ide.common.layout.LayoutConstants.FQCN_GRID_LAYOUT; import static com.android.ide.common.layout.LayoutConstants.FQCN_SPACE; +import static com.android.ide.common.layout.LayoutConstants.FQCN_SPACE_V7; +import static com.android.ide.common.layout.LayoutConstants.GRID_LAYOUT; import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX; +import static com.android.ide.common.layout.LayoutConstants.SPACE; import static com.android.ide.common.layout.LayoutConstants.VALUE_BOTTOM; import static com.android.ide.common.layout.LayoutConstants.VALUE_CENTER_VERTICAL; import static com.android.ide.common.layout.LayoutConstants.VALUE_N_DP; @@ -147,6 +151,9 @@ public class GridModel { */ private Object mViewObject; + /** The namespace to use for attributes */ + private String mNamespace; + /** * Constructs a {@link GridModel} for the given layout * @@ -250,7 +257,7 @@ public class GridModel { if (baseline != -1) { // Even views that do have baselines do not count towards a row // baseline if they have a vertical gravity - String gravity = view.node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_GRAVITY); + String gravity = getGridAttribute(view.node, ATTR_LAYOUT_GRAVITY); if (gravity == null || !(gravity.contains(VALUE_TOP) || gravity.contains(VALUE_BOTTOM) @@ -275,13 +282,61 @@ public class GridModel { } // Also fix the columnCount - if (layout.getStringAttr(ANDROID_URI, ATTR_COLUMN_COUNT) != null && + if (getGridAttribute(layout, ATTR_COLUMN_COUNT) != null && declaredColumnCount > actualColumnCount) { - layout.setAttribute(ANDROID_URI, ATTR_COLUMN_COUNT, - Integer.toString(actualColumnCount)); + setGridAttribute(layout, ATTR_COLUMN_COUNT, actualColumnCount); } } + /** + * Sets the given GridLayout attribute (rowCount, layout_row, etc) to the + * given value. This automatically handles using the right XML namespace + * based on whether the GridLayout is the android.widget.GridLayout, or the + * support library GridLayout, and whether it's in a library project or not + * etc. + * + * @param node the node to apply the attribute to + * @param name the local name of the attribute + * @param value the integer value to set the attribute to + */ + public void setGridAttribute(INode node, String name, int value) { + setGridAttribute(node, name, Integer.toString(value)); + } + + /** + * Sets the given GridLayout attribute (rowCount, layout_row, etc) to the + * given value. This automatically handles using the right XML namespace + * based on whether the GridLayout is the android.widget.GridLayout, or the + * support library GridLayout, and whether it's in a library project or not + * etc. + * + * @param node the node to apply the attribute to + * @param name the local name of the attribute + * @param value the string value to set the attribute to, or null to clear + * it + */ + public void setGridAttribute(INode node, String name, String value) { + node.setAttribute(getNamespace(), name, value); + } + + /** + * Returns the namespace URI to use for GridLayout-specific attributes, such + * as columnCount, layout_column, layout_column_span, layout_gravity etc. + * + * @return the namespace, never null + */ + public String getNamespace() { + if (mNamespace == null) { + mNamespace = ANDROID_URI; + + if (!layout.getFqcn().equals(FQCN_GRID_LAYOUT)) { + mNamespace = mRulesEngine.getAppNameSpace(); + } + } + + return mNamespace; + } + /** Removes the given flag from a flag attribute value and returns the result */ static String removeFlag(String flag, String value) { if (value.equals(flag)) { @@ -313,10 +368,10 @@ public class GridModel { void loadFromXml() { INode[] children = layout.getChildren(); - declaredRowCount = getInt(layout, ATTR_ROW_COUNT, UNDEFINED); - declaredColumnCount = getInt(layout, ATTR_COLUMN_COUNT, UNDEFINED); + declaredRowCount = getGridAttribute(layout, ATTR_ROW_COUNT, UNDEFINED); + declaredColumnCount = getGridAttribute(layout, ATTR_COLUMN_COUNT, UNDEFINED); // Horizontal is the default, so if no value is specified it is horizontal. - vertical = VALUE_VERTICAL.equals(layout.getStringAttr(ANDROID_URI, ATTR_ORIENTATION)); + vertical = VALUE_VERTICAL.equals(getGridAttribute(layout, ATTR_ORIENTATION)); mChildViews = new ArrayList(children.length); int index = 0; @@ -627,7 +682,7 @@ public class GridModel { } // The bounds should be in ascending order now - if (GridLayoutRule.sDebugGridLayout) { + if (false && GridLayoutRule.sDebugGridLayout) { for (int i = 1; i < actualRowCount; i++) { assert mTop[i + 1] >= mTop[i]; } @@ -756,8 +811,7 @@ public class GridModel { // Insert a new column if (declaredColumnCount != UNDEFINED) { declaredColumnCount++; - layout.setAttribute(ANDROID_URI, ATTR_COLUMN_COUNT, - Integer.toString(declaredColumnCount)); + setGridAttribute(layout, ATTR_COLUMN_COUNT, declaredColumnCount); } boolean isLastColumn = true; @@ -796,12 +850,10 @@ public class GridModel { // the new row number, but we use the spacer to assign the row // some height. if (view.column == newColumn) { - view.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN, - Integer.toString(view.column + 1)); + setGridAttribute(view.node, ATTR_LAYOUT_COLUMN, view.column + 1); } // else: endColumn == newColumn: handled below - } else if (view.node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN) != null) { - view.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN, - Integer.toString(view.column + 1)); + } else if (getGridAttribute(view.node, ATTR_LAYOUT_COLUMN) != null) { + setGridAttribute(view.node, ATTR_LAYOUT_COLUMN, view.column + 1); } } else if (endColumn > newColumn) { setColumnSpanAttribute(view.node, view.columnSpan + 1); @@ -820,8 +872,7 @@ public class GridModel { if (isLastColumn) { for (ViewData view : mChildViews) { if (view.column == 0 && view.row > 0) { - view.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, - Integer.toString(view.row)); + setGridAttribute(view.node, ATTR_LAYOUT_ROW, view.row); } } if (split) { @@ -867,8 +918,7 @@ public class GridModel { // TODO: Do this under a write lock? / editXml lock? if (declaredColumnCount != UNDEFINED) { declaredColumnCount--; - layout.setAttribute(ANDROID_URI, ATTR_COLUMN_COUNT, - Integer.toString(declaredColumnCount)); + setGridAttribute(layout, ATTR_COLUMN_COUNT, declaredColumnCount); } // Remove any elements that begin in the deleted columns... @@ -894,9 +944,8 @@ public class GridModel { // Subtract column span to skip this item setColumnSpanAttribute(view.node, view.columnSpan - 1); } else if (view.column > removedColumn) { - if (view.node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN) != null) { - view.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN, - Integer.toString(view.column - 1)); + if (getGridAttribute(view.node, ATTR_LAYOUT_COLUMN) != null) { + setGridAttribute(view.node, ATTR_LAYOUT_COLUMN, view.column - 1); } } } @@ -948,8 +997,7 @@ public class GridModel { if (declaredRowCount != UNDEFINED) { declaredRowCount++; - layout.setAttribute(ANDROID_URI, ATTR_ROW_COUNT, - Integer.toString(declaredRowCount)); + setGridAttribute(layout, ATTR_ROW_COUNT, declaredRowCount); } boolean added = false; for (ViewData view : mChildViews) { @@ -961,8 +1009,7 @@ public class GridModel { int index = getChildIndex(layout.getChildren(), view.node); assert view.index == index; // TODO: Get rid of getter if (declaredColumnCount != UNDEFINED && !split) { - layout.setAttribute(ANDROID_URI, ATTR_COLUMN_COUNT, - Integer.toString(declaredColumnCount)); + setGridAttribute(layout, ATTR_COLUMN_COUNT, declaredColumnCount); } newView = addSpacer(layout, index, split ? newRow - 1 : UNDEFINED, @@ -975,13 +1022,11 @@ public class GridModel { // This means we don't really need the spacer above to imply // the new row number, but we use the spacer to assign the row // some height. - view.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, - Integer.toString(view.row + 1)); + setGridAttribute(view.node, ATTR_LAYOUT_ROW, view.row + 1); added = true; - } else if (view.node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_ROW) != null) { - view.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, - Integer.toString(view.row + 1)); + } else if (getGridAttribute(view.node, ATTR_LAYOUT_ROW) != null) { + setGridAttribute(view.node, ATTR_LAYOUT_ROW, view.row + 1); } } else { int endRow = view.row + view.rowSpan; @@ -1003,12 +1048,12 @@ public class GridModel { rowHeightDp != UNDEFINED ? rowHeightDp : DEFAULT_CELL_HEIGHT); } if (declaredColumnCount != UNDEFINED && !split) { - newView.setAttribute(ANDROID_URI, ATTR_COLUMN_COUNT, - Integer.toString(declaredColumnCount)); + setGridAttribute(layout, ATTR_COLUMN_COUNT, declaredColumnCount); } if (split) { - newView.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, Integer.toString(newRow - 1)); - newView.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN, Integer.toString(column)); + setGridAttribute(newView, ATTR_LAYOUT_ROW, newRow - 1); + setGridAttribute(newView, ATTR_LAYOUT_COLUMN, column); + } } @@ -1046,8 +1091,7 @@ public class GridModel { // the declared row range! if (declaredRowCount != UNDEFINED) { declaredRowCount--; - layout.setAttribute(ANDROID_URI, ATTR_ROW_COUNT, - Integer.toString(declaredRowCount)); + setGridAttribute(layout, ATTR_ROW_COUNT, declaredRowCount); } // Remove any elements that begin in the deleted rows... @@ -1062,9 +1106,8 @@ public class GridModel { // positions for other cells layout.removeChild(view.node); } else if (view.row > removedRow) { - if (view.node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_ROW) != null) { - view.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, - Integer.toString(view.row - 1)); + if (getGridAttribute(view.node, ATTR_LAYOUT_ROW) != null) { + setGridAttribute(view.node, ATTR_LAYOUT_ROW, view.row - 1); } } else if (view.row < removedRow && view.row + view.rowSpan > removedRow) { @@ -1422,8 +1465,7 @@ public class GridModel { if (insertMarginColumn) { declaredColumnCount++; } - layout.setAttribute(ANDROID_URI, ATTR_COLUMN_COUNT, - Integer.toString(declaredColumnCount)); + setGridAttribute(layout, ATTR_COLUMN_COUNT, declaredColumnCount); } // Are we inserting a new last column in the grid? That requires some special handling... @@ -1441,9 +1483,8 @@ public class GridModel { if (isLastColumn) { for (ViewData view : mChildViews) { if (view.column == 0 && view.row > 0) { - if (view.node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_ROW) == null) { - view.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, - Integer.toString(view.row)); + if (getGridAttribute(view.node, ATTR_LAYOUT_ROW) == null) { + setGridAttribute(view.node, ATTR_LAYOUT_ROW, view.row); } } } @@ -1483,9 +1524,8 @@ public class GridModel { // where necessary, e.g. only on the FIRST view on this row following the // skipped column! - //if (node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN) != null) { - node.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN, - Integer.toString(column + (insertMarginColumn ? 2 : 1))); + //if (getGridAttribute(node, ATTR_LAYOUT_COLUMN) != null) { + setGridAttribute(node, ATTR_LAYOUT_COLUMN, column + (insertMarginColumn ? 2 : 1)); //} } else if (!view.isSpacer()) { int endColumn = column + view.columnSpan; @@ -1508,8 +1548,8 @@ public class GridModel { if (remaining > 0) { prevColumnSpacer.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, String.format(VALUE_N_DP, remaining)); - prevColumnSpacer.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN, - Integer.toString(insertMarginColumn ? newColumn + 1 : newColumn)); + setGridAttribute(prevColumnSpacer.node, ATTR_LAYOUT_COLUMN, + insertMarginColumn ? newColumn + 1 : newColumn); } } @@ -1541,8 +1581,7 @@ public class GridModel { if (insertMarginRow) { declaredRowCount++; } - layout.setAttribute(ANDROID_URI, ATTR_ROW_COUNT, - Integer.toString(declaredRowCount)); + setGridAttribute(layout, ATTR_ROW_COUNT, declaredRowCount); } // Find the spacer which marks this row, and if found, mark it as a split @@ -1563,9 +1602,8 @@ public class GridModel { INode node = view.node; int row = view.row; if (row > newRow || (row == newRow && view.node.getBounds().y2() > y)) { - //if (node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_ROW) != null) { - node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, - Integer.toString(row + (insertMarginRow ? 2 : 1))); + //if (getGridAttribute(node, ATTR_LAYOUT_ROW) != null) { + setGridAttribute(node, ATTR_LAYOUT_ROW, row + (insertMarginRow ? 2 : 1)); //} } else if (!view.isSpacer()) { int endRow = row + view.rowSpan; @@ -1588,8 +1626,8 @@ public class GridModel { if (remaining > 0) { prevRowSpacer.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, String.format(VALUE_N_DP, remaining)); - prevRowSpacer.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, - Integer.toString(insertMarginRow ? newRow + 1 : newRow)); + setGridAttribute(prevRowSpacer.node, ATTR_LAYOUT_ROW, + insertMarginRow ? newRow + 1 : newRow); } } @@ -1604,7 +1642,7 @@ public class GridModel { * Data about a view in a table; this is not the same as a cell because multiple views * can share a single cell, and a view can span many cells. */ - static class ViewData { + class ViewData { public final INode node; public final int index; public int row; @@ -1617,23 +1655,20 @@ public class GridModel { node = n; this.index = index; - column = getInt(n, ATTR_LAYOUT_COLUMN, UNDEFINED); - columnSpan = getInt(n, ATTR_LAYOUT_COLUMN_SPAN, 1); - row = getInt(n, ATTR_LAYOUT_ROW, UNDEFINED); - rowSpan = getInt(n, ATTR_LAYOUT_ROW_SPAN, 1); - gravity = GravityHelper.getGravity(n.getStringAttr(ANDROID_URI, ATTR_LAYOUT_GRAVITY), - 0); + column = getGridAttribute(n, ATTR_LAYOUT_COLUMN, UNDEFINED); + columnSpan = getGridAttribute(n, ATTR_LAYOUT_COLUMN_SPAN, 1); + row = getGridAttribute(n, ATTR_LAYOUT_ROW, UNDEFINED); + rowSpan = getGridAttribute(n, ATTR_LAYOUT_ROW_SPAN, 1); + gravity = GravityHelper.getGravity(getGridAttribute(n, ATTR_LAYOUT_GRAVITY), 0); } /** Applies the column and row fields into the XML model */ void applyPositionAttributes() { - if (node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN) == null) { - node.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN, - Integer.toString(column)); + if (getGridAttribute(node, ATTR_LAYOUT_COLUMN) == null) { + setGridAttribute(node, ATTR_LAYOUT_COLUMN, column); } - if (node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_ROW) == null) { - node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, - Integer.toString(row)); + if (getGridAttribute(node, ATTR_LAYOUT_ROW) == null) { + setGridAttribute(node, ATTR_LAYOUT_ROW, row); } } @@ -1653,7 +1688,8 @@ public class GridModel { /** Returns true if this {@link ViewData} represents a spacer */ boolean isSpacer() { - return FQCN_SPACE.equals(node.getFqcn()); + String fqcn = node.getFqcn(); + return FQCN_SPACE.equals(fqcn) || FQCN_SPACE_V7.equals(fqcn); } /** @@ -1690,9 +1726,8 @@ public class GridModel { * @param node the target node * @param span the new column span */ - public static void setColumnSpanAttribute(INode node, int span) { - node.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN_SPAN, - span > 1 ? Integer.toString(span) : null); + public void setColumnSpanAttribute(INode node, int span) { + setGridAttribute(node, ATTR_LAYOUT_COLUMN_SPAN, span > 1 ? Integer.toString(span) : null); } /** @@ -1702,9 +1737,8 @@ public class GridModel { * @param node the target node * @param span the new row span */ - public static void setRowSpanAttribute(INode node, int span) { - node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW_SPAN, - span > 1 ? Integer.toString(span) : null); + public void setRowSpanAttribute(INode node, int span) { + setGridAttribute(node, ATTR_LAYOUT_ROW_SPAN, span > 1 ? Integer.toString(span) : null); } /** Returns the index of the given target node in the given child node array */ @@ -1768,7 +1802,8 @@ public class GridModel { for (ViewData spacer : rowSpacers.values()) { layout.removeChild(spacer.node); } - layout.setAttribute(ANDROID_URI, ATTR_COLUMN_COUNT, Integer.toString(2)); + setGridAttribute(layout, ATTR_COLUMN_COUNT, 2); + return; } @@ -1792,8 +1827,7 @@ public class GridModel { nextSpacer.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, String.format(VALUE_N_DP, combinedSizeDp)); // Also move the spacer into this column - nextSpacer.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN, - Integer.toString(column)); + setGridAttribute(nextSpacer.node, ATTR_LAYOUT_COLUMN, column); columnSpacers.put(column, nextSpacer); } else { continue; @@ -1815,8 +1849,7 @@ public class GridModel { if (view.column >= column) { if (view.column > 0) { view.column--; - view.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN, - Integer.toString(view.column)); + setGridAttribute(view.node, ATTR_LAYOUT_COLUMN, view.column); } } else if (!view.isSpacer()) { int endColumn = view.column + view.columnSpan; @@ -1843,8 +1876,7 @@ public class GridModel { int combinedSizeDp = nextSizeDp + rowHeightDp; nextSpacer.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, String.format(VALUE_N_DP, combinedSizeDp)); - nextSpacer.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, - Integer.toString(row)); + setGridAttribute(nextSpacer.node, ATTR_LAYOUT_ROW, row); rowSpacers.put(row, nextSpacer); } else { continue; @@ -1865,8 +1897,7 @@ public class GridModel { if (view.row >= row) { if (view.row > 0) { view.row--; - view.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, - Integer.toString(view.row)); + setGridAttribute(view.node, ATTR_LAYOUT_ROW, view.row); } } else if (!view.isSpacer()) { int endRow = view.row + view.rowSpan; @@ -1937,19 +1968,27 @@ public class GridModel { * @param heightDp the height in device independent pixels to assign to the spacer * @return the newly added spacer */ - static INode addSpacer(INode parent, int index, int row, int column, + INode addSpacer(INode parent, int index, int row, int column, int widthDp, int heightDp) { INode spacer; + + String tag = FQCN_SPACE; + String gridLayout = parent.getFqcn(); + if (!gridLayout.equals(GRID_LAYOUT) && gridLayout.length() > GRID_LAYOUT.length()) { + String pkg = gridLayout.substring(0, gridLayout.length() - GRID_LAYOUT.length()); + tag = pkg + SPACE; + } if (index != -1) { - spacer = parent.insertChildAt(FQCN_SPACE, index); + spacer = parent.insertChildAt(tag, index); } else { - spacer = parent.appendChild(FQCN_SPACE); + spacer = parent.appendChild(tag); } + if (row != UNDEFINED) { - spacer.setAttribute(ANDROID_URI, ATTR_LAYOUT_ROW, Integer.toString(row)); + setGridAttribute(spacer, ATTR_LAYOUT_ROW, row); } if (column != UNDEFINED) { - spacer.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN, Integer.toString(column)); + setGridAttribute(spacer, ATTR_LAYOUT_COLUMN, column); } if (widthDp > 0) { spacer.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, @@ -1985,41 +2024,34 @@ public class GridModel { } /** - * Returns the integer value of the given attribute, or the given defaultValue if the - * attribute was not set. + * Returns the string value of the given attribute, or null if it does not + * exist. This only works for attributes that are GridLayout specific, such + * as columnCount, layout_column, layout_row_span, etc. * * @param node the target node - * @param attribute the attribute name (which must be in the android: namespace) - * @param defaultValue the default value to use if the value is not set - * @return the attribute integer value + * @param name the attribute name (which must be in the android: namespace) + * @return the attribute value or null */ - private static int getInt(INode node, String attribute, int defaultValue) { - String valueString = node.getStringAttr(ANDROID_URI, attribute); - if (valueString != null) { - try { - return Integer.decode(valueString); - } catch (NumberFormatException nufe) { - // Ignore - error in user's XML - } - } - return defaultValue; + public String getGridAttribute(INode node, String name) { + return node.getStringAttr(getNamespace(), name); } /** - * Returns the float value of the given attribute, or the given defaultValue if the - * attribute was not set. + * Returns the integer value of the given attribute, or the given defaultValue if the + * attribute was not set. This only works for attributes that are GridLayout specific, + * such as columnCount, layout_column, layout_row_span, etc. * * @param node the target node * @param attribute the attribute name (which must be in the android: namespace) * @param defaultValue the default value to use if the value is not set - * @return the attribute float value + * @return the attribute integer value */ - private static float getFloat(INode node, String attribute, float defaultValue) { - String valueString = node.getStringAttr(ANDROID_URI, attribute); + private int getGridAttribute(INode node, String attribute, int defaultValue) { + String valueString = node.getStringAttr(getNamespace(), attribute); if (valueString != null) { try { - return Float.parseFloat(valueString); + return Integer.decode(valueString); } catch (NumberFormatException nufe) { // Ignore - error in user's XML } @@ -2029,24 +2061,6 @@ public class GridModel { } /** - * Returns the boolean value of the given attribute, or the given defaultValue if the - * attribute was not set. - * - * @param node the target node - * @param attribute the attribute name (which must be in the android: namespace) - * @param defaultValue the default value to use if the value is not set - * @return the attribute boolean value - */ - private static boolean getBoolean(INode node, String attribute, boolean defaultValue) { - String valueString = node.getStringAttr(ANDROID_URI, attribute); - if (valueString != null) { - return Boolean.valueOf(valueString); - } - - return defaultValue; - } - - /** * Returns the number of children views in the GridLayout * * @return the number of children views in the GridLayout diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java index dfdc740..ca13d38 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.internal.editors; +import com.android.annotations.NonNull; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.sdklib.SdkConstants; @@ -104,7 +105,7 @@ public class IconFactory { * 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 + * editor's "icons" directory. If it doesn't exist, 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. @@ -172,6 +173,60 @@ public class IconFactory { } /** + * Returns an Image for a given icon name. + *

+ * 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 exist, the + * fallback will be used instead. + * @param fallback the fallback icon name to use if the primary icon does + * not exist. + * @return the icon, which should not be disposed by the caller + */ + public Image getIcon(String osName, String fallback) { + String key = osName; + Image icon = mIconMap.get(key); + if (icon == null && !mIconMap.containsKey(key)) { + ImageDescriptor id = getImageDescriptor(osName, fallback); + 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 icon of the given name, or if that image does not exist and icon + * of the given fallback name. + * + * @param key the icon name + * @param fallbackKey the fallback image to use if the primary key does not exist + * @return the image descriptor + */ + @NonNull + public ImageDescriptor getImageDescriptor(@NonNull String key, @NonNull String fallbackKey) { + ImageDescriptor id = mImageDescMap.get(key); + if (id == null && !mImageDescMap.containsKey(key)) { + id = AbstractUIPlugin.imageDescriptorFromPlugin( + AdtPlugin.PLUGIN_ID, + String.format("/icons/%1$s.png", key)); //$NON-NLS-1$ + if (id == null) { + id = getImageDescriptor(fallbackKey); + } + + // Place the fallback image for this key as well such that we don't keep trying + // to load the failed image + mImageDescMap.put(key, id); + } + + return id; + } + + /** * Returns the image indicated by the given URL * * @param url the url to the image resources diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java index 30c7687..f44faf4 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java @@ -773,7 +773,9 @@ public final class DescriptorsUtils { || tag.equals(VIEW_FRAGMENT) || tag.equals(VIEW_INCLUDE) || tag.equals(VIEW_MERGE) - || tag.equals(SPACE)) { + || tag.equals(SPACE) + || tag.endsWith(SPACE) && tag.length() > SPACE.length() && + tag.charAt(tag.length() - SPACE.length()) == '.') { return false; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java index 90e46cf..5abbb0c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java @@ -319,7 +319,15 @@ public final class CustomViewDescriptorService { @Override public Image getGenericIcon() { - return IconFactory.getInstance().getIcon("customView"); //$NON-NLS-1$ + IconFactory iconFactory = IconFactory.getInstance(); + + int index = mXmlName.lastIndexOf('.'); + if (index != -1) { + return iconFactory.getIcon(mXmlName.substring(index + 1), + "customView"); //$NON-NLS-1$ + } + + return iconFactory.getIcon("customView"); //$NON-NLS-1$ } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java index 792194e..dd103c5 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; import static com.android.ide.common.layout.LayoutConstants.FQCN_SPACE; +import static com.android.ide.common.layout.LayoutConstants.FQCN_SPACE_V7; import static com.android.ide.common.layout.LayoutConstants.GESTURE_OVERLAY_VIEW; import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_MERGE; @@ -457,7 +458,7 @@ public class CanvasViewInfo implements IPropertySource { return false; } - return FQCN_SPACE.equals(mName); + return FQCN_SPACE.equals(mName) || FQCN_SPACE_V7.equals(mName); } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ClipboardSupport.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ClipboardSupport.java index b8ab3fd..b5bcf29 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ClipboardSupport.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ClipboardSupport.java @@ -285,8 +285,9 @@ public class ClipboardSupport { mCanvas.getEditorDelegate().getEditor().wrapUndoEditXmlModel("Paste", new Runnable() { @Override public void run() { - mCanvas.getRulesEngine().callOnPaste(targetNode, target.getViewObject(), pasted); - targetNode.applyPendingChanges(); + RulesEngine engine = mCanvas.getRulesEngine(); + NodeProxy node = engine.callOnPaste(targetNode, target.getViewObject(), pasted); + node.applyPendingChanges(); } }); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java index 7c8cc81..b43cff9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java @@ -1658,6 +1658,7 @@ public class GraphicalEditorPart extends EditorPart for (String clazz : brokenClasses) { addText(mErrorLabel, "- "); + addText(mErrorLabel, clazz); addText(mErrorLabel, " ("); addActionLink(mErrorLabel, ActionLinkStyleRange.LINK_OPEN_CLASS, "Open Class", clazz); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java index 485d574..c5158af 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java @@ -18,18 +18,23 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI; import static com.android.ide.common.layout.LayoutConstants.ATTR_CLASS; +import static com.android.ide.common.layout.LayoutConstants.ATTR_COLUMN_COUNT; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_COLUMN; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_COLUMN_SPAN; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ROW; import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_ROW_SPAN; import static com.android.ide.common.layout.LayoutConstants.ATTR_ORIENTATION; +import static com.android.ide.common.layout.LayoutConstants.ATTR_ROW_COUNT; import static com.android.ide.common.layout.LayoutConstants.ATTR_SRC; import static com.android.ide.common.layout.LayoutConstants.ATTR_TEXT; import static com.android.ide.common.layout.LayoutConstants.DRAWABLE_PREFIX; +import static com.android.ide.common.layout.LayoutConstants.GRID_LAYOUT; import static com.android.ide.common.layout.LayoutConstants.LAYOUT_PREFIX; import static com.android.ide.common.layout.LayoutConstants.LINEAR_LAYOUT; import static com.android.ide.common.layout.LayoutConstants.VALUE_VERTICAL; import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_VIEWTAG; +import static com.android.tools.lint.detector.api.LintConstants.AUTO_URI; +import static com.android.tools.lint.detector.api.LintConstants.URI_PREFIX; import static org.eclipse.jface.viewers.StyledString.QUALIFIER_STYLER; import com.android.annotations.VisibleForTesting; @@ -46,10 +51,14 @@ import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDes import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference; import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy; import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; +import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo; import com.android.ide.eclipse.adt.internal.editors.ui.ErrorImageComposite; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; +import com.android.ide.eclipse.adt.internal.sdk.ProjectState; +import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.util.Pair; +import org.eclipse.core.resources.IProject; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.ActionContributionItem; import org.eclipse.jface.action.IAction; @@ -58,6 +67,7 @@ import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; +import org.eclipse.jface.preference.JFacePreferences; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.IElementComparer; @@ -66,6 +76,7 @@ import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.ITreeSelection; import org.eclipse.jface.viewers.StyledCellLabelProvider; import org.eclipse.jface.viewers.StyledString; +import org.eclipse.jface.viewers.StyledString.Styler; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.jface.viewers.TreeViewer; @@ -559,21 +570,59 @@ public class OutlinePage extends ContentOutlinePage Element e = (Element) xmlNode; // Temporary diagnostics code when developing GridLayout - if (GridLayoutRule.sDebugGridLayout && e.getParentNode() != null - && e.getParentNode().getNodeName() != null) { - if (e.getParentNode().getNodeName().equals("GridLayout")) { //$NON-NLS-1$ + if (GridLayoutRule.sDebugGridLayout) { + String namespace; + if (e.getParentNode().getNodeName().equals(GRID_LAYOUT)) { + namespace = ANDROID_URI; + } else { + IProject project = mGraphicalEditorPart.getProject(); + ProjectState projectState = Sdk.getProjectState(project); + if (projectState != null && projectState.isLibrary()) { + namespace = AUTO_URI; + } else { + ManifestInfo info = ManifestInfo.get(project); + namespace = URI_PREFIX + info.getPackage(); + } + } + + if (e.getNodeName() != null && e.getNodeName().endsWith(GRID_LAYOUT)) { + // Attach rowCount/columnCount info + String rowCount = e.getAttributeNS(namespace, ATTR_ROW_COUNT); + if (rowCount.length() == 0) { + rowCount = "?"; + } + String columnCount = e.getAttributeNS(namespace, ATTR_COLUMN_COUNT); + if (columnCount.length() == 0) { + columnCount = "?"; + } + + styledString.append(" - columnCount=", QUALIFIER_STYLER); + styledString.append(columnCount, QUALIFIER_STYLER); + styledString.append(", rowCount=", QUALIFIER_STYLER); + styledString.append(rowCount, QUALIFIER_STYLER); + } else if (e.getParentNode() != null + && e.getParentNode().getNodeName() != null + && e.getParentNode().getNodeName().endsWith(GRID_LAYOUT)) { // Attach row/column info - styledString.append(" - cell (", QUALIFIER_STYLER); - String row = e.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_ROW); + String row = e.getAttributeNS(namespace, ATTR_LAYOUT_ROW); if (row.length() == 0) { row = "?"; } - String column = e.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_COLUMN); + Styler colStyle = QUALIFIER_STYLER; + String column = e.getAttributeNS(namespace, ATTR_LAYOUT_COLUMN); if (column.length() == 0) { column = "?"; + } else { + String colCount = ((Element) e.getParentNode()).getAttributeNS( + namespace, ATTR_COLUMN_COUNT); + if (colCount.length() > 0 && Integer.parseInt(colCount) <= + Integer.parseInt(column)) { + colStyle = StyledString.createColorRegistryStyler( + JFacePreferences.ERROR_COLOR, null); + } } - String rowSpan = e.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_ROW_SPAN); - String columnSpan = e.getAttributeNS(ANDROID_URI, + String rowSpan = e.getAttributeNS(namespace, ATTR_LAYOUT_ROW_SPAN); + String columnSpan = e.getAttributeNS(namespace, ATTR_LAYOUT_COLUMN_SPAN); if (rowSpan.length() == 0) { rowSpan = "1"; @@ -582,10 +631,13 @@ public class OutlinePage extends ContentOutlinePage columnSpan = "1"; } + styledString.append(" - cell (row=", QUALIFIER_STYLER); styledString.append(row, QUALIFIER_STYLER); styledString.append(',', QUALIFIER_STYLER); - styledString.append(column, QUALIFIER_STYLER); - styledString.append("), span=(", QUALIFIER_STYLER); + styledString.append("col=", colStyle); + styledString.append(column, colStyle); + styledString.append(')', colStyle); + styledString.append(", span=(", QUALIFIER_STYLER); styledString.append(columnSpan, QUALIFIER_STYLER); styledString.append(',', QUALIFIER_STYLER); styledString.append(rowSpan, QUALIFIER_STYLER); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java index 495600f..e2c573a 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java @@ -16,6 +16,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; import static com.android.ide.common.layout.LayoutConstants.FQCN_SPACE; +import static com.android.ide.common.layout.LayoutConstants.FQCN_SPACE_V7; import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionHandle.PIXEL_MARGIN; import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionHandle.PIXEL_RADIUS; @@ -874,7 +875,8 @@ public class SelectionManager implements ISelectionProvider { // Skip spacers - unless you're dropping just one continue; } - if (GridLayoutRule.sDebugGridLayout && viewInfo.getName().equals(FQCN_SPACE)) { + if (GridLayoutRule.sDebugGridLayout && (viewInfo.getName().equals(FQCN_SPACE) + || viewInfo.getName().equals(FQCN_SPACE_V7))) { // In debug mode they might not be marked as hidden but we never never // want to select these guys continue; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java index 7b860fe..7d21484 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java @@ -18,6 +18,8 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gre; import static com.android.sdklib.SdkConstants.CLASS_FRAGMENT; import static com.android.sdklib.SdkConstants.CLASS_V4_FRAGMENT; +import static com.android.tools.lint.detector.api.LintConstants.AUTO_URI; +import static com.android.tools.lint.detector.api.LintConstants.URI_PREFIX; import com.android.ide.common.api.IClientRulesEngine; import com.android.ide.common.api.INode; @@ -43,6 +45,7 @@ import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; import com.android.ide.eclipse.adt.internal.resources.CyclicDependencyValidator; import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; +import com.android.ide.eclipse.adt.internal.sdk.ProjectState; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.adt.internal.ui.MarginChooser; import com.android.ide.eclipse.adt.internal.ui.ReferenceChooserDialog; @@ -556,7 +559,14 @@ class ClientRulesEngine implements IClientRulesEngine { @Override public String getAppNameSpace() { - ManifestInfo info = ManifestInfo.get(mRulesEngine.getEditor().getProject()); - return info.getPackage(); + IProject project = mRulesEngine.getEditor().getProject(); + + ProjectState projectState = Sdk.getProjectState(project); + if (projectState != null && projectState.isLibrary()) { + return AUTO_URI; + } + + ManifestInfo info = ManifestInfo.get(project); + return URI_PREFIX + info.getPackage(); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java index 9c3af1d..a8438d8 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java @@ -368,9 +368,30 @@ public class RulesEngine { * @param targetNode The first node selected. * @param targetView The view object for the target node, or null if not known * @param pastedElements The elements being pasted. + * @return the parent node the paste was applied into */ - public void callOnPaste(NodeProxy targetNode, Object targetView, + public NodeProxy callOnPaste(NodeProxy targetNode, Object targetView, SimpleElement[] pastedElements) { + + // Find a target which accepts children. If you for example select a button + // and attempt to paste, this will reselect the parent of the button as the paste + // target. (This is a loop rather than just checking the direct parent since + // we will soon ask each child whether they are *willing* to accept the new child. + // A ScrollView for example, which only accepts one child, might also say no + // and delegate to its parent in turn. + INode parent = targetNode; + while (parent instanceof NodeProxy) { + NodeProxy np = (NodeProxy) parent; + if (np.getNode() != null && np.getNode().getDescriptor() != null) { + ElementDescriptor descriptor = np.getNode().getDescriptor(); + if (descriptor.hasChildren()) { + targetNode = np; + break; + } + } + parent = parent.getParent(); + } + // try to find a rule for this element's FQCN IViewRule rule = loadRule(targetNode.getNode()); @@ -385,6 +406,8 @@ public class RulesEngine { e.toString()); } } + + return targetNode; } // ---- Resize operations ---- diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/GridLayoutConverter.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/GridLayoutConverter.java index 912d216..daf1f54 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/GridLayoutConverter.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/GridLayoutConverter.java @@ -38,6 +38,7 @@ import static com.android.ide.common.layout.LayoutConstants.FQCN_GRID_LAYOUT; import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_FILL; import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_FILL_HORIZONTAL; import static com.android.ide.common.layout.LayoutConstants.GRAVITY_VALUE_FILL_VERTICAL; +import static com.android.ide.common.layout.LayoutConstants.GRID_LAYOUT; import static com.android.ide.common.layout.LayoutConstants.ID_PREFIX; import static com.android.ide.common.layout.LayoutConstants.LINEAR_LAYOUT; import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX; @@ -210,20 +211,26 @@ class GridLayoutConverter { // TODO: May also have to increment column count! int offset = 0; // WHERE? + String gridLayout = mLayout.getTagName(); if (mLayout instanceof IndexedRegion) { IndexedRegion region = (IndexedRegion) mLayout; int end = region.getEndOffset(); // TODO: Look backwards for the ") ? - end -= (mLayout.getTagName().length() + 3); // 3: <, /, > + end -= (gridLayout.length() + 3); // 3: <, /, > offset = end; } int row = rowFixed.size(); int column = columnFixed.size(); StringBuilder sb = new StringBuilder(64); - String tag = SPACE; - sb.append('<').append(tag).append(' '); + String spaceTag = SPACE; + if (!gridLayout.equals(GRID_LAYOUT) && gridLayout.length() > GRID_LAYOUT.length()) { + String pkg = gridLayout.substring(0, gridLayout.length() - GRID_LAYOUT.length()); + spaceTag = pkg + spaceTag; + } + + sb.append('<').append(spaceTag).append(' '); String gravity; if (!hasStretchableRow && !hasStretchableColumn) { gravity = GRAVITY_VALUE_FILL; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java index a25d07b..dfe38b0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java @@ -1725,15 +1725,17 @@ public class UiElementNode implements IPropertySource { /** * Returns the namespace prefix matching the requested namespace URI. - * If no such declaration is found, returns the default "android" prefix. + * If no such declaration is found, returns the default "android" prefix for + * the Android URI, and "app" for other URI's. * * @param node The current node. Must not be null. * @param nsUri The namespace URI of which the prefix is to be found, * e.g. SdkConstants.NS_RESOURCES - * @return The first prefix declared or the default "android" prefix. + * @return The first prefix declared or the default "android" prefix + * (or "app" for non-Android URIs) */ public static String lookupNamespacePrefix(Node node, String nsUri) { - String defaultPrefix = NS_RESOURCES.equals(nsUri) ? ANDROID_NS_NAME : "ns"; //$NON-NLS-1$ + String defaultPrefix = NS_RESOURCES.equals(nsUri) ? ANDROID_NS_NAME : "app"; //$NON-NLS-1$ return lookupNamespacePrefix(node, nsUri, defaultPrefix); } -- cgit v1.1