aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsListViewRule.java28
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java254
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AdapterViewRule.java62
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseLayoutRule.java878
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java996
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/CalendarViewRule.java44
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DatePickerRule.java22
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DialerFilterRule.java63
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/EditTextRule.java139
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FragmentRule.java46
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FrameLayoutRule.java195
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GravityHelper.java233
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridLayoutRule.java676
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridViewRule.java41
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java96
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/IgnoredLayoutRule.java46
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageButtonRule.java54
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageViewRule.java57
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/IncludeRule.java46
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java1092
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ListViewRule.java42
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MapViewRule.java46
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MergeRule.java38
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/PropertyCallback.java82
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/PropertySettingNodeHandler.java42
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/QuickContactBadgeRule.java34
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/RadioGroupRule.java50
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/RelativeLayoutRule.java413
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ResizeState.java131
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java96
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SeekBarRule.java42
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SlidingDrawerRule.java68
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabHostRule.java82
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabWidgetRule.java27
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java218
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableRowRule.java80
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TimePickerRule.java27
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ViewRule.java31
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ViewTagRule.java49
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/WebViewRule.java46
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ZoomButtonRule.java35
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ZoomControlsRule.java27
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/addcol.pngbin479 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/addrow.pngbin442 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/allweight.pngbin460 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/baseline.pngbin602 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/centerHorizontally.pngbin505 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/centerVertically.pngbin501 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/clearweights.pngbin573 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/constraints.pngbin507 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/distribute.pngbin460 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/fillheight.pngbin483 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/fillwidth.pngbin476 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/gravity.pngbin563 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridDropHandler.java840
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridLayoutPainter.java370
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridMatch.java154
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridModel.java2384
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/gridmode.pngbin519 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/hlinear.pngbin403 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/margins.pngbin503 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ConstraintPainter.java783
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ConstraintType.java241
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/DeletionHandler.java267
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/DependencyGraph.java326
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/GuidelineHandler.java839
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/GuidelinePainter.java208
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/Match.java100
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/MoveHandler.java299
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ResizeHandler.java265
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/removecol.pngbin469 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/removerow.pngbin442 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/showgrid.pngbin477 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/snap.pngbin575 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/structure.pngbin546 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/vlinear.pngbin369 -> 0 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/weights.pngbin520 -> 0 bytes
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttributeInfo.java355
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttrsXmlParser.java698
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/DeclareStyleableInfo.java102
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/ViewClassInfo.java158
81 files changed, 0 insertions, 15163 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsListViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsListViewRule.java
deleted file mode 100644
index cd1b0fcae..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsListViewRule.java
+++ /dev/null
@@ -1,28 +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 com.android.ide.common.api.IViewRule;
-
-/**
- * An {@link IViewRule} for android.widget.AbsListViewRule
- */
-public class AbsListViewRule extends IgnoredLayoutRule {
-
- // GridViews and ListViews are not configurable via XML
-
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java
deleted file mode 100644
index 3ec3b5f1a..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java
+++ /dev/null
@@ -1,254 +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_LAYOUT_X;
-import static com.android.SdkConstants.ATTR_LAYOUT_Y;
-import static com.android.SdkConstants.VALUE_N_DP;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IFeedbackPainter;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.INodeHandler;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.SegmentType;
-import com.android.utils.Pair;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * An {@link IViewRule} for android.widget.AbsoluteLayout and all its derived
- * classes.
- */
-public class AbsoluteLayoutRule extends BaseLayoutRule {
-
- @Override
- public List<String> getSelectionHint(@NonNull INode parentNode, @NonNull INode childNode) {
- List<String> infos = new ArrayList<String>(2);
- infos.add("AbsoluteLayout is deprecated.");
- infos.add("Use other layouts instead.");
- return infos;
- }
-
- // ==== Drag'n'drop support ====
- // The AbsoluteLayout accepts any drag'n'drop anywhere on its surface.
-
- @Override
- public DropFeedback onDropEnter(@NonNull INode targetNode, @Nullable Object targetView,
- final @Nullable IDragElement[] elements) {
-
- if (elements.length == 0) {
- return null;
- }
-
- DropFeedback df = new DropFeedback(null, new IFeedbackPainter() {
- @Override
- public void paint(@NonNull IGraphics gc, @NonNull INode node,
- @NonNull DropFeedback feedback) {
- // Paint callback for the AbsoluteLayout.
- // This is called by the canvas when a draw is needed.
- drawFeedback(gc, node, elements, feedback);
- }
- });
- df.errorMessage = "AbsoluteLayout is deprecated.";
- return df;
- }
-
- void drawFeedback(
- IGraphics gc,
- INode targetNode,
- IDragElement[] elements,
- DropFeedback feedback) {
- Rect b = targetNode.getBounds();
- if (!b.isValid()) {
- return;
- }
-
- // Highlight the receiver
- gc.useStyle(DrawingStyle.DROP_RECIPIENT);
- gc.drawRect(b);
-
- // Get the drop point
- Point p = (Point) feedback.userData;
-
- if (p == null) {
- return;
- }
-
- int x = p.x;
- int y = p.y;
-
- Rect be = elements[0].getBounds();
-
- if (be.isValid()) {
- // At least the first element has a bound. Draw rectangles
- // for all dropped elements with valid bounds, offset at
- // the drop point.
- int offsetX = x - be.x + (feedback.dragBounds != null ? feedback.dragBounds.x : 0);
- int offsetY = y - be.y + (feedback.dragBounds != null ? feedback.dragBounds.y : 0);
- gc.useStyle(DrawingStyle.DROP_PREVIEW);
- for (IDragElement element : elements) {
- drawElement(gc, element, offsetX, offsetY);
- }
- } else {
- // We don't have bounds for new elements. In this case
- // just draw cross hairs to the drop point.
- gc.useStyle(DrawingStyle.GUIDELINE);
- gc.drawLine(x, b.y, x, b.y + b.h);
- gc.drawLine(b.x, y, b.x + b.w, y);
-
- // Use preview lines to indicate the bottom quadrant as well (to
- // indicate that you are looking at the top left position of the
- // drop, not the center for example)
- gc.useStyle(DrawingStyle.DROP_PREVIEW);
- gc.drawLine(x, y, b.x + b.w, y);
- gc.drawLine(x, y, x, b.y + b.h);
- }
- }
-
- @Override
- public DropFeedback onDropMove(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback, @NonNull Point p) {
- // Update the data used by the DropFeedback.paintCallback above.
- feedback.userData = p;
- feedback.requestPaint = true;
-
- return feedback;
- }
-
- @Override
- public void onDropLeave(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback) {
- // Nothing to do.
- }
-
- @Override
- public void onDropped(final @NonNull INode targetNode, final @NonNull IDragElement[] elements,
- final @Nullable DropFeedback feedback, final @NonNull Point p) {
-
- final Rect b = targetNode.getBounds();
- if (!b.isValid()) {
- return;
- }
-
- // Collect IDs from dropped elements and remap them to new IDs
- // if this is a copy or from a different canvas.
- final Map<String, Pair<String, String>> idMap = getDropIdMap(targetNode, elements,
- feedback.isCopy || !feedback.sameCanvas);
-
- targetNode.editXml("Add elements to AbsoluteLayout", new INodeHandler() {
- @Override
- public void handle(@NonNull INode node) {
- boolean first = true;
- Point offset = null;
-
- // Now write the new elements.
- for (IDragElement element : elements) {
- String fqcn = element.getFqcn();
- Rect be = element.getBounds();
-
- INode newChild = targetNode.appendChild(fqcn);
-
- // Copy all the attributes, modifying them as needed.
- addAttributes(newChild, element, idMap, DEFAULT_ATTR_FILTER);
-
- int deltaX = (feedback.dragBounds != null ? feedback.dragBounds.x : 0);
- int deltaY = (feedback.dragBounds != null ? feedback.dragBounds.y : 0);
-
- int x = p.x - b.x + deltaX;
- int y = p.y - b.y + deltaY;
-
- if (first) {
- first = false;
- if (be.isValid()) {
- offset = new Point(x - be.x, y - be.y);
- }
- } else if (offset != null && be.isValid()) {
- x = offset.x + be.x;
- y = offset.y + be.y;
- } else {
- x += 10;
- y += be.isValid() ? be.h : 10;
- }
-
- double scale = feedback.dipScale;
- if (scale != 1.0) {
- x *= scale;
- y *= scale;
- }
-
- newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_X,
- String.format(VALUE_N_DP, x));
- newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_Y,
- String.format(VALUE_N_DP, y));
-
- addInnerElements(newChild, element, idMap);
- }
- }
- });
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * Overridden in this layout in order to let the top left coordinate be affected by
- * the resize operation too. In other words, dragging the top left corner to resize a
- * widget will not only change the size of the widget, it will also move it (though in
- * this case, the bottom right corner will stay fixed).
- */
- @Override
- protected void setNewSizeBounds(ResizeState resizeState, INode node, INode layout,
- Rect previousBounds, Rect newBounds, SegmentType horizontalEdge,
- SegmentType verticalEdge) {
- super.setNewSizeBounds(resizeState, node, layout, previousBounds, newBounds,
- horizontalEdge, verticalEdge);
- if (verticalEdge != null && newBounds.x != previousBounds.x) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_X,
- String.format(VALUE_N_DP,
- mRulesEngine.pxToDp(newBounds.x - node.getParent().getBounds().x)));
- }
- if (horizontalEdge != null && newBounds.y != previousBounds.y) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_Y,
- String.format(VALUE_N_DP,
- mRulesEngine.pxToDp(newBounds.y - node.getParent().getBounds().y)));
- }
- }
-
- @Override
- protected String getResizeUpdateMessage(ResizeState resizeState, INode child, INode parent,
- Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) {
- Rect parentBounds = parent.getBounds();
- if (horizontalEdge == SegmentType.BOTTOM && verticalEdge == SegmentType.RIGHT) {
- return super.getResizeUpdateMessage(resizeState, child, parent, newBounds,
- horizontalEdge, verticalEdge);
- }
- return String.format("x=%d, y=%d\nwidth=%s, height=%s",
- mRulesEngine.pxToDp(newBounds.x - parentBounds.x),
- mRulesEngine.pxToDp(newBounds.y - parentBounds.y),
- resizeState.getWidthAttribute(), resizeState.getHeightAttribute());
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AdapterViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AdapterViewRule.java
deleted file mode 100644
index 28f5fc95e..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AdapterViewRule.java
+++ /dev/null
@@ -1,62 +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 com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IFeedbackPainter;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-
-/** Rule for AdapterView subclasses that don't have more specific rules */
-public class AdapterViewRule extends BaseLayoutRule {
- @Override
- public DropFeedback onDropEnter(@NonNull INode targetNode, @Nullable Object targetView,
- @Nullable IDragElement[] elements) {
- // You are not allowed to insert children into AdapterViews; you must
- // use the dedicated addView methods etc dynamically
- DropFeedback dropFeedback = new DropFeedback(null, new IFeedbackPainter() {
- @Override
- public void paint(@NonNull IGraphics gc, @NonNull INode node,
- @NonNull DropFeedback feedback) {
- Rect b = node.getBounds();
- if (b.isValid()) {
- gc.useStyle(DrawingStyle.DROP_RECIPIENT);
- gc.drawRect(b);
- }
- }
- });
- String fqcn = targetNode.getFqcn();
- String name = fqcn.substring(fqcn.lastIndexOf('.') +1);
- dropFeedback.errorMessage = String.format(
- "%s cannot be configured via XML; add content to the AdapterView using Java code",
- name);
- dropFeedback.invalidTarget = true;
- return dropFeedback;
- }
-
- @Override
- public DropFeedback onDropMove(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback, @NonNull Point p) {
- feedback.invalidTarget = true;
- return feedback;
- }
-}
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
deleted file mode 100644
index df2c8f473..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseLayoutRule.java
+++ /dev/null
@@ -1,878 +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_ID;
-import static com.android.SdkConstants.ATTR_LAYOUT_ABOVE;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BASELINE;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BOTTOM;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_LEFT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_BOTTOM;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_LEFT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_RIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_TOP;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_RIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_TOP;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_WITH_PARENT_MISSING;
-import static com.android.SdkConstants.ATTR_LAYOUT_BELOW;
-import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_HORIZONTAL;
-import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_IN_PARENT;
-import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_VERTICAL;
-import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN;
-import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN_SPAN;
-import static com.android.SdkConstants.ATTR_LAYOUT_GRAVITY;
-import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN;
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_BOTTOM;
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_LEFT;
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_RIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_TOP;
-import static com.android.SdkConstants.ATTR_LAYOUT_ROW;
-import static com.android.SdkConstants.ATTR_LAYOUT_ROW_SPAN;
-import static com.android.SdkConstants.ATTR_LAYOUT_TO_LEFT_OF;
-import static com.android.SdkConstants.ATTR_LAYOUT_TO_RIGHT_OF;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-import static com.android.SdkConstants.ATTR_LAYOUT_X;
-import static com.android.SdkConstants.ATTR_LAYOUT_Y;
-import static com.android.SdkConstants.VALUE_FILL_PARENT;
-import static com.android.SdkConstants.VALUE_MATCH_PARENT;
-import static com.android.SdkConstants.VALUE_WRAP_CONTENT;
-
-import com.android.SdkConstants;
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IAttributeInfo;
-import com.android.ide.common.api.IClientRulesEngine;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IDragElement.IDragAttribute;
-import com.android.ide.common.api.IFeedbackPainter;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.IMenuCallback;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.INodeHandler;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.MarginType;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.RuleAction;
-import com.android.ide.common.api.RuleAction.ChoiceProvider;
-import com.android.ide.common.api.Segment;
-import com.android.ide.common.api.SegmentType;
-import com.android.utils.Pair;
-
-import java.net.URL;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A {@link IViewRule} for all layouts.
- */
-public class BaseLayoutRule extends BaseViewRule {
- private static final String ACTION_FILL_WIDTH = "_fillW"; //$NON-NLS-1$
- private static final String ACTION_FILL_HEIGHT = "_fillH"; //$NON-NLS-1$
- private static final String ACTION_MARGIN = "_margin"; //$NON-NLS-1$
- private static final URL ICON_MARGINS =
- BaseLayoutRule.class.getResource("margins.png"); //$NON-NLS-1$
- private static final URL ICON_GRAVITY =
- BaseLayoutRule.class.getResource("gravity.png"); //$NON-NLS-1$
- private static final URL ICON_FILL_WIDTH =
- BaseLayoutRule.class.getResource("fillwidth.png"); //$NON-NLS-1$
- private static final URL ICON_FILL_HEIGHT =
- BaseLayoutRule.class.getResource("fillheight.png"); //$NON-NLS-1$
-
- // ==== Layout Actions support ====
-
- // The Margin layout parameters are available for LinearLayout, FrameLayout, RelativeLayout,
- // and their subclasses.
- protected final RuleAction createMarginAction(final INode parentNode,
- final List<? extends INode> children) {
-
- final List<? extends INode> targets = children == null || children.size() == 0 ?
- Collections.singletonList(parentNode)
- : children;
- final INode first = targets.get(0);
-
- IMenuCallback actionCallback = new IMenuCallback() {
- @Override
- public void action(@NonNull RuleAction action,
- @NonNull List<? extends INode> selectedNodes,
- final @Nullable String valueId,
- final @Nullable Boolean newValue) {
- parentNode.editXml("Change Margins", new INodeHandler() {
- @Override
- public void handle(@NonNull INode n) {
- String uri = ANDROID_URI;
- String all = first.getStringAttr(uri, ATTR_LAYOUT_MARGIN);
- String left = first.getStringAttr(uri, ATTR_LAYOUT_MARGIN_LEFT);
- String right = first.getStringAttr(uri, ATTR_LAYOUT_MARGIN_RIGHT);
- String top = first.getStringAttr(uri, ATTR_LAYOUT_MARGIN_TOP);
- String bottom = first.getStringAttr(uri, ATTR_LAYOUT_MARGIN_BOTTOM);
- String[] margins = mRulesEngine.displayMarginInput(all, left,
- right, top, bottom);
- if (margins != null) {
- assert margins.length == 5;
- for (INode child : targets) {
- child.setAttribute(uri, ATTR_LAYOUT_MARGIN, margins[0]);
- child.setAttribute(uri, ATTR_LAYOUT_MARGIN_LEFT, margins[1]);
- child.setAttribute(uri, ATTR_LAYOUT_MARGIN_RIGHT, margins[2]);
- child.setAttribute(uri, ATTR_LAYOUT_MARGIN_TOP, margins[3]);
- child.setAttribute(uri, ATTR_LAYOUT_MARGIN_BOTTOM, margins[4]);
- }
- }
- }
- });
- }
- };
-
- return RuleAction.createAction(ACTION_MARGIN, "Change Margins...", actionCallback,
- ICON_MARGINS, 40, false);
- }
-
- // Both LinearLayout and RelativeLayout have a gravity (but RelativeLayout applies it
- // to the parent whereas for LinearLayout it's on the children)
- protected final RuleAction createGravityAction(final List<? extends INode> targets, final
- String attributeName) {
- if (targets != null && targets.size() > 0) {
- final INode first = targets.get(0);
- ChoiceProvider provider = new ChoiceProvider() {
- @Override
- public void addChoices(@NonNull List<String> titles, @NonNull List<URL> iconUrls,
- @NonNull List<String> ids) {
- IAttributeInfo info = first.getAttributeInfo(ANDROID_URI, attributeName);
- if (info != null) {
- // Generate list of possible gravity value constants
- assert info.getFormats().contains(IAttributeInfo.Format.FLAG);
- for (String name : info.getFlagValues()) {
- titles.add(getAttributeDisplayName(name));
- ids.add(name);
- }
- }
- }
- };
-
- return RuleAction.createChoices("_gravity", "Change Gravity", //$NON-NLS-1$
- new PropertyCallback(targets, "Change Gravity", ANDROID_URI,
- attributeName),
- provider,
- first.getStringAttr(ANDROID_URI, attributeName), ICON_GRAVITY,
- 43, false);
- }
-
- return null;
- }
-
- @Override
- public void addLayoutActions(
- @NonNull List<RuleAction> actions,
- final @NonNull INode parentNode,
- final @NonNull List<? extends INode> children) {
- super.addLayoutActions(actions, parentNode, children);
-
- final List<? extends INode> targets = children == null || children.size() == 0 ?
- Collections.singletonList(parentNode)
- : children;
- final INode first = targets.get(0);
-
- // Shared action callback
- IMenuCallback actionCallback = new IMenuCallback() {
- @Override
- public void action(
- @NonNull RuleAction action,
- @NonNull List<? extends INode> selectedNodes,
- final @Nullable String valueId,
- final @Nullable Boolean newValue) {
- final String actionId = action.getId();
- final String undoLabel;
- if (actionId.equals(ACTION_FILL_WIDTH)) {
- undoLabel = "Change Width Fill";
- } else if (actionId.equals(ACTION_FILL_HEIGHT)) {
- undoLabel = "Change Height Fill";
- } else {
- return;
- }
- parentNode.editXml(undoLabel, new INodeHandler() {
- @Override
- public void handle(@NonNull INode n) {
- String attribute = actionId.equals(ACTION_FILL_WIDTH)
- ? ATTR_LAYOUT_WIDTH : ATTR_LAYOUT_HEIGHT;
- String value;
- if (newValue) {
- if (supportsMatchParent()) {
- value = VALUE_MATCH_PARENT;
- } else {
- value = VALUE_FILL_PARENT;
- }
- } else {
- value = VALUE_WRAP_CONTENT;
- }
- for (INode child : targets) {
- child.setAttribute(ANDROID_URI, attribute, value);
- }
- }
- });
- }
- };
-
- actions.add(RuleAction.createToggle(ACTION_FILL_WIDTH, "Toggle Fill Width",
- isFilled(first, ATTR_LAYOUT_WIDTH), actionCallback, ICON_FILL_WIDTH, 10, false));
- actions.add(RuleAction.createToggle(ACTION_FILL_HEIGHT, "Toggle Fill Height",
- isFilled(first, ATTR_LAYOUT_HEIGHT), actionCallback, ICON_FILL_HEIGHT, 20, false));
- }
-
- // ==== Paste support ====
-
- /**
- * The default behavior for pasting in a layout is to simulate a drop in the
- * top-left corner of the view.
- * <p/>
- * Note that we explicitly do not call super() here -- the BaseViewRule.onPaste handler
- * will call onPasteBeforeChild() instead.
- * <p/>
- * Derived layouts should override this behavior if not appropriate.
- */
- @Override
- public void onPaste(@NonNull INode targetNode, @Nullable Object targetView,
- @NonNull IDragElement[] elements) {
- DropFeedback feedback = onDropEnter(targetNode, targetView, elements);
- if (feedback != null) {
- Point p = targetNode.getBounds().getTopLeft();
- feedback = onDropMove(targetNode, elements, feedback, p);
- if (feedback != null) {
- onDropLeave(targetNode, elements, feedback);
- onDropped(targetNode, elements, feedback, p);
- }
- }
- }
-
- /**
- * The default behavior for pasting in a layout with a specific child target
- * is to simulate a drop right above the top left of the given child target.
- * <p/>
- * This method is invoked by BaseView when onPaste() is called --
- * views don't generally accept children and instead use the target node as
- * a hint to paste "before" it.
- *
- * @param parentNode the parent node we're pasting into
- * @param parentView the view object for the parent layout, or null
- * @param targetNode the first selected node
- * @param elements the elements being pasted
- */
- public void onPasteBeforeChild(INode parentNode, Object parentView, INode targetNode,
- IDragElement[] elements) {
- DropFeedback feedback = onDropEnter(parentNode, parentView, elements);
- if (feedback != null) {
- Point parentP = parentNode.getBounds().getTopLeft();
- Point targetP = targetNode.getBounds().getTopLeft();
- if (parentP.y < targetP.y) {
- targetP.y -= 1;
- }
-
- feedback = onDropMove(parentNode, elements, feedback, targetP);
- if (feedback != null) {
- onDropLeave(parentNode, elements, feedback);
- onDropped(parentNode, elements, feedback, targetP);
- }
- }
- }
-
- // ==== Utility methods used by derived layouts ====
-
- /**
- * Draws the bounds of the given elements and all its children elements in the canvas
- * with the specified offset.
- *
- * @param gc the graphics context
- * @param element the element to be drawn
- * @param offsetX a horizontal delta to add to the current bounds of the element when
- * drawing it
- * @param offsetY a vertical delta to add to the current bounds of the element when
- * drawing it
- */
- public void drawElement(IGraphics gc, IDragElement element, int offsetX, int offsetY) {
- Rect b = element.getBounds();
- if (b.isValid()) {
- gc.drawRect(b.x + offsetX, b.y + offsetY, b.x + offsetX + b.w, b.y + offsetY + b.h);
- }
-
- for (IDragElement inner : element.getInnerElements()) {
- drawElement(gc, inner, offsetX, offsetY);
- }
- }
-
- /**
- * Collect all the "android:id" IDs from the dropped elements. When moving
- * objects within the same canvas, that's all there is to do. However if the
- * objects are moved to a different canvas or are copied then set
- * createNewIds to true to find the existing IDs under targetNode and create
- * a map with new non-conflicting unique IDs as needed. Returns a map String
- * old-id => tuple (String new-id, String fqcn) where fqcn is the FQCN of
- * the element.
- */
- protected static Map<String, Pair<String, String>> getDropIdMap(INode targetNode,
- IDragElement[] elements, boolean createNewIds) {
- Map<String, Pair<String, String>> idMap = new HashMap<String, Pair<String, String>>();
-
- if (createNewIds) {
- collectIds(idMap, elements);
- // Need to remap ids if necessary
- idMap = remapIds(targetNode, idMap);
- }
-
- return idMap;
- }
-
- /**
- * Fills idMap with a map String id => tuple (String id, String fqcn) where
- * fqcn is the FQCN of the element (in case we want to generate new IDs
- * based on the element type.)
- *
- * @see #getDropIdMap
- */
- protected static Map<String, Pair<String, String>> collectIds(
- Map<String, Pair<String, String>> idMap,
- IDragElement[] elements) {
- for (IDragElement element : elements) {
- IDragAttribute attr = element.getAttribute(ANDROID_URI, ATTR_ID);
- if (attr != null) {
- String id = attr.getValue();
- if (id != null && id.length() > 0) {
- idMap.put(id, Pair.of(id, element.getFqcn()));
- }
- }
-
- collectIds(idMap, element.getInnerElements());
- }
-
- return idMap;
- }
-
- /**
- * Used by #getDropIdMap to find new IDs in case of conflict.
- */
- protected static Map<String, Pair<String, String>> remapIds(INode node,
- Map<String, Pair<String, String>> idMap) {
- // Visit the document to get a list of existing ids
- Set<String> existingIdSet = new HashSet<String>();
- collectExistingIds(node.getRoot(), existingIdSet);
-
- Map<String, Pair<String, String>> new_map = new HashMap<String, Pair<String, String>>();
- for (Map.Entry<String, Pair<String, String>> entry : idMap.entrySet()) {
- String key = entry.getKey();
- Pair<String, String> value = entry.getValue();
-
- String id = normalizeId(key);
-
- if (!existingIdSet.contains(id)) {
- // Not a conflict. Use as-is.
- new_map.put(key, value);
- if (!key.equals(id)) {
- new_map.put(id, value);
- }
- } else {
- // There is a conflict. Get a new id.
- String new_id = findNewId(value.getSecond(), existingIdSet);
- value = Pair.of(new_id, value.getSecond());
- new_map.put(id, value);
- new_map.put(id.replaceFirst("@\\+", "@"), value); //$NON-NLS-1$ //$NON-NLS-2$
- }
- }
-
- return new_map;
- }
-
- /**
- * Used by #remapIds to find a new ID for a conflicting element.
- */
- protected static String findNewId(String fqcn, Set<String> existingIdSet) {
- // Get the last component of the FQCN (e.g. "android.view.Button" =>
- // "Button")
- String name = fqcn.substring(fqcn.lastIndexOf('.') + 1);
-
- for (int i = 1; i < 1000000; i++) {
- String id = String.format("@+id/%s%02d", name, i); //$NON-NLS-1$
- if (!existingIdSet.contains(id)) {
- existingIdSet.add(id);
- return id;
- }
- }
-
- // We'll never reach here.
- return null;
- }
-
- /**
- * Used by #getDropIdMap to find existing IDs recursively.
- */
- protected static void collectExistingIds(INode root, Set<String> existingIdSet) {
- if (root == null) {
- return;
- }
-
- String id = root.getStringAttr(ANDROID_URI, ATTR_ID);
- if (id != null) {
- id = normalizeId(id);
-
- if (!existingIdSet.contains(id)) {
- existingIdSet.add(id);
- }
- }
-
- for (INode child : root.getChildren()) {
- collectExistingIds(child, existingIdSet);
- }
- }
-
- /**
- * Transforms @id/name into @+id/name to treat both forms the same way.
- */
- protected static String normalizeId(String id) {
- if (id.indexOf("@+") == -1) { //$NON-NLS-1$
- id = id.replaceFirst("@", "@+"); //$NON-NLS-1$ //$NON-NLS-2$
- }
- return id;
- }
-
- /**
- * For use by {@link BaseLayoutRule#addAttributes} A filter should return a
- * valid replacement string.
- */
- protected static interface AttributeFilter {
- String replace(String attributeUri, String attributeName, String attributeValue);
- }
-
- private static final String[] EXCLUDED_ATTRIBUTES = new String[] {
- // Common
- ATTR_LAYOUT_GRAVITY,
-
- // from AbsoluteLayout
- ATTR_LAYOUT_X,
- ATTR_LAYOUT_Y,
-
- // from RelativeLayout
- ATTR_LAYOUT_ABOVE,
- ATTR_LAYOUT_BELOW,
- ATTR_LAYOUT_TO_LEFT_OF,
- ATTR_LAYOUT_TO_RIGHT_OF,
- ATTR_LAYOUT_ALIGN_BASELINE,
- ATTR_LAYOUT_ALIGN_TOP,
- ATTR_LAYOUT_ALIGN_BOTTOM,
- ATTR_LAYOUT_ALIGN_LEFT,
- ATTR_LAYOUT_ALIGN_RIGHT,
- ATTR_LAYOUT_ALIGN_PARENT_TOP,
- ATTR_LAYOUT_ALIGN_PARENT_BOTTOM,
- ATTR_LAYOUT_ALIGN_PARENT_LEFT,
- ATTR_LAYOUT_ALIGN_PARENT_RIGHT,
- ATTR_LAYOUT_ALIGN_WITH_PARENT_MISSING,
- ATTR_LAYOUT_CENTER_HORIZONTAL,
- ATTR_LAYOUT_CENTER_IN_PARENT,
- ATTR_LAYOUT_CENTER_VERTICAL,
-
- // From GridLayout
- ATTR_LAYOUT_ROW,
- ATTR_LAYOUT_ROW_SPAN,
- ATTR_LAYOUT_COLUMN,
- ATTR_LAYOUT_COLUMN_SPAN
- };
-
- /**
- * Default attribute filter used by the various layouts to filter out some properties
- * we don't want to offer.
- */
- public static final AttributeFilter DEFAULT_ATTR_FILTER = new AttributeFilter() {
- Set<String> mExcludes;
-
- @Override
- public String replace(String uri, String name, String value) {
- if (!ANDROID_URI.equals(uri)) {
- return value;
- }
-
- if (mExcludes == null) {
- mExcludes = new HashSet<String>(EXCLUDED_ATTRIBUTES.length);
- mExcludes.addAll(Arrays.asList(EXCLUDED_ATTRIBUTES));
- }
-
- return mExcludes.contains(name) ? null : value;
- }
- };
-
- /**
- * Copies all the attributes from oldElement to newNode. Uses the idMap to
- * transform the value of all attributes of Format.REFERENCE. If filter is
- * non-null, it's a filter that can rewrite the attribute string.
- */
- protected static void addAttributes(INode newNode, IDragElement oldElement,
- Map<String, Pair<String, String>> idMap, AttributeFilter filter) {
-
- for (IDragAttribute attr : oldElement.getAttributes()) {
- String uri = attr.getUri();
- String name = attr.getName();
- String value = attr.getValue();
-
- IAttributeInfo attrInfo = newNode.getAttributeInfo(uri, name);
- if (attrInfo != null) {
- if (attrInfo.getFormats().contains(IAttributeInfo.Format.REFERENCE)) {
- if (idMap.containsKey(value)) {
- value = idMap.get(value).getFirst();
- }
- }
- }
-
- if (filter != null) {
- value = filter.replace(uri, name, value);
- }
- if (value != null && value.length() > 0) {
- newNode.setAttribute(uri, name, value);
- }
- }
- }
-
- /**
- * Adds all the children elements of oldElement to newNode, recursively.
- * Attributes are adjusted by calling addAttributes with idMap as necessary,
- * with no closure filter.
- */
- protected static void addInnerElements(INode newNode, IDragElement oldElement,
- Map<String, Pair<String, String>> idMap) {
-
- for (IDragElement element : oldElement.getInnerElements()) {
- String fqcn = element.getFqcn();
- INode childNode = newNode.appendChild(fqcn);
-
- addAttributes(childNode, element, idMap, null /* filter */);
- addInnerElements(childNode, element, idMap);
- }
- }
-
- /**
- * Insert the given elements into the given node at the given position
- *
- * @param targetNode the node to insert into
- * @param elements the elements to insert
- * @param createNewIds if true, generate new ids when there is a conflict
- * @param initialInsertPos index among targetnode's children which to insert the
- * children
- */
- public static void insertAt(final INode targetNode, final IDragElement[] elements,
- final boolean createNewIds, final int initialInsertPos) {
-
- // Collect IDs from dropped elements and remap them to new IDs
- // if this is a copy or from a different canvas.
- final Map<String, Pair<String, String>> idMap = getDropIdMap(targetNode, elements,
- createNewIds);
-
- targetNode.editXml("Insert Elements", new INodeHandler() {
-
- @Override
- public void handle(@NonNull INode node) {
- // Now write the new elements.
- int insertPos = initialInsertPos;
- for (IDragElement element : elements) {
- String fqcn = element.getFqcn();
-
- INode newChild = targetNode.insertChildAt(fqcn, insertPos);
-
- // insertPos==-1 means to insert at the end. Otherwise
- // increment the insertion position.
- if (insertPos >= 0) {
- insertPos++;
- }
-
- // Copy all the attributes, modifying them as needed.
- addAttributes(newChild, element, idMap, DEFAULT_ATTR_FILTER);
- addInnerElements(newChild, element, idMap);
- }
- }
- });
- }
-
- // ---- Resizing ----
-
- /** Creates a new {@link ResizeState} object to track resize state */
- protected ResizeState createResizeState(INode layout, Object layoutView, INode node) {
- return new ResizeState(this, layout, layoutView, node);
- }
-
- @Override
- public DropFeedback onResizeBegin(@NonNull INode child, @NonNull INode parent,
- @Nullable SegmentType horizontalEdge, @Nullable SegmentType verticalEdge,
- @Nullable Object childView, @Nullable Object parentView) {
- ResizeState state = createResizeState(parent, parentView, child);
- state.horizontalEdgeType = horizontalEdge;
- state.verticalEdgeType = verticalEdge;
-
- // Compute preferred (wrap_content) size such that we can offer guidelines to
- // snap to the preferred size
- Map<INode, Rect> sizes = mRulesEngine.measureChildren(parent,
- new IClientRulesEngine.AttributeFilter() {
- @Override
- public String getAttribute(@NonNull INode node, @Nullable String namespace,
- @NonNull String localName) {
- // Change attributes to wrap_content
- if (ATTR_LAYOUT_WIDTH.equals(localName)
- && SdkConstants.NS_RESOURCES.equals(namespace)) {
- return VALUE_WRAP_CONTENT;
- }
- if (ATTR_LAYOUT_HEIGHT.equals(localName)
- && SdkConstants.NS_RESOURCES.equals(namespace)) {
- return VALUE_WRAP_CONTENT;
- }
-
- return null;
- }
- });
- if (sizes != null) {
- state.wrapBounds = sizes.get(child);
- }
-
- return new DropFeedback(state, new IFeedbackPainter() {
- @Override
- public void paint(@NonNull IGraphics gc, @NonNull INode node,
- @NonNull DropFeedback feedback) {
- ResizeState resizeState = (ResizeState) feedback.userData;
- if (resizeState != null && resizeState.bounds != null) {
- paintResizeFeedback(gc, node, resizeState);
- }
- }
- });
- }
-
- protected void paintResizeFeedback(IGraphics gc, INode node, ResizeState resizeState) {
- gc.useStyle(DrawingStyle.RESIZE_PREVIEW);
- Rect b = resizeState.bounds;
- gc.drawRect(b);
-
- if (resizeState.horizontalFillSegment != null) {
- gc.useStyle(DrawingStyle.GUIDELINE);
- Segment s = resizeState.horizontalFillSegment;
- gc.drawLine(s.from, s.at, s.to, s.at);
- }
- if (resizeState.verticalFillSegment != null) {
- gc.useStyle(DrawingStyle.GUIDELINE);
- Segment s = resizeState.verticalFillSegment;
- gc.drawLine(s.at, s.from, s.at, s.to);
- }
-
- if (resizeState.wrapBounds != null) {
- gc.useStyle(DrawingStyle.GUIDELINE);
- int wrapWidth = resizeState.wrapBounds.w;
- int wrapHeight = resizeState.wrapBounds.h;
-
- // Show the "wrap_content" guideline.
- // If we are showing both the wrap_width and wrap_height lines
- // then we show at most the rectangle formed by the two lines;
- // otherwise we show the entire width of the line
- if (resizeState.horizontalEdgeType != null) {
- int y = -1;
- switch (resizeState.horizontalEdgeType) {
- case TOP:
- y = b.y + b.h - wrapHeight;
- break;
- case BOTTOM:
- y = b.y + wrapHeight;
- break;
- default: assert false : resizeState.horizontalEdgeType;
- }
- if (resizeState.verticalEdgeType != null) {
- switch (resizeState.verticalEdgeType) {
- case LEFT:
- gc.drawLine(b.x + b.w - wrapWidth, y, b.x + b.w, y);
- break;
- case RIGHT:
- gc.drawLine(b.x, y, b.x + wrapWidth, y);
- break;
- default: assert false : resizeState.verticalEdgeType;
- }
- } else {
- gc.drawLine(b.x, y, b.x + b.w, y);
- }
- }
- if (resizeState.verticalEdgeType != null) {
- int x = -1;
- switch (resizeState.verticalEdgeType) {
- case LEFT:
- x = b.x + b.w - wrapWidth;
- break;
- case RIGHT:
- x = b.x + wrapWidth;
- break;
- default: assert false : resizeState.verticalEdgeType;
- }
- if (resizeState.horizontalEdgeType != null) {
- switch (resizeState.horizontalEdgeType) {
- case TOP:
- gc.drawLine(x, b.y + b.h - wrapHeight, x, b.y + b.h);
- break;
- case BOTTOM:
- gc.drawLine(x, b.y, x, b.y + wrapHeight);
- break;
- default: assert false : resizeState.horizontalEdgeType;
- }
- } else {
- gc.drawLine(x, b.y, x, b.y + b.h);
- }
- }
- }
- }
-
- /**
- * Returns the maximum number of pixels will be considered a "match" when snapping
- * resize or move positions to edges or other constraints
- *
- * @return the maximum number of pixels to consider for snapping
- */
- public static final int getMaxMatchDistance() {
- // TODO - make constant once we're happy with the feel
- return 20;
- }
-
- @Override
- public void onResizeUpdate(@Nullable DropFeedback feedback, @NonNull INode child,
- @NonNull INode parent, @NonNull Rect newBounds, int modifierMask) {
- ResizeState state = (ResizeState) feedback.userData;
- state.bounds = newBounds;
- state.modifierMask = modifierMask;
-
- // Match on wrap bounds
- state.wrapWidth = state.wrapHeight = false;
- if (state.wrapBounds != null) {
- Rect b = state.wrapBounds;
- int maxMatchDistance = getMaxMatchDistance();
- if (state.horizontalEdgeType != null) {
- if (Math.abs(newBounds.h - b.h) < maxMatchDistance) {
- state.wrapHeight = true;
- if (state.horizontalEdgeType == SegmentType.TOP) {
- newBounds.y += newBounds.h - b.h;
- }
- newBounds.h = b.h;
- }
- }
- if (state.verticalEdgeType != null) {
- if (Math.abs(newBounds.w - b.w) < maxMatchDistance) {
- state.wrapWidth = true;
- if (state.verticalEdgeType == SegmentType.LEFT) {
- newBounds.x += newBounds.w - b.w;
- }
- newBounds.w = b.w;
- }
- }
- }
-
- // Match on fill bounds
- state.horizontalFillSegment = null;
- state.fillHeight = false;
- if (state.horizontalEdgeType == SegmentType.BOTTOM && !state.wrapHeight) {
- Rect parentBounds = parent.getBounds();
- state.horizontalFillSegment = new Segment(parentBounds.y2(), newBounds.x,
- newBounds.x2(),
- null /*node*/, null /*id*/, SegmentType.BOTTOM, MarginType.NO_MARGIN);
- if (Math.abs(newBounds.y2() - parentBounds.y2()) < getMaxMatchDistance()) {
- state.fillHeight = true;
- newBounds.h = parentBounds.y2() - newBounds.y;
- }
- }
- state.verticalFillSegment = null;
- state.fillWidth = false;
- if (state.verticalEdgeType == SegmentType.RIGHT && !state.wrapWidth) {
- Rect parentBounds = parent.getBounds();
- state.verticalFillSegment = new Segment(parentBounds.x2(), newBounds.y,
- newBounds.y2(),
- null /*node*/, null /*id*/, SegmentType.RIGHT, MarginType.NO_MARGIN);
- if (Math.abs(newBounds.x2() - parentBounds.x2()) < getMaxMatchDistance()) {
- state.fillWidth = true;
- newBounds.w = parentBounds.x2() - newBounds.x;
- }
- }
-
- feedback.tooltip = getResizeUpdateMessage(state, child, parent,
- newBounds, state.horizontalEdgeType, state.verticalEdgeType);
- }
-
- @Override
- public void onResizeEnd(@Nullable DropFeedback feedback, @NonNull INode child,
- final @NonNull INode parent, final @NonNull Rect newBounds) {
- final Rect oldBounds = child.getBounds();
- if (oldBounds.w != newBounds.w || oldBounds.h != newBounds.h) {
- final ResizeState state = (ResizeState) feedback.userData;
- child.editXml("Resize", new INodeHandler() {
- @Override
- public void handle(@NonNull INode n) {
- setNewSizeBounds(state, n, parent, oldBounds, newBounds,
- state.horizontalEdgeType, state.verticalEdgeType);
- }
- });
- }
- }
-
- /**
- * Returns the message to display to the user during the resize operation
- *
- * @param resizeState the current resize state
- * @param child the child node being resized
- * @param parent the parent of the resized node
- * @param newBounds the new bounds to resize the child to, in pixels
- * @param horizontalEdge the horizontal edge being resized
- * @param verticalEdge the vertical edge being resized
- * @return the message to display for the current resize bounds
- */
- protected String getResizeUpdateMessage(ResizeState resizeState, INode child, INode parent,
- Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) {
- String width = resizeState.getWidthAttribute();
- String height = resizeState.getHeightAttribute();
-
- if (horizontalEdge == null) {
- return width;
- } else if (verticalEdge == null) {
- return height;
- } else {
- // U+00D7: Unicode for multiplication sign
- return String.format("%s \u00D7 %s", width, height);
- }
- }
-
- /**
- * Performs the edit on the node to complete a resizing operation. The actual edit
- * part is pulled out such that subclasses can change/add to the edits and be part of
- * the same undo event
- *
- * @param resizeState the current resize state
- * @param node the child node being resized
- * @param layout the parent of the resized node
- * @param newBounds the new bounds to resize the child to, in pixels
- * @param horizontalEdge the horizontal edge being resized
- * @param verticalEdge the vertical edge being resized
- */
- protected void setNewSizeBounds(ResizeState resizeState, INode node, INode layout,
- Rect oldBounds, Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) {
- if (verticalEdge != null
- && (newBounds.w != oldBounds.w || resizeState.wrapWidth || resizeState.fillWidth)) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, resizeState.getWidthAttribute());
- }
- if (horizontalEdge != null
- && (newBounds.h != oldBounds.h || resizeState.wrapHeight || resizeState.fillHeight)) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, resizeState.getHeightAttribute());
- }
- }
-}
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;
- }
- }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/CalendarViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/CalendarViewRule.java
deleted file mode 100644
index 91684e2c5..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/CalendarViewRule.java
+++ /dev/null
@@ -1,44 +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_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for android.widget.CalendarView.
- */
-public class CalendarViewRule extends BaseViewRule {
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- // CalendarViews need a lot of space, and the wrapping doesn't seem to work
- // well anyway; it reports a much-to-small size than actually accommodates its
- // content.
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, getFillParentValueName());
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, getFillParentValueName());
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DatePickerRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DatePickerRule.java
deleted file mode 100644
index a635a9ad6..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DatePickerRule.java
+++ /dev/null
@@ -1,22 +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;
-
-public class DatePickerRule extends IgnoredLayoutRule {
- // A DatePicker inherits from FrameLayout but is not a general purpose
- // FrameLayout
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DialerFilterRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DialerFilterRule.java
deleted file mode 100644
index 606bbd86c..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DialerFilterRule.java
+++ /dev/null
@@ -1,63 +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_ID;
-import static com.android.SdkConstants.ATTR_LAYOUT_BELOW;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-import static com.android.SdkConstants.ATTR_TEXT;
-import static com.android.SdkConstants.FQCN_EDIT_TEXT;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for android.widget.DialerFilterRule.
- */
-public class DialerFilterRule extends BaseViewRule {
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- // A DialerFilter requires a couple of nested EditTexts with fixed ids:
- if (insertType.isCreate()) {
- String fillParent = getFillParentValueName();
- INode hint = node.appendChild(FQCN_EDIT_TEXT);
- hint.setAttribute(ANDROID_URI, ATTR_TEXT, "Hint");
- hint.setAttribute(ANDROID_URI, ATTR_ID, "@android:id/hint"); //$NON-NLS-1$
- hint.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
-
- INode primary = node.appendChild(FQCN_EDIT_TEXT);
- primary.setAttribute(ANDROID_URI, ATTR_TEXT, "Primary");
- primary.setAttribute(ANDROID_URI, ATTR_ID, "@android:id/primary"); //$NON-NLS-1$
- primary.setAttribute(ANDROID_URI, ATTR_LAYOUT_BELOW,
- "@android:id/hint"); //$NON-NLS-1$
- primary.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
-
-
- // What do we initialize the icon to?
- //INode icon = node.appendChild("android.widget.ImageView"); //$NON-NLS-1$
- //icon.setAttribute(ANDROID_URI, ATTR_ID, "@android:id/icon"); //$NON-NLS-1$
- }
- }
-
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/EditTextRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/EditTextRule.java
deleted file mode 100644
index 03a5bc04e..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/EditTextRule.java
+++ /dev/null
@@ -1,139 +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_EMS;
-import static com.android.SdkConstants.REQUEST_FOCUS;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.IMenuCallback;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.INodeHandler;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.RuleAction;
-
-import java.util.List;
-
-/**
- * An {@link IViewRule} for android.widget.EditText.
- */
-public class EditTextRule extends BaseViewRule {
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- if (parent != null) {
- INode focus = findFocus(findRoot(parent));
- if (focus == null) {
- // Add <requestFocus>
- node.appendChild(REQUEST_FOCUS);
- }
-
- if (parent.getBounds().w >= 320) {
- node.setAttribute(ANDROID_URI, ATTR_EMS, "10"); //$NON-NLS-1$
- }
- }
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * Adds a "Request Focus" menu item.
- */
- @Override
- public void addContextMenuActions(@NonNull List<RuleAction> actions,
- final @NonNull INode selectedNode) {
- super.addContextMenuActions(actions, selectedNode);
-
- final boolean hasFocus = hasFocus(selectedNode);
- final String label = hasFocus ? "Clear Focus" : "Request Focus";
-
- IMenuCallback onChange = new IMenuCallback() {
- @Override
- public void action(
- @NonNull RuleAction menuAction,
- @NonNull List<? extends INode> selectedNodes,
- @Nullable String valueId,
- @Nullable Boolean newValue) {
- selectedNode.editXml(label, new INodeHandler() {
- @Override
- public void handle(@NonNull INode node) {
- INode focus = findFocus(findRoot(node));
- if (focus != null && focus.getParent() != null) {
- focus.getParent().removeChild(focus);
- }
- if (!hasFocus) {
- node.appendChild(REQUEST_FOCUS);
- }
- }
- });
- }
- };
-
- actions.add(RuleAction.createAction("_setfocus", label, onChange, //$NON-NLS-1$
- null, 5, false /*supportsMultipleNodes*/));
- actions.add(RuleAction.createSeparator(7));
- }
-
- /** Returns true if the given node currently has focus */
- private static boolean hasFocus(INode node) {
- INode focus = findFocus(node);
- if (focus != null) {
- return focus.getParent() == node;
- }
-
- return false;
- }
-
- /** Returns the root/top level node in the view hierarchy that contains the given node */
- private static INode findRoot(INode node) {
- // First find the parent
- INode root = node;
- while (root != null) {
- INode parent = root.getParent();
- if (parent == null) {
- break;
- } else {
- root = parent;
- }
- }
-
- return root;
- }
-
- /** Finds the focus node (not the node containing focus, but the actual request focus node
- * under a given node */
- private static INode findFocus(INode node) {
- if (node.getFqcn().equals(REQUEST_FOCUS)) {
- return node;
- }
-
- for (INode child : node.getChildren()) {
- INode focus = findFocus(child);
- if (focus != null) {
- return focus;
- }
- }
- return null;
- }
-
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FragmentRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FragmentRule.java
deleted file mode 100644
index f99cf0ceb..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FragmentRule.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2011 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_NAME;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for the special XML {@code <fragment>} tag.
- */
-public class FragmentRule extends BaseViewRule {
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- // When dropping a fragment tag, ask the user which class to use.
- if (insertType == InsertType.CREATE) { // NOT InsertType.CREATE_PREVIEW
- String fqcn = mRulesEngine.displayFragmentSourceInput();
- if (fqcn != null) {
- node.editXml("Add Fragment",
- new PropertySettingNodeHandler(ANDROID_URI, ATTR_NAME,
- fqcn.length() > 0 ? fqcn : null));
- } else {
- // Remove the view; the insertion was canceled
- parent.removeChild(node);
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FrameLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FrameLayoutRule.java
deleted file mode 100644
index 0f9096294..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FrameLayoutRule.java
+++ /dev/null
@@ -1,195 +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_LAYOUT_GRAVITY;
-import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IFeedbackPainter;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.INodeHandler;
-import com.android.ide.common.api.IViewMetadata;
-import com.android.ide.common.api.IViewMetadata.FillPreference;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.RuleAction;
-import com.android.utils.Pair;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * An {@link IViewRule} for android.widget.FrameLayout and all its derived
- * classes.
- */
-public class FrameLayoutRule extends BaseLayoutRule {
-
- // ==== Drag'n'drop support ====
- // The FrameLayout accepts any drag'n'drop anywhere on its surface.
-
- @Override
- public DropFeedback onDropEnter(@NonNull INode targetNode, @Nullable Object targetView,
- final @Nullable IDragElement[] elements) {
- if (elements.length == 0) {
- return null;
- }
-
- return new DropFeedback(null, new IFeedbackPainter() {
- @Override
- public void paint(@NonNull IGraphics gc, @NonNull INode node,
- @NonNull DropFeedback feedback) {
- drawFeedback(gc, node, elements, feedback);
- }
- });
- }
-
- protected void drawFeedback(
- IGraphics gc,
- INode targetNode,
- IDragElement[] elements,
- DropFeedback feedback) {
- Rect b = targetNode.getBounds();
- if (!b.isValid()) {
- return;
- }
-
- gc.useStyle(DrawingStyle.DROP_RECIPIENT);
- gc.drawRect(b);
-
- // Get the drop point
- Point p = (Point) feedback.userData;
-
- if (p == null) {
- return;
- }
-
- Rect be = elements[0].getBounds();
-
- gc.useStyle(DrawingStyle.DROP_PREVIEW);
- if (be.isValid()) {
- // At least the first element has a bound. Draw rectangles
- // for all dropped elements with valid bounds, offset at
- // (0,0)
- for (IDragElement it : elements) {
- Rect currBounds = it.getBounds();
- if (currBounds.isValid()) {
- int offsetX = b.x - currBounds.x;
- int offsetY = b.y - currBounds.y;
- drawElement(gc, it, offsetX, offsetY);
- }
- }
- } else {
- // We don't have bounds for new elements. In this case
- // just draw insert lines indicating the top left corner where
- // the item will be placed
-
- // +1: Place lines fully within the view (the stroke width is 2) to
- // make
- // it even more visually obvious
- gc.drawLine(b.x + 1, b.y, b.x + 1, b.y + b.h);
- gc.drawLine(b.x, b.y + 1, b.x + b.w, b.y + 1);
- }
- }
-
- @Override
- public DropFeedback onDropMove(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback, @NonNull Point p) {
- feedback.userData = p;
- feedback.requestPaint = true;
- return feedback;
- }
-
- @Override
- public void onDropLeave(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback) {
- // ignore
- }
-
- @Override
- public void onDropped(final @NonNull INode targetNode, final @NonNull IDragElement[] elements,
- final @Nullable DropFeedback feedback, final @NonNull Point p) {
- Rect b = targetNode.getBounds();
- if (!b.isValid()) {
- return;
- }
-
- // Collect IDs from dropped elements and remap them to new IDs
- // if this is a copy or from a different canvas.
- final Map<String, Pair<String, String>> idMap = getDropIdMap(targetNode, elements,
- feedback.isCopy || !feedback.sameCanvas);
-
- targetNode.editXml("Add elements to FrameLayout", new INodeHandler() {
-
- @Override
- public void handle(@NonNull INode node) {
-
- // Now write the new elements.
- for (IDragElement element : elements) {
- String fqcn = element.getFqcn();
-
- INode newChild = targetNode.appendChild(fqcn);
-
- // Copy all the attributes, modifying them as needed.
- addAttributes(newChild, element, idMap, DEFAULT_ATTR_FILTER);
-
- addInnerElements(newChild, element, idMap);
- }
- }
- });
- }
-
- @Override
- public void addLayoutActions(
- @NonNull List<RuleAction> actions,
- final @NonNull INode parentNode,
- final @NonNull List<? extends INode> children) {
- super.addLayoutActions(actions, parentNode, children);
- actions.add(RuleAction.createSeparator(25));
- actions.add(createMarginAction(parentNode, children));
- if (children != null && children.size() > 0) {
- actions.add(createGravityAction(children, ATTR_LAYOUT_GRAVITY));
- }
- }
-
- @Override
- public void onChildInserted(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- // Look at the fill preferences and fill embedded layouts etc
- String fqcn = node.getFqcn();
- IViewMetadata metadata = mRulesEngine.getMetadata(fqcn);
- if (metadata != null) {
- FillPreference fill = metadata.getFillPreference();
- String fillParent = getFillParentValueName();
- if (fill.fillHorizontally(true)) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
- }
- if (fill.fillVertically(false)) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, fillParent);
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GravityHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GravityHelper.java
deleted file mode 100644
index b9aabad3f..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GravityHelper.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2011 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_LAYOUT_GRAVITY;
-import static com.android.SdkConstants.GRAVITY_VALUE_BOTTOM;
-import static com.android.SdkConstants.GRAVITY_VALUE_CENTER;
-import static com.android.SdkConstants.GRAVITY_VALUE_CENTER_HORIZONTAL;
-import static com.android.SdkConstants.GRAVITY_VALUE_CENTER_VERTICAL;
-import static com.android.SdkConstants.GRAVITY_VALUE_FILL;
-import static com.android.SdkConstants.GRAVITY_VALUE_FILL_HORIZONTAL;
-import static com.android.SdkConstants.GRAVITY_VALUE_FILL_VERTICAL;
-import static com.android.SdkConstants.GRAVITY_VALUE_LEFT;
-import static com.android.SdkConstants.GRAVITY_VALUE_RIGHT;
-import static com.android.SdkConstants.GRAVITY_VALUE_TOP;
-
-import org.w3c.dom.Element;
-
-/** Helper class for looking up the gravity masks of gravity attributes */
-public class GravityHelper {
- // From SDK constants; temporary
- public static final String GRAVITY_VALUE_START = "start"; //$NON-NLS-1$
- public static final String GRAVITY_VALUE_END = "end"; //$NON-NLS-1$
-
- /** Bitmask for a gravity which includes left */
- @SuppressWarnings("PointlessBitwiseExpression") // for symmetry with other fields
- public static final int GRAVITY_LEFT = 1 << 0;
-
- /** Bitmask for a gravity which includes right */
- public static final int GRAVITY_RIGHT = 1 << 1;
-
- /** Bitmask for a gravity which includes center horizontal */
- public static final int GRAVITY_CENTER_HORIZ = 1 << 2;
-
- /** Bitmask for a gravity which includes fill horizontal */
- public static final int GRAVITY_FILL_HORIZ = 1 << 3;
-
- /** Bitmask for a gravity which includes center vertical */
- public static final int GRAVITY_CENTER_VERT = 1 << 4;
-
- /** Bitmask for a gravity which includes fill vertical */
- public static final int GRAVITY_FILL_VERT = 1 << 5;
-
- /** Bitmask for a gravity which includes top */
- public static final int GRAVITY_TOP = 1 << 6;
-
- /** Bitmask for a gravity which includes bottom */
- public static final int GRAVITY_BOTTOM = 1 << 7;
-
- /** Bitmask for a gravity which includes start */
- public static final int GRAVITY_START = 1 << 8;
-
- /** Bitmask for a gravity which includes end */
- public static final int GRAVITY_END = 1 << 9;
-
- /** Bitmask for a gravity which includes any horizontal constraint */
- public static final int GRAVITY_HORIZ_MASK = GRAVITY_CENTER_HORIZ | GRAVITY_FILL_HORIZ
- | GRAVITY_LEFT | GRAVITY_RIGHT | GRAVITY_START | GRAVITY_END;
-
- /** Bitmask for a gravity which any vertical constraint */
- public static final int GRAVITY_VERT_MASK = GRAVITY_CENTER_VERT | GRAVITY_FILL_VERT
- | GRAVITY_TOP | GRAVITY_BOTTOM;
-
- /**
- * Returns the gravity of the given element
- *
- * @param element the element to look up the gravity for
- * @return a bit mask corresponding to the selected gravities
- */
- public static int getGravity(Element element) {
- String gravityString = element.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_GRAVITY);
- return getGravity(gravityString, GRAVITY_LEFT | GRAVITY_TOP);
- }
-
- /**
- * Returns the gravity bitmask for the given gravity string description
- *
- * @param gravityString the gravity string description
- * @param defaultMask the default/initial bitmask to start with
- * @return a bitmask corresponding to the gravity description
- */
- public static int getGravity(String gravityString, int defaultMask) {
- int gravity = defaultMask;
- if (gravityString != null && !gravityString.isEmpty()) {
- String[] anchors = gravityString.split("\\|"); //$NON-NLS-1$
- for (String anchor : anchors) {
- if (GRAVITY_VALUE_CENTER.equals(anchor)) {
- gravity = GRAVITY_CENTER_HORIZ | GRAVITY_CENTER_VERT;
- } else if (GRAVITY_VALUE_FILL.equals(anchor)) {
- gravity = GRAVITY_FILL_HORIZ | GRAVITY_FILL_VERT;
- } else if (GRAVITY_VALUE_CENTER_VERTICAL.equals(anchor)) {
- gravity = (gravity & GRAVITY_HORIZ_MASK) | GRAVITY_CENTER_VERT;
- } else if (GRAVITY_VALUE_CENTER_HORIZONTAL.equals(anchor)) {
- gravity = (gravity & GRAVITY_VERT_MASK) | GRAVITY_CENTER_HORIZ;
- } else if (GRAVITY_VALUE_FILL_VERTICAL.equals(anchor)) {
- gravity = (gravity & GRAVITY_HORIZ_MASK) | GRAVITY_FILL_VERT;
- } else if (GRAVITY_VALUE_FILL_HORIZONTAL.equals(anchor)) {
- gravity = (gravity & GRAVITY_VERT_MASK) | GRAVITY_FILL_HORIZ;
- } else if (GRAVITY_VALUE_TOP.equals(anchor)) {
- gravity = (gravity & GRAVITY_HORIZ_MASK) | GRAVITY_TOP;
- } else if (GRAVITY_VALUE_BOTTOM.equals(anchor)) {
- gravity = (gravity & GRAVITY_HORIZ_MASK) | GRAVITY_BOTTOM;
- } else if (GRAVITY_VALUE_LEFT.equals(anchor)) {
- gravity = (gravity & (GRAVITY_VERT_MASK|GRAVITY_START)) | GRAVITY_LEFT;
- } else if (GRAVITY_VALUE_RIGHT.equals(anchor)) {
- gravity = (gravity & (GRAVITY_VERT_MASK|GRAVITY_END)) | GRAVITY_RIGHT;
- } else if (GRAVITY_VALUE_START.equals(anchor)) {
- gravity = (gravity & (GRAVITY_VERT_MASK|GRAVITY_LEFT)) | GRAVITY_START;
- } else if (GRAVITY_VALUE_END.equals(anchor)) {
- gravity = (gravity & (GRAVITY_VERT_MASK|GRAVITY_RIGHT)) | GRAVITY_END;
- } // else: "clip" not supported
- }
- }
-
- return gravity;
- }
-
- /**
- * Returns true if the given gravity bitmask is constrained horizontally
- *
- * @param gravity the gravity bitmask
- * @return true if the given gravity bitmask is constrained horizontally
- */
- public static boolean isConstrainedHorizontally(int gravity) {
- return (gravity & GRAVITY_HORIZ_MASK) != 0;
- }
-
- /**
- * Returns true if the given gravity bitmask is constrained vertically
- *
- * @param gravity the gravity bitmask
- * @return true if the given gravity bitmask is constrained vertically
- */
- public static boolean isConstrainedVertically(int gravity) {
- return (gravity & GRAVITY_VERT_MASK) != 0;
- }
-
- /**
- * Returns true if the given gravity bitmask is left aligned
- *
- * @param gravity the gravity bitmask
- * @return true if the given gravity bitmask is left aligned
- */
- public static boolean isLeftAligned(int gravity) {
- return (gravity & (GRAVITY_LEFT|GRAVITY_START)) != 0;
- }
-
- /**
- * Returns true if the given gravity bitmask is top aligned
- *
- * @param gravity the gravity bitmask
- * @return true if the given gravity bitmask is aligned
- */
- public static boolean isTopAligned(int gravity) {
- return (gravity & GRAVITY_TOP) != 0;
- }
-
- /** Returns a gravity value string from the given gravity bitmask
- *
- * @param gravity the gravity bitmask
- * @return the corresponding gravity string suitable as an XML attribute value
- */
- public static String getGravity(int gravity) {
- if (gravity == 0) {
- return "";
- }
-
- if ((gravity & (GRAVITY_CENTER_HORIZ | GRAVITY_CENTER_VERT)) ==
- (GRAVITY_CENTER_HORIZ | GRAVITY_CENTER_VERT)) {
- return GRAVITY_VALUE_CENTER;
- }
-
- StringBuilder sb = new StringBuilder(30);
- int horizontal = gravity & GRAVITY_HORIZ_MASK;
- int vertical = gravity & GRAVITY_VERT_MASK;
-
- if ((horizontal & (GRAVITY_LEFT|GRAVITY_START)) != 0) {
- if ((horizontal & GRAVITY_LEFT) != 0) {
- sb.append(GRAVITY_VALUE_LEFT);
- }
- if ((horizontal & GRAVITY_START) != 0) {
- if (sb.length() > 0) {
- sb.append('|');
- }
- sb.append(GRAVITY_VALUE_START);
- }
- } else if ((horizontal & (GRAVITY_RIGHT|GRAVITY_END)) != 0) {
- if ((horizontal & GRAVITY_RIGHT) != 0) {
- sb.append(GRAVITY_VALUE_RIGHT);
- }
- if ((horizontal & GRAVITY_END) != 0) {
- if (sb.length() > 0) {
- sb.append('|');
- }
- sb.append(GRAVITY_VALUE_END);
- }
- } else if ((horizontal & GRAVITY_CENTER_HORIZ) != 0) {
- sb.append(GRAVITY_VALUE_CENTER_HORIZONTAL);
- } else if ((horizontal & GRAVITY_FILL_HORIZ) != 0) {
- sb.append(GRAVITY_VALUE_FILL_HORIZONTAL);
- }
-
- if (sb.length() > 0 && vertical != 0) {
- sb.append('|');
- }
-
- if ((vertical & GRAVITY_TOP) != 0) {
- sb.append(GRAVITY_VALUE_TOP);
- } else if ((vertical & GRAVITY_BOTTOM) != 0) {
- sb.append(GRAVITY_VALUE_BOTTOM);
- } else if ((vertical & GRAVITY_CENTER_VERT) != 0) {
- sb.append(GRAVITY_VALUE_CENTER_VERTICAL);
- } else if ((vertical & GRAVITY_FILL_VERT) != 0) {
- sb.append(GRAVITY_VALUE_FILL_VERTICAL);
- }
-
- return sb.toString();
- }
-}
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
deleted file mode 100644
index 80a23c6db..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridLayoutRule.java
+++ /dev/null
@@ -1,676 +0,0 @@
-/*
- * Copyright (C) 2011 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_LAYOUT_COLUMN;
-import static com.android.SdkConstants.ATTR_LAYOUT_GRAVITY;
-import static com.android.SdkConstants.ATTR_LAYOUT_ROW;
-import static com.android.SdkConstants.ATTR_ORIENTATION;
-import static com.android.SdkConstants.FQCN_GRID_LAYOUT;
-import static com.android.SdkConstants.FQCN_SPACE;
-import static com.android.SdkConstants.FQCN_SPACE_V7;
-import static com.android.SdkConstants.GRAVITY_VALUE_FILL;
-import static com.android.SdkConstants.GRAVITY_VALUE_FILL_HORIZONTAL;
-import static com.android.SdkConstants.GRAVITY_VALUE_FILL_VERTICAL;
-import static com.android.SdkConstants.GRAVITY_VALUE_LEFT;
-import static com.android.SdkConstants.GRID_LAYOUT;
-import static com.android.SdkConstants.VALUE_HORIZONTAL;
-import static com.android.SdkConstants.VALUE_TRUE;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IFeedbackPainter;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.IMenuCallback;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.INodeHandler;
-import com.android.ide.common.api.IViewMetadata;
-import com.android.ide.common.api.IViewMetadata.FillPreference;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.RuleAction;
-import com.android.ide.common.api.RuleAction.Choices;
-import com.android.ide.common.api.SegmentType;
-import com.android.ide.common.layout.grid.GridDropHandler;
-import com.android.ide.common.layout.grid.GridLayoutPainter;
-import com.android.ide.common.layout.grid.GridModel;
-import com.android.ide.common.layout.grid.GridModel.ViewData;
-import com.android.utils.Pair;
-
-import java.net.URL;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-/**
- * An {@link IViewRule} for android.widget.GridLayout which provides designtime
- * interaction with GridLayouts.
- * <p>
- * TODO:
- * <ul>
- * <li>Handle multi-drag: preserving relative positions and alignments among dragged
- * views.
- * <li>Handle GridLayouts that have been configured in a vertical orientation.
- * <li>Handle free-form editing GridLayouts that have been manually edited rather than
- * built up using free-form editing (e.g. they might not follow the same spacing
- * convention, might use weights etc)
- * <li>Avoid setting row and column numbers on the actual elements if they can be skipped
- * to make the XML leaner.
- * </ul>
- */
-public class GridLayoutRule extends BaseLayoutRule {
- /**
- * The size of the visual regular grid that we snap to (if {@link #sSnapToGrid} is set
- */
- public static final int GRID_SIZE = 16;
-
- /** Standard gap between views */
- public static final int SHORT_GAP_DP = 16;
-
- /**
- * The preferred margin size, in pixels
- */
- public static final int MARGIN_SIZE = 32;
-
- /**
- * Size in screen pixels in the IDE of the gutter shown for new rows and columns (in
- * grid mode)
- */
- private static final int NEW_CELL_WIDTH = 10;
-
- /**
- * Maximum size of a widget relative to a cell which is allowed to fit into a cell
- * (and thereby enlarge it) before it is spread with row or column spans.
- */
- public static final double MAX_CELL_DIFFERENCE = 1.2;
-
- /** Whether debugging diagnostics is available in the toolbar */
- private static final boolean CAN_DEBUG =
- VALUE_TRUE.equals(System.getenv("ADT_DEBUG_GRIDLAYOUT")); //$NON-NLS-1$
-
- private static final String ACTION_ADD_ROW = "_addrow"; //$NON-NLS-1$
- private static final String ACTION_REMOVE_ROW = "_removerow"; //$NON-NLS-1$
- private static final String ACTION_ADD_COL = "_addcol"; //$NON-NLS-1$
- private static final String ACTION_REMOVE_COL = "_removecol"; //$NON-NLS-1$
- private static final String ACTION_ORIENTATION = "_orientation"; //$NON-NLS-1$
- private static final String ACTION_SHOW_STRUCTURE = "_structure"; //$NON-NLS-1$
- private static final String ACTION_GRID_MODE = "_gridmode"; //$NON-NLS-1$
- private static final String ACTION_SNAP = "_snap"; //$NON-NLS-1$
- private static final String ACTION_DEBUG = "_debug"; //$NON-NLS-1$
-
- private static final URL ICON_HORIZONTAL = GridLayoutRule.class.getResource("hlinear.png"); //$NON-NLS-1$
- private static final URL ICON_VERTICAL = GridLayoutRule.class.getResource("vlinear.png"); //$NON-NLS-1$
- private static final URL ICON_ADD_ROW = GridLayoutRule.class.getResource("addrow.png"); //$NON-NLS-1$
- private static final URL ICON_REMOVE_ROW = GridLayoutRule.class.getResource("removerow.png"); //$NON-NLS-1$
- private static final URL ICON_ADD_COL = GridLayoutRule.class.getResource("addcol.png"); //$NON-NLS-1$
- private static final URL ICON_REMOVE_COL = GridLayoutRule.class.getResource("removecol.png"); //$NON-NLS-1$
- private static final URL ICON_SHOW_STRUCT = GridLayoutRule.class.getResource("showgrid.png"); //$NON-NLS-1$
- private static final URL ICON_GRID_MODE = GridLayoutRule.class.getResource("gridmode.png"); //$NON-NLS-1$
- private static final URL ICON_SNAP = GridLayoutRule.class.getResource("snap.png"); //$NON-NLS-1$
-
- /**
- * Whether the IDE should show diagnostics for debugging the grid layout - including
- * spacers visibly in the outline, showing row and column numbers, and so on
- */
- public static boolean sDebugGridLayout = CAN_DEBUG;
-
- /** Whether the structure (grid model) should be displayed persistently to the user */
- public static boolean sShowStructure = false;
-
- /** Whether the drop positions should snap to a regular grid */
- public static boolean sSnapToGrid = false;
-
- /**
- * Whether the grid is edited in "grid mode" where the operations are row/column based
- * rather than free-form
- */
- public static boolean sGridMode = true;
-
- /** Constructs a new {@link GridLayoutRule} */
- public GridLayoutRule() {
- }
-
- @Override
- public void addLayoutActions(
- @NonNull List<RuleAction> actions,
- final @NonNull INode parentNode,
- final @NonNull List<? extends INode> 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", namespace, ATTR_ORIENTATION), Arrays
- .<String> asList("Set Horizontal Orientation", "Set Vertical Orientation"),
- Arrays.<URL> asList(ICON_HORIZONTAL, ICON_VERTICAL), Arrays.<String> asList(
- "horizontal", "vertical"), getCurrentOrientation(parentNode),
- null /* icon */, -10, false);
- orientationAction.setRadio(true);
- actions.add(orientationAction);
-
- // Gravity and margins
- if (children != null && children.size() > 0) {
- actions.add(RuleAction.createSeparator(35));
- actions.add(createMarginAction(parentNode, children));
- actions.add(createGravityAction(children, ATTR_LAYOUT_GRAVITY));
- }
-
- IMenuCallback actionCallback = new IMenuCallback() {
- @Override
- public void action(
- final @NonNull RuleAction action,
- @NonNull List<? extends INode> selectedNodes,
- final @Nullable String valueId,
- final @Nullable Boolean newValue) {
- parentNode.editXml("Add/Remove Row/Column", new INodeHandler() {
- @Override
- public void handle(@NonNull INode n) {
- String id = action.getId();
- if (id.equals(ACTION_SHOW_STRUCTURE)) {
- sShowStructure = !sShowStructure;
- mRulesEngine.redraw();
- return;
- } else if (id.equals(ACTION_GRID_MODE)) {
- sGridMode = !sGridMode;
- mRulesEngine.redraw();
- return;
- } else if (id.equals(ACTION_SNAP)) {
- sSnapToGrid = !sSnapToGrid;
- mRulesEngine.redraw();
- return;
- } else if (id.equals(ACTION_DEBUG)) {
- sDebugGridLayout = !sDebugGridLayout;
- mRulesEngine.layout();
- return;
- }
-
- GridModel grid = GridModel.get(mRulesEngine, parentNode, null);
- if (id.equals(ACTION_ADD_ROW)) {
- grid.addRow(children);
- } else if (id.equals(ACTION_REMOVE_ROW)) {
- grid.removeRows(children);
- } else if (id.equals(ACTION_ADD_COL)) {
- grid.addColumn(children);
- } else if (id.equals(ACTION_REMOVE_COL)) {
- grid.removeColumns(children);
- }
- }
-
- });
- }
- };
-
- actions.add(RuleAction.createSeparator(142));
-
- actions.add(RuleAction.createToggle(ACTION_GRID_MODE, "Grid Model Mode",
- sGridMode, actionCallback, ICON_GRID_MODE, 145, false));
-
- // Add and Remove Column actions only apply in Grid Mode
- if (sGridMode) {
- actions.add(RuleAction.createToggle(ACTION_SHOW_STRUCTURE, "Show Structure",
- sShowStructure, actionCallback, ICON_SHOW_STRUCT, 147, false));
-
- // Add Row and Add Column
- actions.add(RuleAction.createSeparator(150));
- actions.add(RuleAction.createAction(ACTION_ADD_COL, "Add Column", actionCallback,
- ICON_ADD_COL, 160, false /* supportsMultipleNodes */));
- actions.add(RuleAction.createAction(ACTION_ADD_ROW, "Add Row", actionCallback,
- ICON_ADD_ROW, 165, false));
-
- // Remove Row and Remove Column (if something is selected)
- if (children != null && children.size() > 0) {
- // TODO: Add "Merge Columns" and "Merge Rows" ?
-
- actions.add(RuleAction.createAction(ACTION_REMOVE_COL, "Remove Column",
- actionCallback, ICON_REMOVE_COL, 170, false));
- actions.add(RuleAction.createAction(ACTION_REMOVE_ROW, "Remove Row",
- actionCallback, ICON_REMOVE_ROW, 175, false));
- }
-
- actions.add(RuleAction.createSeparator(185));
- } else {
- actions.add(RuleAction.createToggle(ACTION_SHOW_STRUCTURE, "Show Structure",
- sShowStructure, actionCallback, ICON_SHOW_STRUCT, 190, false));
-
- // Snap to Grid and Show Structure are only relevant in free form mode
- actions.add(RuleAction.createToggle(ACTION_SNAP, "Snap to Grid",
- sSnapToGrid, actionCallback, ICON_SNAP, 200, false));
- }
-
- // Temporary: Diagnostics for GridLayout
- if (CAN_DEBUG) {
- actions.add(RuleAction.createToggle(ACTION_DEBUG, "Debug",
- sDebugGridLayout, actionCallback, null, 210, false));
- }
- }
-
- /**
- * Returns the orientation attribute value currently used by the node (even if not
- * defined, in which case the default horizontal value is returned)
- */
- private String getCurrentOrientation(final INode node) {
- String orientation = node.getStringAttr(getNamespace(node), ATTR_ORIENTATION);
- if (orientation == null || orientation.length() == 0) {
- orientation = VALUE_HORIZONTAL;
- }
- return orientation;
- }
-
- @Override
- public DropFeedback onDropEnter(@NonNull INode targetNode, @Nullable Object targetView,
- @Nullable IDragElement[] elements) {
- GridDropHandler userData = new GridDropHandler(this, targetNode, targetView);
- IFeedbackPainter painter = GridLayoutPainter.createDropFeedbackPainter(this, elements);
- return new DropFeedback(userData, painter);
- }
-
- @Override
- public DropFeedback onDropMove(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback, @NonNull Point p) {
- if (feedback == null) {
- return null;
- }
- feedback.requestPaint = true;
-
- GridDropHandler handler = (GridDropHandler) feedback.userData;
- handler.computeMatches(feedback, p);
-
- return feedback;
- }
-
- @Override
- public void onDropped(final @NonNull INode targetNode, final @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback, @NonNull Point p) {
- if (feedback == null) {
- return;
- }
-
- Rect b = targetNode.getBounds();
- if (!b.isValid()) {
- return;
- }
-
- GridDropHandler dropHandler = (GridDropHandler) feedback.userData;
- if (dropHandler.getRowMatch() == null || dropHandler.getColumnMatch() == null) {
- return;
- }
-
- // Collect IDs from dropped elements and remap them to new IDs
- // if this is a copy or from a different canvas.
- Map<String, Pair<String, String>> idMap = getDropIdMap(targetNode, elements,
- feedback.isCopy || !feedback.sameCanvas);
-
- for (IDragElement element : elements) {
- INode newChild;
- if (!sGridMode) {
- newChild = dropHandler.handleFreeFormDrop(targetNode, element);
- } else {
- newChild = dropHandler.handleGridModeDrop(targetNode, element);
- }
-
- // Copy all the attributes, modifying them as needed.
- addAttributes(newChild, element, idMap, DEFAULT_ATTR_FILTER);
-
- addInnerElements(newChild, element, idMap);
- }
- }
-
- @Override
- public void onChildInserted(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- if (insertType == InsertType.MOVE_WITHIN) {
- // Don't adjust widths/heights/weights when just moving within a single layout
- return;
- }
-
- if (GridModel.isSpace(node.getFqcn())) {
- return;
- }
-
- // Attempt to set "fill" properties on newly added views such that for example
- // a text field will stretch horizontally.
- String fqcn = node.getFqcn();
- IViewMetadata metadata = mRulesEngine.getMetadata(fqcn);
- FillPreference fill = metadata.getFillPreference();
- String gravity = computeDefaultGravity(fill);
- if (gravity != null) {
- 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;
-
- String fqcn = layout.getFqcn();
- if (!fqcn.equals(GRID_LAYOUT) && !fqcn.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
- *
- * @param fill the fill preference for the widget
- * @return the gravity value, or null, to be set on the widget
- */
- public static String computeDefaultGravity(FillPreference fill) {
- String horizontal = GRAVITY_VALUE_LEFT;
- String vertical = null;
- if (fill.fillHorizontally(true /*verticalContext*/)) {
- horizontal = GRAVITY_VALUE_FILL_HORIZONTAL;
- }
- if (fill.fillVertically(true /*verticalContext*/)) {
- vertical = GRAVITY_VALUE_FILL_VERTICAL;
- }
- String gravity;
- if (horizontal == GRAVITY_VALUE_FILL_HORIZONTAL
- && vertical == GRAVITY_VALUE_FILL_VERTICAL) {
- gravity = GRAVITY_VALUE_FILL;
- } else if (vertical != null) {
- gravity = horizontal + '|' + vertical;
- } else {
- gravity = horizontal;
- }
-
- return gravity;
- }
-
- @Override
- public void onRemovingChildren(@NonNull List<INode> deleted, @NonNull INode parent,
- boolean moved) {
- super.onRemovingChildren(deleted, parent, moved);
-
- if (!sGridMode) {
- // Attempt to clean up spacer objects for any newly-empty rows or columns
- // as the result of this deletion
- GridModel grid = GridModel.get(mRulesEngine, parent, null);
- grid.onDeleted(deleted);
- }
- }
-
- @Override
- protected void paintResizeFeedback(IGraphics gc, INode node, ResizeState state) {
- if (!sGridMode) {
- GridModel grid = getGrid(state);
- GridLayoutPainter.paintResizeFeedback(gc, state.layout, grid);
- }
-
- if (resizingWidget(state)) {
- super.paintResizeFeedback(gc, node, state);
- } else {
- GridModel grid = getGrid(state);
- int startColumn = grid.getColumn(state.bounds.x);
- int endColumn = grid.getColumn(state.bounds.x2());
- int columnSpan = endColumn - startColumn + 1;
-
- int startRow = grid.getRow(state.bounds.y);
- int endRow = grid.getRow(state.bounds.y2());
- int rowSpan = endRow - startRow + 1;
-
- Rect cellBounds = grid.getCellBounds(startRow, startColumn, rowSpan, columnSpan);
- gc.useStyle(DrawingStyle.RESIZE_PREVIEW);
- gc.drawRect(cellBounds);
- }
- }
-
- /** Returns the grid size cached on the given {@link ResizeState} object */
- private GridModel getGrid(ResizeState resizeState) {
- GridModel grid = (GridModel) resizeState.clientData;
- if (grid == null) {
- grid = GridModel.get(mRulesEngine, resizeState.layout, resizeState.layoutView);
- resizeState.clientData = grid;
- }
-
- return grid;
- }
-
- @Override
- protected void setNewSizeBounds(ResizeState state, INode node, INode layout,
- Rect oldBounds, Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) {
-
- if (resizingWidget(state)) {
- if (state.fillWidth || state.fillHeight || state.wrapWidth || state.wrapHeight) {
- GridModel grid = getGrid(state);
- ViewData view = grid.getView(node);
- if (view != null) {
- String gravityString = grid.getGridAttribute(view.node, ATTR_LAYOUT_GRAVITY);
- int gravity = GravityHelper.getGravity(gravityString, 0);
- if (view.column > 0 && verticalEdge != null && state.fillWidth) {
- state.fillWidth = false;
- state.wrapWidth = true;
- gravity &= ~GravityHelper.GRAVITY_HORIZ_MASK;
- gravity |= GravityHelper.GRAVITY_FILL_HORIZ;
- } else if (verticalEdge != null && state.wrapWidth) {
- gravity &= ~GravityHelper.GRAVITY_HORIZ_MASK;
- gravity |= GravityHelper.GRAVITY_LEFT;
- }
- if (view.row > 0 && horizontalEdge != null && state.fillHeight) {
- state.fillHeight = false;
- state.wrapHeight = true;
- gravity &= ~GravityHelper.GRAVITY_VERT_MASK;
- gravity |= GravityHelper.GRAVITY_FILL_VERT;
- } else if (horizontalEdge != null && state.wrapHeight) {
- gravity &= ~GravityHelper.GRAVITY_VERT_MASK;
- gravity |= GravityHelper.GRAVITY_TOP;
- }
- gravityString = GravityHelper.getGravity(gravity);
- grid.setGridAttribute(view.node, ATTR_LAYOUT_GRAVITY, gravityString);
- // Fall through and set layout_width and/or layout_height to wrap_content
- }
- }
- super.setNewSizeBounds(state, node, layout, oldBounds, newBounds, horizontalEdge,
- verticalEdge);
- } else {
- Pair<Integer, Integer> spans = computeResizeSpans(state);
- int rowSpan = spans.getFirst();
- int columnSpan = spans.getSecond();
- GridModel grid = getGrid(state);
- grid.setColumnSpanAttribute(node, columnSpan);
- grid.setRowSpanAttribute(node, rowSpan);
-
- ViewData view = grid.getView(node);
- if (view != null) {
- String gravityString = grid.getGridAttribute(view.node, ATTR_LAYOUT_GRAVITY);
- int gravity = GravityHelper.getGravity(gravityString, 0);
- if (verticalEdge != null && columnSpan > 1) {
- gravity &= ~GravityHelper.GRAVITY_HORIZ_MASK;
- gravity |= GravityHelper.GRAVITY_FILL_HORIZ;
- }
- if (horizontalEdge != null && rowSpan > 1) {
- gravity &= ~GravityHelper.GRAVITY_VERT_MASK;
- gravity |= GravityHelper.GRAVITY_FILL_VERT;
- }
- gravityString = GravityHelper.getGravity(gravity);
- grid.setGridAttribute(view.node, ATTR_LAYOUT_GRAVITY, gravityString);
- }
- }
- }
-
- @Override
- protected String getResizeUpdateMessage(ResizeState state, INode child, INode parent,
- Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) {
- Pair<Integer, Integer> spans = computeResizeSpans(state);
- if (resizingWidget(state)) {
- String width = state.getWidthAttribute();
- String height = state.getHeightAttribute();
-
- String message;
- if (horizontalEdge == null) {
- message = width;
- } else if (verticalEdge == null) {
- message = height;
- } else {
- // U+00D7: Unicode for multiplication sign
- message = String.format("%s \u00D7 %s", width, height);
- }
-
- // Tack on a tip about using the Shift modifier key
- return String.format("%s\n(Press Shift to resize row/column spans)", message);
- } else {
- int rowSpan = spans.getFirst();
- int columnSpan = spans.getSecond();
- return String.format("ColumnSpan=%d, RowSpan=%d\n(Release Shift to resize widget itself)",
- columnSpan, rowSpan);
- }
- }
-
- /**
- * Returns true if we're resizing the widget, and false if we're resizing the cell
- * spans
- */
- private static boolean resizingWidget(ResizeState state) {
- return (state.modifierMask & DropFeedback.MODIFIER2) == 0;
- }
-
- /**
- * Computes the new column and row spans as the result of the current resizing
- * operation
- */
- private Pair<Integer, Integer> computeResizeSpans(ResizeState state) {
- GridModel grid = getGrid(state);
-
- int startColumn = grid.getColumn(state.bounds.x);
- int endColumn = grid.getColumn(state.bounds.x2());
- int columnSpan = endColumn - startColumn + 1;
-
- int startRow = grid.getRow(state.bounds.y);
- int endRow = grid.getRow(state.bounds.y2());
- int rowSpan = endRow - startRow + 1;
-
- return Pair.of(rowSpan, columnSpan);
- }
-
- /**
- * Returns the size of the new cell gutter in layout coordinates
- *
- * @return the size of the new cell gutter in layout coordinates
- */
- public int getNewCellSize() {
- return mRulesEngine.screenToLayout(NEW_CELL_WIDTH / 2);
- }
-
- @Override
- public void paintSelectionFeedback(@NonNull IGraphics graphics, @NonNull INode parentNode,
- @NonNull List<? extends INode> childNodes, @Nullable Object view) {
- super.paintSelectionFeedback(graphics, parentNode, childNodes, view);
-
- if (sShowStructure) {
- // TODO: Cache the grid
- if (view != null) {
- if (GridLayoutPainter.paintStructure(view, DrawingStyle.GUIDELINE_DASHED,
- parentNode, graphics)) {
- return;
- }
- }
- GridLayoutPainter.paintStructure(DrawingStyle.GUIDELINE_DASHED,
- parentNode, graphics, GridModel.get(mRulesEngine, parentNode, view));
- } else if (sDebugGridLayout) {
- GridLayoutPainter.paintStructure(DrawingStyle.GRID,
- parentNode, graphics, GridModel.get(mRulesEngine, parentNode, view));
- }
-
- // TBD: Highlight the cells around the selection, and display easy controls
- // for for example tweaking the rowspan/colspan of a cell? (but only in grid mode)
- }
-
- /**
- * Paste into a GridLayout. We have several possible behaviors (and many
- * more than are listed here):
- * <ol>
- * <li> Preserve the current positions of the elements (if pasted from another
- * canvas, not just XML markup copied from say a web site) and apply those
- * into the current grid. This might mean "overwriting" (sitting on top of)
- * existing elements.
- * <li> Fill available "holes" in the grid.
- * <li> Lay them out consecutively, row by row, like text.
- * <li> Some hybrid approach, where I attempt to preserve the <b>relative</b>
- * relationships (columns/wrapping, spacing between the pasted views etc)
- * but I append them to the bottom of the layout on one or more new rows.
- * <li> Try to paste at the current mouse position, if known, preserving the
- * relative distances between the existing elements there.
- * </ol>
- * Attempting to preserve the current position isn't possible right now,
- * because the clipboard data contains only the textual representation of
- * the markup. (We'd need to stash position information from a previous
- * layout render along with the clipboard data).
- * <p>
- * Currently, this implementation simply lays out the elements row by row,
- * approach #3 above.
- */
- @Override
- public void onPaste(
- @NonNull INode targetNode,
- @Nullable Object targetView,
- @NonNull IDragElement[] elements) {
- DropFeedback feedback = onDropEnter(targetNode, targetView, elements);
- if (feedback != null) {
- Rect b = targetNode.getBounds();
- if (!b.isValid()) {
- return;
- }
-
- Map<String, Pair<String, String>> idMap = getDropIdMap(targetNode, elements,
- true /* remap id's */);
-
- for (IDragElement element : elements) {
- // Skip <Space> elements and only insert the real elements being
- // copied
- if (elements.length > 1 && (FQCN_SPACE.equals(element.getFqcn())
- || FQCN_SPACE_V7.equals(element.getFqcn()))) {
- continue;
- }
-
- String fqcn = element.getFqcn();
- INode newChild = targetNode.appendChild(fqcn);
- addAttributes(newChild, element, idMap, DEFAULT_ATTR_FILTER);
-
- // Ensure that we reset any potential row/column attributes from a different
- // grid layout being copied from
- 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
- // Alternatively, I could just lay them all out on subsequent lines
- // with a column span of columnSpan5
-
- addInnerElements(newChild, element, idMap);
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridViewRule.java
deleted file mode 100644
index b82f391b4..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridViewRule.java
+++ /dev/null
@@ -1,41 +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_LAYOUT_WIDTH;
-import static com.android.SdkConstants.ATTR_NUM_COLUMNS;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for android.widget.GridView
- */
-public class GridViewRule extends BaseViewRule {
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, getFillParentValueName());
- node.setAttribute(ANDROID_URI, ATTR_NUM_COLUMNS, "3"); //$NON-NLS-1$
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java
deleted file mode 100644
index 722949051..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java
+++ /dev/null
@@ -1,96 +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_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-import static com.android.SdkConstants.ATTR_ORIENTATION;
-import static com.android.SdkConstants.FQCN_LINEAR_LAYOUT;
-import static com.android.SdkConstants.VALUE_HORIZONTAL;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-
-/**
- * An {@link IViewRule} for android.widget.HorizontalScrollView.
- */
-public class HorizontalScrollViewRule extends FrameLayoutRule {
-
- @Override
- public void onChildInserted(@NonNull INode child, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onChildInserted(child, parent, insertType);
-
- // The child of the ScrollView should fill in both directions
- String fillParent = getFillParentValueName();
- child.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
- child.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, fillParent);
- }
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- if (insertType.isCreate()) {
- // Insert a horizontal linear layout which is commonly used with horizontal scrollbars
- // as described by the documentation for HorizontalScrollbars.
- INode linearLayout = node.appendChild(FQCN_LINEAR_LAYOUT);
- linearLayout.setAttribute(ANDROID_URI, ATTR_ORIENTATION,
- VALUE_HORIZONTAL);
- }
- }
-
- @Override
- public DropFeedback onDropMove(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback, @NonNull Point p) {
- DropFeedback f = super.onDropMove(targetNode, elements, feedback, p);
-
- // HorizontalScrollViews only allow a single child
- if (targetNode.getChildren().length > 0) {
- f.invalidTarget = true;
- }
- return f;
- }
-
- @Override
- protected void drawFeedback(
- IGraphics gc,
- INode targetNode,
- IDragElement[] elements,
- DropFeedback feedback) {
- if (targetNode.getChildren().length > 0) {
- Rect b = targetNode.getBounds();
- if (b.isValid()) {
- gc.useStyle(DrawingStyle.DROP_RECIPIENT);
- gc.drawRect(b);
- }
- } else {
- super.drawFeedback(gc, targetNode, elements, feedback);
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/IgnoredLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/IgnoredLayoutRule.java
deleted file mode 100644
index 3a65a8601..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/IgnoredLayoutRule.java
+++ /dev/null
@@ -1,46 +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 com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.INode;
-
-/**
- * An ignored layout is a layout that should not be treated as a layout by the
- * visual editor (usually because the widget extends a layout class we recognize
- * and support, but where the widget is more restrictive in how it manages its
- * children so we don't want to expose the normal configuration options).
- * <p>
- * For example, the ZoomControls widget is not user-configurable as a
- * LinearLayout even though it extends it. Our ZoomControls rule is therefore a
- * subclass of this {@link IgnoredLayoutRule} class.
- */
-public abstract class IgnoredLayoutRule extends BaseLayoutRule {
- @Override
- public DropFeedback onDropEnter(@NonNull INode targetNode, @Nullable Object targetView,
- @Nullable IDragElement[] elements) {
- // Do nothing; this layout rule corresponds to a layout that
- // should not be handled as a layout by the visual editor - usually
- // because some widget is extending a layout for implementation purposes
- // but does not want to expose configurability of the base layout in the
- // editor.
- return null;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageButtonRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageButtonRule.java
deleted file mode 100644
index 990795091..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageButtonRule.java
+++ /dev/null
@@ -1,54 +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_SRC;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for android.widget.ImageButton.
- */
-public class ImageButtonRule extends BaseViewRule {
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- // When dropping an include tag, ask the user which layout to include.
- if (insertType == InsertType.CREATE) { // NOT InsertType.CREATE_PREVIEW
- String src = mRulesEngine.displayResourceInput("drawable", ""); //$NON-NLS-1$ //$NON-NLS-2$
- if (src != null) {
- node.editXml("Set Image",
- new PropertySettingNodeHandler(ANDROID_URI, ATTR_SRC,
- src.length() > 0 ? src : null));
- return;
- }
- }
-
- // Fallback if dismissed or during previews etc
- if (insertType.isCreate()) {
- node.setAttribute(ANDROID_URI, ATTR_SRC, getSampleImageSrc());
- }
- }
-
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageViewRule.java
deleted file mode 100644
index bc0184c4f..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageViewRule.java
+++ /dev/null
@@ -1,57 +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_SRC;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for android.widget.ImageViewRule.
- */
-public class ImageViewRule extends BaseViewRule {
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- // When dropping an include tag, ask the user which layout to include.
- if (insertType == InsertType.CREATE) { // NOT InsertType.CREATE_PREVIEW
- String src = mRulesEngine.displayResourceInput("drawable", ""); //$NON-NLS-1$ //$NON-NLS-2$
- if (src != null) {
- node.editXml("Set Image",
- new PropertySettingNodeHandler(ANDROID_URI, ATTR_SRC,
- src.length() > 0 ? src : null));
- return;
- } else {
- // Remove the view; the insertion was canceled
- parent.removeChild(node);
- }
- }
-
- // Fallback if dismissed or during previews etc
- if (insertType.isCreate()) {
- node.setAttribute(ANDROID_URI, ATTR_SRC, getSampleImageSrc());
- }
- }
-
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/IncludeRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/IncludeRule.java
deleted file mode 100644
index fcb1a6dac..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/IncludeRule.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2011 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.ATTR_LAYOUT;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for the special XML {@code <include>} tag.
- */
-public class IncludeRule extends BaseViewRule {
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- // When dropping an include tag, ask the user which layout to include.
- if (insertType == InsertType.CREATE) { // NOT InsertType.CREATE_PREVIEW
- String include = mRulesEngine.displayIncludeSourceInput();
- if (include != null) {
- node.editXml("Include Layout",
- // Note -- the layout attribute is NOT in the Android namespace!
- new PropertySettingNodeHandler(null, ATTR_LAYOUT,
- include.length() > 0 ? include : null));
- } else {
- // Remove the view; the insertion was canceled
- parent.removeChild(node);
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java
deleted file mode 100644
index 610fe5d8b..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java
+++ /dev/null
@@ -1,1092 +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_BASELINE_ALIGNED;
-import static com.android.SdkConstants.ATTR_LAYOUT_GRAVITY;
-import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_WEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-import static com.android.SdkConstants.ATTR_ORIENTATION;
-import static com.android.SdkConstants.ATTR_WEIGHT_SUM;
-import static com.android.SdkConstants.VALUE_1;
-import static com.android.SdkConstants.VALUE_HORIZONTAL;
-import static com.android.SdkConstants.VALUE_VERTICAL;
-import static com.android.SdkConstants.VALUE_WRAP_CONTENT;
-import static com.android.SdkConstants.VALUE_ZERO_DP;
-import static com.android.ide.eclipse.adt.AdtUtils.formatFloatAttribute;
-
-import com.android.SdkConstants;
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IClientRulesEngine;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IFeedbackPainter;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.IMenuCallback;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.INodeHandler;
-import com.android.ide.common.api.IViewMetadata;
-import com.android.ide.common.api.IViewMetadata.FillPreference;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.RuleAction;
-import com.android.ide.common.api.RuleAction.Choices;
-import com.android.ide.common.api.SegmentType;
-import com.android.ide.eclipse.adt.AdtPlugin;
-
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-/**
- * An {@link IViewRule} for android.widget.LinearLayout and all its derived
- * classes.
- */
-public class LinearLayoutRule extends BaseLayoutRule {
- private static final String ACTION_ORIENTATION = "_orientation"; //$NON-NLS-1$
- private static final String ACTION_WEIGHT = "_weight"; //$NON-NLS-1$
- private static final String ACTION_DISTRIBUTE = "_distribute"; //$NON-NLS-1$
- private static final String ACTION_BASELINE = "_baseline"; //$NON-NLS-1$
- private static final String ACTION_CLEAR = "_clear"; //$NON-NLS-1$
- private static final String ACTION_DOMINATE = "_dominate"; //$NON-NLS-1$
-
- private static final URL ICON_HORIZONTAL =
- LinearLayoutRule.class.getResource("hlinear.png"); //$NON-NLS-1$
- private static final URL ICON_VERTICAL =
- LinearLayoutRule.class.getResource("vlinear.png"); //$NON-NLS-1$
- private static final URL ICON_WEIGHTS =
- LinearLayoutRule.class.getResource("weights.png"); //$NON-NLS-1$
- private static final URL ICON_DISTRIBUTE =
- LinearLayoutRule.class.getResource("distribute.png"); //$NON-NLS-1$
- private static final URL ICON_BASELINE =
- LinearLayoutRule.class.getResource("baseline.png"); //$NON-NLS-1$
- private static final URL ICON_CLEAR_WEIGHTS =
- LinearLayoutRule.class.getResource("clearweights.png"); //$NON-NLS-1$
- private static final URL ICON_DOMINATE =
- LinearLayoutRule.class.getResource("allweight.png"); //$NON-NLS-1$
-
- /**
- * Returns the current orientation, regardless of whether it has been defined in XML
- *
- * @param node The LinearLayout to look up the orientation for
- * @return "horizontal" or "vertical" depending on the current orientation of the
- * linear layout
- */
- private String getCurrentOrientation(final INode node) {
- String orientation = node.getStringAttr(ANDROID_URI, ATTR_ORIENTATION);
- if (orientation == null || orientation.length() == 0) {
- orientation = VALUE_HORIZONTAL;
- }
- return orientation;
- }
-
- /**
- * Returns true if the given node represents a vertical linear layout.
- * @param node the node to check layout orientation for
- * @return true if the layout is in vertical mode, otherwise false
- */
- protected boolean isVertical(INode node) {
- // Horizontal is the default, so if no value is specified it is horizontal.
- return VALUE_VERTICAL.equals(node.getStringAttr(ANDROID_URI,
- ATTR_ORIENTATION));
- }
-
- /**
- * Returns true if this LinearLayout supports switching orientation.
- *
- * @return true if this layout supports orientations
- */
- protected boolean supportsOrientation() {
- return true;
- }
-
- @Override
- public void addLayoutActions(
- @NonNull List<RuleAction> actions,
- final @NonNull INode parentNode,
- final @NonNull List<? extends INode> children) {
- super.addLayoutActions(actions, parentNode, children);
- if (supportsOrientation()) {
- Choices action = RuleAction.createChoices(
- ACTION_ORIENTATION, "Orientation", //$NON-NLS-1$
- new PropertyCallback(Collections.singletonList(parentNode),
- "Change LinearLayout Orientation",
- ANDROID_URI, ATTR_ORIENTATION),
- Arrays.<String>asList("Set Horizontal Orientation","Set Vertical Orientation"),
- Arrays.<URL>asList(ICON_HORIZONTAL, ICON_VERTICAL),
- Arrays.<String>asList("horizontal", "vertical"),
- getCurrentOrientation(parentNode),
- null /* icon */,
- -10,
- false /* supportsMultipleNodes */
- );
- action.setRadio(true);
- actions.add(action);
- }
- if (!isVertical(parentNode)) {
- String current = parentNode.getStringAttr(ANDROID_URI, ATTR_BASELINE_ALIGNED);
- boolean isAligned = current == null || Boolean.valueOf(current);
- actions.add(RuleAction.createToggle(ACTION_BASELINE, "Toggle Baseline Alignment",
- isAligned,
- new PropertyCallback(Collections.singletonList(parentNode),
- "Change Baseline Alignment",
- ANDROID_URI, ATTR_BASELINE_ALIGNED), // TODO: Also set index?
- ICON_BASELINE, 38, false));
- }
-
- // Gravity
- if (children != null && children.size() > 0) {
- actions.add(RuleAction.createSeparator(35));
-
- // Margins
- actions.add(createMarginAction(parentNode, children));
-
- // Gravity
- actions.add(createGravityAction(children, ATTR_LAYOUT_GRAVITY));
-
- // Weights
- IMenuCallback actionCallback = new IMenuCallback() {
- @Override
- public void action(
- final @NonNull RuleAction action,
- @NonNull List<? extends INode> selectedNodes,
- final @Nullable String valueId,
- final @Nullable Boolean newValue) {
- parentNode.editXml("Change Weight", new INodeHandler() {
- @Override
- public void handle(@NonNull INode n) {
- String id = action.getId();
- if (id.equals(ACTION_WEIGHT)) {
- String weight =
- children.get(0).getStringAttr(ANDROID_URI, ATTR_LAYOUT_WEIGHT);
- if (weight == null || weight.length() == 0) {
- weight = "0.0"; //$NON-NLS-1$
- }
- weight = mRulesEngine.displayInput("Enter Weight Value:", weight,
- null);
- if (weight != null) {
- if (weight.isEmpty()) {
- weight = null; // remove attribute
- }
- for (INode child : children) {
- child.setAttribute(ANDROID_URI,
- ATTR_LAYOUT_WEIGHT, weight);
- }
- }
- } else if (id.equals(ACTION_DISTRIBUTE)) {
- distributeWeights(parentNode, parentNode.getChildren());
- } else if (id.equals(ACTION_CLEAR)) {
- clearWeights(parentNode);
- } else if (id.equals(ACTION_CLEAR) || id.equals(ACTION_DOMINATE)) {
- clearWeights(parentNode);
- distributeWeights(parentNode,
- children.toArray(new INode[children.size()]));
- } else {
- assert id.equals(ACTION_BASELINE);
- }
- }
- });
- }
- };
- actions.add(RuleAction.createSeparator(50));
- actions.add(RuleAction.createAction(ACTION_DISTRIBUTE, "Distribute Weights Evenly",
- actionCallback, ICON_DISTRIBUTE, 60, false /*supportsMultipleNodes*/));
- actions.add(RuleAction.createAction(ACTION_DOMINATE, "Assign All Weight",
- actionCallback, ICON_DOMINATE, 70, false));
- actions.add(RuleAction.createAction(ACTION_WEIGHT, "Change Layout Weight",
- actionCallback, ICON_WEIGHTS, 80, false));
- actions.add(RuleAction.createAction(ACTION_CLEAR, "Clear All Weights",
- actionCallback, ICON_CLEAR_WEIGHTS, 90, false));
- }
- }
-
- private void distributeWeights(INode parentNode, INode[] targets) {
- // Any XML to get weight sum?
- String weightSum = parentNode.getStringAttr(ANDROID_URI,
- ATTR_WEIGHT_SUM);
- double sum = -1.0;
- if (weightSum != null) {
- // Distribute
- try {
- sum = Double.parseDouble(weightSum);
- } catch (NumberFormatException nfe) {
- // Just keep using the default
- }
- }
- int numTargets = targets.length;
- double share;
- if (sum <= 0.0) {
- // The sum will be computed from the children, so just
- // use arbitrary amount
- share = 1.0;
- } else {
- share = sum / numTargets;
- }
- String value = formatFloatAttribute((float) share);
- String sizeAttribute = isVertical(parentNode) ?
- ATTR_LAYOUT_HEIGHT : ATTR_LAYOUT_WIDTH;
- for (INode target : targets) {
- target.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, value);
- // Also set the width/height to 0dp to ensure actual equal
- // size (without this, only the remaining space is
- // distributed)
- if (VALUE_WRAP_CONTENT.equals(target.getStringAttr(ANDROID_URI, sizeAttribute))) {
- target.setAttribute(ANDROID_URI, sizeAttribute, VALUE_ZERO_DP);
- }
- }
- }
-
- private void clearWeights(INode parentNode) {
- // Clear attributes
- String sizeAttribute = isVertical(parentNode)
- ? ATTR_LAYOUT_HEIGHT : ATTR_LAYOUT_WIDTH;
- for (INode target : parentNode.getChildren()) {
- target.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, null);
- String size = target.getStringAttr(ANDROID_URI, sizeAttribute);
- if (size != null && size.startsWith("0")) { //$NON-NLS-1$
- target.setAttribute(ANDROID_URI, sizeAttribute, VALUE_WRAP_CONTENT);
- }
- }
- }
-
- // ==== Drag'n'drop support ====
-
- @Override
- public DropFeedback onDropEnter(final @NonNull INode targetNode, @Nullable Object targetView,
- final @Nullable IDragElement[] elements) {
-
- if (elements.length == 0) {
- return null;
- }
-
- Rect bn = targetNode.getBounds();
- if (!bn.isValid()) {
- return null;
- }
-
- boolean isVertical = isVertical(targetNode);
-
- // Prepare a list of insertion points: X coords for horizontal, Y for
- // vertical.
- List<MatchPos> indexes = new ArrayList<MatchPos>();
-
- int last = isVertical ? bn.y : bn.x;
- int pos = 0;
- boolean lastDragged = false;
- int selfPos = -1;
- for (INode it : targetNode.getChildren()) {
- Rect bc = it.getBounds();
- if (bc.isValid()) {
- // First see if this node looks like it's the same as one of the
- // *dragged* bounds
- boolean isDragged = false;
- for (IDragElement element : elements) {
- // This tries to determine if an INode corresponds to an
- // IDragElement, by comparing their bounds.
- if (element.isSame(it)) {
- isDragged = true;
- break;
- }
- }
-
- // We don't want to insert drag positions before or after the
- // element that is itself being dragged. However, we -do- want
- // to insert a match position here, at the center, such that
- // when you drag near its current position we show a match right
- // where it's already positioned.
- if (isDragged) {
- int v = isVertical ? bc.y + (bc.h / 2) : bc.x + (bc.w / 2);
- selfPos = pos;
- indexes.add(new MatchPos(v, pos++));
- } else if (lastDragged) {
- // Even though we don't want to insert a match below, we
- // need to increment the index counter such that subsequent
- // lines know their correct index in the child list.
- pos++;
- } else {
- // Add an insertion point between the last point and the
- // start of this child
- int v = isVertical ? bc.y : bc.x;
- v = (last + v) / 2;
- indexes.add(new MatchPos(v, pos++));
- }
-
- last = isVertical ? (bc.y + bc.h) : (bc.x + bc.w);
- lastDragged = isDragged;
- } else {
- // We still have to count this position even if it has no bounds, or
- // subsequent children will be inserted at the wrong place
- pos++;
- }
- }
-
- // Finally add an insert position after all the children - unless of
- // course we happened to be dragging the last element
- if (!lastDragged) {
- int v = last + 1;
- indexes.add(new MatchPos(v, pos));
- }
-
- int posCount = targetNode.getChildren().length + 1;
- return new DropFeedback(new LinearDropData(indexes, posCount, isVertical, selfPos),
- new IFeedbackPainter() {
-
- @Override
- public void paint(@NonNull IGraphics gc, @NonNull INode node,
- @NonNull DropFeedback feedback) {
- // Paint callback for the LinearLayout. This is called
- // by the canvas when a draw is needed.
- drawFeedback(gc, node, elements, feedback);
- }
- });
- }
-
- void drawFeedback(IGraphics gc, INode node, IDragElement[] elements, DropFeedback feedback) {
- Rect b = node.getBounds();
- if (!b.isValid()) {
- return;
- }
-
- // Highlight the receiver
- gc.useStyle(DrawingStyle.DROP_RECIPIENT);
- gc.drawRect(b);
-
- gc.useStyle(DrawingStyle.DROP_ZONE);
-
- LinearDropData data = (LinearDropData) feedback.userData;
- boolean isVertical = data.isVertical();
- int selfPos = data.getSelfPos();
-
- for (MatchPos it : data.getIndexes()) {
- int i = it.getDistance();
- int pos = it.getPosition();
- // Don't show insert drop zones for "self"-index since that one goes
- // right through the center of the widget rather than in a sibling
- // position
- if (pos != selfPos) {
- if (isVertical) {
- // draw horizontal lines
- gc.drawLine(b.x, i, b.x + b.w, i);
- } else {
- // draw vertical lines
- gc.drawLine(i, b.y, i, b.y + b.h);
- }
- }
- }
-
- Integer currX = data.getCurrX();
- Integer currY = data.getCurrY();
-
- if (currX != null && currY != null) {
- gc.useStyle(DrawingStyle.DROP_ZONE_ACTIVE);
-
- int x = currX;
- int y = currY;
-
- Rect be = elements[0].getBounds();
-
- // Draw a clear line at the closest drop zone (unless we're over the
- // dragged element itself)
- if (data.getInsertPos() != selfPos || selfPos == -1) {
- gc.useStyle(DrawingStyle.DROP_PREVIEW);
- if (data.getWidth() != null) {
- int width = data.getWidth();
- int fromX = x - width / 2;
- int toX = x + width / 2;
- gc.drawLine(fromX, y, toX, y);
- } else if (data.getHeight() != null) {
- int height = data.getHeight();
- int fromY = y - height / 2;
- int toY = y + height / 2;
- gc.drawLine(x, fromY, x, toY);
- }
- }
-
- if (be.isValid()) {
- boolean isLast = data.isLastPosition();
-
- // At least the first element has a bound. Draw rectangles for
- // all dropped elements with valid bounds, offset at the drop
- // point.
- int offsetX;
- int offsetY;
- if (isVertical) {
- offsetX = b.x - be.x;
- offsetY = currY - be.y - (isLast ? 0 : (be.h / 2));
-
- } else {
- offsetX = currX - be.x - (isLast ? 0 : (be.w / 2));
- offsetY = b.y - be.y;
- }
-
- gc.useStyle(DrawingStyle.DROP_PREVIEW);
- for (IDragElement element : elements) {
- Rect bounds = element.getBounds();
- if (bounds.isValid() && (bounds.w > b.w || bounds.h > b.h) &&
- node.getChildren().length == 0) {
- // The bounds of the child does not fully fit inside the target.
- // Limit the bounds to the layout bounds (but only when there
- // are no children, since otherwise positioning around the existing
- // children gets difficult)
- final int px, py, pw, ph;
- if (bounds.w > b.w) {
- px = b.x;
- pw = b.w;
- } else {
- px = bounds.x + offsetX;
- pw = bounds.w;
- }
- if (bounds.h > b.h) {
- py = b.y;
- ph = b.h;
- } else {
- py = bounds.y + offsetY;
- ph = bounds.h;
- }
- Rect within = new Rect(px, py, pw, ph);
- gc.drawRect(within);
- } else {
- drawElement(gc, element, offsetX, offsetY);
- }
- }
- }
- }
- }
-
- @Override
- public DropFeedback onDropMove(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback, @NonNull Point p) {
- Rect b = targetNode.getBounds();
- if (!b.isValid()) {
- return feedback;
- }
-
- LinearDropData data = (LinearDropData) feedback.userData;
- boolean isVertical = data.isVertical();
-
- int bestDist = Integer.MAX_VALUE;
- int bestIndex = Integer.MIN_VALUE;
- Integer bestPos = null;
-
- for (MatchPos index : data.getIndexes()) {
- int i = index.getDistance();
- int pos = index.getPosition();
- int dist = (isVertical ? p.y : p.x) - i;
- if (dist < 0)
- dist = -dist;
- if (dist < bestDist) {
- bestDist = dist;
- bestIndex = i;
- bestPos = pos;
- if (bestDist <= 0)
- break;
- }
- }
-
- if (bestIndex != Integer.MIN_VALUE) {
- Integer oldX = data.getCurrX();
- Integer oldY = data.getCurrY();
-
- if (isVertical) {
- data.setCurrX(b.x + b.w / 2);
- data.setCurrY(bestIndex);
- data.setWidth(b.w);
- data.setHeight(null);
- } else {
- data.setCurrX(bestIndex);
- data.setCurrY(b.y + b.h / 2);
- data.setWidth(null);
- data.setHeight(b.h);
- }
-
- data.setInsertPos(bestPos);
-
- feedback.requestPaint = !equals(oldX, data.getCurrX())
- || !equals(oldY, data.getCurrY());
- }
-
- return feedback;
- }
-
- private static boolean equals(Integer i1, Integer i2) {
- if (i1 == i2) {
- return true;
- } else if (i1 != null) {
- return i1.equals(i2);
- } else {
- // We know i2 != null
- return i2.equals(i1);
- }
- }
-
- @Override
- public void onDropLeave(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback) {
- // ignore
- }
-
- @Override
- public void onDropped(final @NonNull INode targetNode, final @NonNull IDragElement[] elements,
- final @Nullable DropFeedback feedback, final @NonNull Point p) {
-
- LinearDropData data = (LinearDropData) feedback.userData;
- final int initialInsertPos = data.getInsertPos();
- insertAt(targetNode, elements, feedback.isCopy || !feedback.sameCanvas, initialInsertPos);
- }
-
- @Override
- public void onChildInserted(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- if (insertType == InsertType.MOVE_WITHIN) {
- // Don't adjust widths/heights/weights when just moving within a single
- // LinearLayout
- return;
- }
-
- // Attempt to set fill-properties on newly added views such that for example,
- // in a vertical layout, a text field defaults to filling horizontally, but not
- // vertically.
- String fqcn = node.getFqcn();
- IViewMetadata metadata = mRulesEngine.getMetadata(fqcn);
- if (metadata != null) {
- boolean vertical = isVertical(parent);
- FillPreference fill = metadata.getFillPreference();
- String fillParent = getFillParentValueName();
- if (fill.fillHorizontally(vertical)) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
- } else if (!vertical && fill == FillPreference.WIDTH_IN_VERTICAL) {
- // In a horizontal layout, make views that would fill horizontally in a
- // vertical layout have a non-zero weight instead. This will make the item
- // fill but only enough to allow other views to be shown as well.
- // (However, for drags within the same layout we do not touch
- // the weight, since it might already have been tweaked to a particular
- // value)
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, VALUE_1);
- }
- if (fill.fillVertically(vertical)) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, fillParent);
- }
- }
-
- // If you insert into a layout that already is using layout weights,
- // and all the layout weights are the same (nonzero) value, then use
- // the same weight for this new layout as well. Also duplicate the 0dip/0px/0dp
- // sizes, if used.
- boolean duplicateWeight = true;
- boolean duplicate0dip = true;
- String sameWeight = null;
- String sizeAttribute = isVertical(parent) ? ATTR_LAYOUT_HEIGHT : ATTR_LAYOUT_WIDTH;
- for (INode target : parent.getChildren()) {
- if (target == node) {
- continue;
- }
- String weight = target.getStringAttr(ANDROID_URI, ATTR_LAYOUT_WEIGHT);
- if (weight == null || weight.length() == 0) {
- duplicateWeight = false;
- break;
- } else if (sameWeight != null && !sameWeight.equals(weight)) {
- duplicateWeight = false;
- } else {
- sameWeight = weight;
- }
- String size = target.getStringAttr(ANDROID_URI, sizeAttribute);
- if (size != null && !size.startsWith("0")) { //$NON-NLS-1$
- duplicate0dip = false;
- break;
- }
- }
- if (duplicateWeight && sameWeight != null) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, sameWeight);
- if (duplicate0dip) {
- node.setAttribute(ANDROID_URI, sizeAttribute, VALUE_ZERO_DP);
- }
- }
- }
-
- /** A possible match position */
- private static class MatchPos {
- /** The pixel distance */
- private int mDistance;
- /** The position among siblings */
- private int mPosition;
-
- public MatchPos(int distance, int position) {
- mDistance = distance;
- mPosition = position;
- }
-
- @Override
- public String toString() {
- return "MatchPos [distance=" + mDistance //$NON-NLS-1$
- + ", position=" + mPosition //$NON-NLS-1$
- + "]"; //$NON-NLS-1$
- }
-
- private int getDistance() {
- return mDistance;
- }
-
- private int getPosition() {
- return mPosition;
- }
- }
-
- private static class LinearDropData {
- /** Vertical layout? */
- private final boolean mVertical;
-
- /** Insert points (pixels + index) */
- private final List<MatchPos> mIndexes;
-
- /** Number of insert positions in the target node */
- private final int mNumPositions;
-
- /** Current marker X position */
- private Integer mCurrX;
-
- /** Current marker Y position */
- private Integer mCurrY;
-
- /** Position of the dragged element in this layout (or
- -1 if the dragged element is from elsewhere) */
- private final int mSelfPos;
-
- /** Current drop insert index (-1 for "at the end") */
- private int mInsertPos = -1;
-
- /** width of match line if it's a horizontal one */
- private Integer mWidth;
-
- /** height of match line if it's a vertical one */
- private Integer mHeight;
-
- public LinearDropData(List<MatchPos> indexes, int numPositions,
- boolean isVertical, int selfPos) {
- mIndexes = indexes;
- mNumPositions = numPositions;
- mVertical = isVertical;
- mSelfPos = selfPos;
- }
-
- @Override
- public String toString() {
- return "LinearDropData [currX=" + mCurrX //$NON-NLS-1$
- + ", currY=" + mCurrY //$NON-NLS-1$
- + ", height=" + mHeight //$NON-NLS-1$
- + ", indexes=" + mIndexes //$NON-NLS-1$
- + ", insertPos=" + mInsertPos //$NON-NLS-1$
- + ", isVertical=" + mVertical //$NON-NLS-1$
- + ", selfPos=" + mSelfPos //$NON-NLS-1$
- + ", width=" + mWidth //$NON-NLS-1$
- + "]"; //$NON-NLS-1$
- }
-
- private boolean isVertical() {
- return mVertical;
- }
-
- private void setCurrX(Integer currX) {
- mCurrX = currX;
- }
-
- private Integer getCurrX() {
- return mCurrX;
- }
-
- private void setCurrY(Integer currY) {
- mCurrY = currY;
- }
-
- private Integer getCurrY() {
- return mCurrY;
- }
-
- private int getSelfPos() {
- return mSelfPos;
- }
-
- private void setInsertPos(int insertPos) {
- mInsertPos = insertPos;
- }
-
- private int getInsertPos() {
- return mInsertPos;
- }
-
- private List<MatchPos> getIndexes() {
- return mIndexes;
- }
-
- private void setWidth(Integer width) {
- mWidth = width;
- }
-
- private Integer getWidth() {
- return mWidth;
- }
-
- private void setHeight(Integer height) {
- mHeight = height;
- }
-
- private Integer getHeight() {
- return mHeight;
- }
-
- /**
- * Returns true if we are inserting into the last position
- *
- * @return true if we are inserting into the last position
- */
- public boolean isLastPosition() {
- return mInsertPos == mNumPositions - 1;
- }
- }
-
- /** Custom resize state used during linear layout resizing */
- private class LinearResizeState extends ResizeState {
- /** Whether the node should be assigned a new weight */
- public boolean useWeight;
- /** Weight sum to be applied to the parent */
- private float mNewWeightSum;
- /** The weight to be set on the node (provided {@link #useWeight} is true) */
- private float mWeight;
- /** Map from nodes to preferred bounds of nodes where the weights have been cleared */
- public final Map<INode, Rect> unweightedSizes;
- /** Total required size required by the siblings <b>without</b> weights */
- public int totalLength;
- /** List of nodes which should have their weights cleared */
- public List<INode> mClearWeights;
-
- private LinearResizeState(BaseLayoutRule rule, INode layout, Object layoutView,
- INode node) {
- super(rule, layout, layoutView, node);
-
- unweightedSizes = mRulesEngine.measureChildren(layout,
- new IClientRulesEngine.AttributeFilter() {
- @Override
- public String getAttribute(@NonNull INode n, @Nullable String namespace,
- @NonNull String localName) {
- // Clear out layout weights; we need to measure the unweighted sizes
- // of the children
- if (ATTR_LAYOUT_WEIGHT.equals(localName)
- && SdkConstants.NS_RESOURCES.equals(namespace)) {
- return ""; //$NON-NLS-1$
- }
-
- return null;
- }
- });
-
- // Compute total required size required by the siblings *without* weights
- totalLength = 0;
- final boolean isVertical = isVertical(layout);
- for (Map.Entry<INode, Rect> entry : unweightedSizes.entrySet()) {
- Rect preferredSize = entry.getValue();
- if (isVertical) {
- totalLength += preferredSize.h;
- } else {
- totalLength += preferredSize.w;
- }
- }
- }
-
- /** Resets the computed state */
- void reset() {
- mNewWeightSum = -1;
- useWeight = false;
- mClearWeights = null;
- }
-
- /** Sets a weight to be applied to the node */
- void setWeight(float weight) {
- useWeight = true;
- mWeight = weight;
- }
-
- /** Sets a weight sum to be applied to the parent layout */
- void setWeightSum(float weightSum) {
- mNewWeightSum = weightSum;
- }
-
- /** Marks that the given node should be cleared when applying the new size */
- void clearWeight(INode n) {
- if (mClearWeights == null) {
- mClearWeights = new ArrayList<INode>();
- }
- mClearWeights.add(n);
- }
-
- /** Applies the state to the nodes */
- public void apply() {
- assert useWeight;
-
- String value = mWeight > 0 ? formatFloatAttribute(mWeight) : null;
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, value);
-
- if (mClearWeights != null) {
- for (INode n : mClearWeights) {
- if (getWeight(n) > 0.0f) {
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, null);
- }
- }
- }
-
- if (mNewWeightSum > 0.0) {
- layout.setAttribute(ANDROID_URI, ATTR_WEIGHT_SUM,
- formatFloatAttribute(mNewWeightSum));
- }
- }
- }
-
- @Override
- protected ResizeState createResizeState(INode layout, Object layoutView, INode node) {
- return new LinearResizeState(this, layout, layoutView, node);
- }
-
- protected void updateResizeState(LinearResizeState resizeState, final INode node, INode layout,
- Rect oldBounds, Rect newBounds, SegmentType horizontalEdge,
- SegmentType verticalEdge) {
- // Update the resize state.
- // This method attempts to compute a new layout weight to be used in the direction
- // of the linear layout. If the superclass has already determined that we can snap to
- // a wrap_content or match_parent boundary, we prefer that. Otherwise, we attempt to
- // compute a layout weight - which can fail if the size is too big (not enough room),
- // or if the size is too small (smaller than the natural width of the node), and so on.
- // In that case this method just aborts, which will leave the resize state object
- // in such a state that it will call the superclass to resize instead, which will fall
- // back to device independent pixel sizing.
- resizeState.reset();
-
- if (oldBounds.equals(newBounds)) {
- return;
- }
-
- // If we're setting the width/height to wrap_content/match_parent in the dimension of the
- // linear layout, then just apply wrap_content and clear weights.
- boolean isVertical = isVertical(layout);
- if (!isVertical && verticalEdge != null) {
- if (resizeState.wrapWidth || resizeState.fillWidth) {
- resizeState.clearWeight(node);
- return;
- }
- if (newBounds.w == oldBounds.w) {
- return;
- }
- }
-
- if (isVertical && horizontalEdge != null) {
- if (resizeState.wrapHeight || resizeState.fillHeight) {
- resizeState.clearWeight(node);
- return;
- }
- if (newBounds.h == oldBounds.h) {
- return;
- }
- }
-
- // Compute weight sum
- float sum = getWeightSum(layout);
- if (sum <= 0.0f) {
- sum = 1.0f;
- resizeState.setWeightSum(sum);
- }
-
- // If the new size of the node is smaller than its preferred/wrap_content size,
- // then we cannot use weights to size it; switch to pixel-based sizing instead
- Map<INode, Rect> sizes = resizeState.unweightedSizes;
- Rect nodePreferredSize = sizes.get(node);
- if (nodePreferredSize != null) {
- if (horizontalEdge != null && newBounds.h < nodePreferredSize.h ||
- verticalEdge != null && newBounds.w < nodePreferredSize.w) {
- return;
- }
- }
-
- Rect layoutBounds = layout.getBounds();
- int remaining = (isVertical ? layoutBounds.h : layoutBounds.w) - resizeState.totalLength;
- Rect nodeBounds = sizes.get(node);
- if (nodeBounds == null) {
- return;
- }
-
- if (remaining > 0) {
- int missing = 0;
- if (isVertical) {
- if (newBounds.h > nodeBounds.h) {
- missing = newBounds.h - nodeBounds.h;
- } else if (newBounds.h > resizeState.wrapBounds.h) {
- // The weights concern how much space to ADD to the view.
- // What if we have resized it to a size *smaller* than its current
- // size without the weight delta? This can happen if you for example
- // have set a hardcoded size, such as 500dp, and then size it to some
- // smaller size.
- missing = newBounds.h - resizeState.wrapBounds.h;
- remaining += nodeBounds.h - resizeState.wrapBounds.h;
- resizeState.wrapHeight = true;
- }
- } else {
- if (newBounds.w > nodeBounds.w) {
- missing = newBounds.w - nodeBounds.w;
- } else if (newBounds.w > resizeState.wrapBounds.w) {
- missing = newBounds.w - resizeState.wrapBounds.w;
- remaining += nodeBounds.w - resizeState.wrapBounds.w;
- resizeState.wrapWidth = true;
- }
- }
- if (missing > 0) {
- // (weight / weightSum) * remaining = missing, so
- // weight = missing * weightSum / remaining
- float weight = missing * sum / remaining;
- resizeState.setWeight(weight);
- }
- }
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * Overridden in this layout in order to make resizing affect the layout_weight
- * attribute instead of the layout_width (for horizontal LinearLayouts) or
- * layout_height (for vertical LinearLayouts).
- */
- @Override
- protected void setNewSizeBounds(ResizeState state, final INode node, INode layout,
- Rect oldBounds, Rect newBounds, SegmentType horizontalEdge,
- SegmentType verticalEdge) {
- LinearResizeState resizeState = (LinearResizeState) state;
- updateResizeState(resizeState, node, layout, oldBounds, newBounds,
- horizontalEdge, verticalEdge);
-
- if (resizeState.useWeight) {
- resizeState.apply();
-
- // Handle resizing in the opposite dimension of the layout
- final boolean isVertical = isVertical(layout);
- if (!isVertical && horizontalEdge != null) {
- if (newBounds.h != oldBounds.h || resizeState.wrapHeight
- || resizeState.fillHeight) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT,
- resizeState.getHeightAttribute());
- }
- }
- if (isVertical && verticalEdge != null) {
- if (newBounds.w != oldBounds.w || resizeState.wrapWidth || resizeState.fillWidth) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH,
- resizeState.getWidthAttribute());
- }
- }
- } else {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, null);
- super.setNewSizeBounds(resizeState, node, layout, oldBounds, newBounds,
- horizontalEdge, verticalEdge);
- }
- }
-
- @Override
- protected String getResizeUpdateMessage(ResizeState state, INode child, INode parent,
- Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) {
- LinearResizeState resizeState = (LinearResizeState) state;
- updateResizeState(resizeState, child, parent, child.getBounds(), newBounds,
- horizontalEdge, verticalEdge);
-
- if (resizeState.useWeight) {
- String weight = formatFloatAttribute(resizeState.mWeight);
- String dimension = String.format("weight %1$s", weight);
-
- String width;
- String height;
- if (isVertical(parent)) {
- width = resizeState.getWidthAttribute();
- height = dimension;
- } else {
- width = dimension;
- height = resizeState.getHeightAttribute();
- }
-
- if (horizontalEdge == null) {
- return width;
- } else if (verticalEdge == null) {
- return height;
- } else {
- // U+00D7: Unicode for multiplication sign
- return String.format("%s \u00D7 %s", width, height);
- }
- } else {
- return super.getResizeUpdateMessage(state, child, parent, newBounds,
- horizontalEdge, verticalEdge);
- }
- }
-
- /**
- * Returns the layout weight of of the given child of a LinearLayout, or 0.0 if it
- * does not define a weight
- */
- private static float getWeight(INode linearLayoutChild) {
- String weight = linearLayoutChild.getStringAttr(ANDROID_URI, ATTR_LAYOUT_WEIGHT);
- if (weight != null && weight.length() > 0) {
- try {
- return Float.parseFloat(weight);
- } catch (NumberFormatException nfe) {
- AdtPlugin.log(nfe, "Invalid weight %1$s", weight);
- }
- }
-
- return 0.0f;
- }
-
- /**
- * Returns the sum of all the layout weights of the children in the given LinearLayout
- *
- * @param linearLayout the layout to compute the total sum for
- * @return the total sum of all the layout weights in the given layout
- */
- private static float getWeightSum(INode linearLayout) {
- String weightSum = linearLayout.getStringAttr(ANDROID_URI,
- ATTR_WEIGHT_SUM);
- float sum = -1.0f;
- if (weightSum != null) {
- // Distribute
- try {
- sum = Float.parseFloat(weightSum);
- return sum;
- } catch (NumberFormatException nfe) {
- // Just keep using the default
- }
- }
-
- return getSumOfWeights(linearLayout);
- }
-
- private static float getSumOfWeights(INode linearLayout) {
- float sum = 0.0f;
- for (INode child : linearLayout.getChildren()) {
- sum += getWeight(child);
- }
-
- return sum;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ListViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ListViewRule.java
deleted file mode 100644
index 70728c81d..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ListViewRule.java
+++ /dev/null
@@ -1,42 +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_LAYOUT_WIDTH;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for android.widget.ListView and all its derived classes such
- * as ExpandableListView.
- * This is the "root" rule, that is used whenever there is not more specific
- * rule to apply.
- */
-public class ListViewRule extends AdapterViewRule {
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, getFillParentValueName());
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MapViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MapViewRule.java
deleted file mode 100644
index 006661e57..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MapViewRule.java
+++ /dev/null
@@ -1,46 +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 com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for com.google.android.maps.MapView.
- * <p>
- * TODO: This class should be pulled out of the ADT and bundled with the add ons
- * (not the core jar but an optional tool jar)
- */
-public class MapViewRule extends BaseViewRule {
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- if (insertType.isCreate()) {
- node.setAttribute(ANDROID_URI, "android:apiKey", //$NON-NLS-1$
- "Your API key: see " + //$NON-NLS-1$
- "http://code.google.com/android/add-ons/google-apis/mapkey.html"); //$NON-NLS-1$
- }
- }
-
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MergeRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MergeRule.java
deleted file mode 100644
index 9cef9c4b3..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MergeRule.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2011 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 com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.RuleAction;
-
-import java.util.List;
-
-/**
- * Drop handler for the {@code <merge>} tag
- */
-public class MergeRule extends FrameLayoutRule {
- // The <merge> tag behaves a lot like the FrameLayout; all children are added
- // on top of each other at (0,0)
-
- @Override
- public void addContextMenuActions(@NonNull List<RuleAction> actions,
- final @NonNull INode selectedNode) {
- // Deliberately ignore super.getContextMenu(); we don't want to attempt to list
- // properties for the <merge> tag
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/PropertyCallback.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/PropertyCallback.java
deleted file mode 100644
index da2614eef..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/PropertyCallback.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2011 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 com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.IMenuCallback;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.INodeHandler;
-import com.android.ide.common.api.RuleAction;
-
-import java.util.List;
-
-/**
- * Convenience implementation of {@link IMenuCallback} which can be used to set a
- * particular property to the new valueId or newValue passed from the {@link IMenuCallback}
- */
-public class PropertyCallback implements IMenuCallback {
- private final List<? extends INode> mTargetNodes;
- private final String mUndoLabel;
- private final String mUri;
- private final String mAttribute;
-
- /**
- * Creates a new property callback.
- *
- * @param targetNodes the nodes to apply the property to, or null to use the
- * nodes pass into the
- * {@link #action(RuleAction, List, String, Boolean)} method.
- * @param undoLabel the label to use for the undo action
- * @param uri the attribute URI to apply
- * @param attribute the attribute name to apply
- */
- public PropertyCallback(List<? extends INode> targetNodes, String undoLabel,
- String uri, String attribute) {
- super();
- mTargetNodes = targetNodes;
- mUndoLabel = undoLabel;
- mUri = uri;
- mAttribute = attribute;
- }
-
- // ---- Implements IMenuCallback ----
- @Override
- public void action(@NonNull RuleAction action, @NonNull List<? extends INode> selectedNodes,
- final @Nullable String valueId, final @Nullable Boolean newValue) {
- if (mTargetNodes != null && mTargetNodes.size() > 0) {
- selectedNodes = mTargetNodes;
- }
- if (selectedNodes == null || selectedNodes.size() == 0) {
- return;
- }
- final List<? extends INode> nodes = selectedNodes;
- selectedNodes.get(0).editXml(mUndoLabel, new INodeHandler() {
- @Override
- public void handle(@NonNull INode n) {
- for (INode targetNode : nodes) {
- if (valueId != null) {
- targetNode.setAttribute(mUri, mAttribute, valueId);
- } else {
- assert newValue != null;
- targetNode.setAttribute(mUri, mAttribute, Boolean.toString(newValue));
- }
- }
- }
- });
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/PropertySettingNodeHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/PropertySettingNodeHandler.java
deleted file mode 100644
index 13c8842ed..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/PropertySettingNodeHandler.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2011 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 com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.INodeHandler;
-
-/**
- * A convenience implementation of {@link INodeHandler} for setting a given attribute to a
- * given value on a particular node.
- */
-class PropertySettingNodeHandler implements INodeHandler {
- private final String mNamespaceUri;
- private final String mAttribute;
- private final String mValue;
-
- PropertySettingNodeHandler(String namespaceUri, String attribute, String value) {
- super();
- mNamespaceUri = namespaceUri;
- mAttribute = attribute;
- mValue = value;
- }
-
- @Override
- public void handle(@NonNull INode node) {
- node.setAttribute(mNamespaceUri, mAttribute, mValue);
- }
-} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/QuickContactBadgeRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/QuickContactBadgeRule.java
deleted file mode 100644
index 0164794d3..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/QuickContactBadgeRule.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2011 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 com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for android.widget.QuickContactBadgeRule.
- */
-public class QuickContactBadgeRule extends ImageViewRule {
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- // Deliberately override onCreate such that we don't populate a default
- // image; at design time layoutlib will supply the system default contacts
- // image.
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/RadioGroupRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/RadioGroupRule.java
deleted file mode 100644
index c9aa20768..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/RadioGroupRule.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2011 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.ATTR_CHECKED;
-import static com.android.SdkConstants.ATTR_ID;
-import static com.android.SdkConstants.VALUE_TRUE;
-
-
-import com.android.SdkConstants;
-import static com.android.SdkConstants.ANDROID_URI;
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for android.widget.RadioGroup which initializes the radio group
- * with some radio buttons
- */
-public class RadioGroupRule extends LinearLayoutRule {
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- if (insertType.isCreate()) {
- for (int i = 0; i < 3; i++) {
- INode handle = node.appendChild(SdkConstants.FQCN_RADIO_BUTTON);
- handle.setAttribute(ANDROID_URI, ATTR_ID, String.format("@+id/radio%d", i));
- if (i == 0) {
- handle.setAttribute(ANDROID_URI, ATTR_CHECKED, VALUE_TRUE);
- }
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/RelativeLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/RelativeLayoutRule.java
deleted file mode 100644
index b4bc86978..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/RelativeLayoutRule.java
+++ /dev/null
@@ -1,413 +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_GRAVITY;
-import static com.android.SdkConstants.ATTR_LAYOUT_ABOVE;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BASELINE;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BOTTOM;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_LEFT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_BOTTOM;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_LEFT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_RIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_TOP;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_RIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_TOP;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_WITH_PARENT_MISSING;
-import static com.android.SdkConstants.ATTR_LAYOUT_BELOW;
-import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_HORIZONTAL;
-import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_IN_PARENT;
-import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_VERTICAL;
-import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX;
-import static com.android.SdkConstants.ATTR_LAYOUT_TO_LEFT_OF;
-import static com.android.SdkConstants.ATTR_LAYOUT_TO_RIGHT_OF;
-import static com.android.SdkConstants.VALUE_TRUE;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.IMenuCallback;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.INodeHandler;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.RuleAction;
-import com.android.ide.common.api.SegmentType;
-import com.android.ide.common.layout.relative.ConstraintPainter;
-import com.android.ide.common.layout.relative.DeletionHandler;
-import com.android.ide.common.layout.relative.GuidelinePainter;
-import com.android.ide.common.layout.relative.MoveHandler;
-import com.android.ide.common.layout.relative.ResizeHandler;
-import com.android.utils.Pair;
-
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-/**
- * An {@link IViewRule} for android.widget.RelativeLayout and all its derived
- * classes.
- */
-public class RelativeLayoutRule extends BaseLayoutRule {
- private static final String ACTION_SHOW_STRUCTURE = "_structure"; //$NON-NLS-1$
- private static final String ACTION_SHOW_CONSTRAINTS = "_constraints"; //$NON-NLS-1$
- private static final String ACTION_CENTER_VERTICAL = "_centerVert"; //$NON-NLS-1$
- private static final String ACTION_CENTER_HORIZONTAL = "_centerHoriz"; //$NON-NLS-1$
- private static final URL ICON_CENTER_VERTICALLY =
- RelativeLayoutRule.class.getResource("centerVertically.png"); //$NON-NLS-1$
- private static final URL ICON_CENTER_HORIZONTALLY =
- RelativeLayoutRule.class.getResource("centerHorizontally.png"); //$NON-NLS-1$
- private static final URL ICON_SHOW_STRUCTURE =
- BaseLayoutRule.class.getResource("structure.png"); //$NON-NLS-1$
- private static final URL ICON_SHOW_CONSTRAINTS =
- BaseLayoutRule.class.getResource("constraints.png"); //$NON-NLS-1$
-
- public static boolean sShowStructure = false;
- public static boolean sShowConstraints = true;
-
- // ==== Selection ====
-
- @Override
- public List<String> getSelectionHint(@NonNull INode parentNode, @NonNull INode childNode) {
- List<String> infos = new ArrayList<String>(18);
- addAttr(ATTR_LAYOUT_ABOVE, childNode, infos);
- addAttr(ATTR_LAYOUT_BELOW, childNode, infos);
- addAttr(ATTR_LAYOUT_TO_LEFT_OF, childNode, infos);
- addAttr(ATTR_LAYOUT_TO_RIGHT_OF, childNode, infos);
- addAttr(ATTR_LAYOUT_ALIGN_BASELINE, childNode, infos);
- addAttr(ATTR_LAYOUT_ALIGN_TOP, childNode, infos);
- addAttr(ATTR_LAYOUT_ALIGN_BOTTOM, childNode, infos);
- addAttr(ATTR_LAYOUT_ALIGN_LEFT, childNode, infos);
- addAttr(ATTR_LAYOUT_ALIGN_RIGHT, childNode, infos);
- addAttr(ATTR_LAYOUT_ALIGN_PARENT_TOP, childNode, infos);
- addAttr(ATTR_LAYOUT_ALIGN_PARENT_BOTTOM, childNode, infos);
- addAttr(ATTR_LAYOUT_ALIGN_PARENT_LEFT, childNode, infos);
- addAttr(ATTR_LAYOUT_ALIGN_PARENT_RIGHT, childNode, infos);
- addAttr(ATTR_LAYOUT_ALIGN_WITH_PARENT_MISSING, childNode, infos);
- addAttr(ATTR_LAYOUT_CENTER_HORIZONTAL, childNode, infos);
- addAttr(ATTR_LAYOUT_CENTER_IN_PARENT, childNode, infos);
- addAttr(ATTR_LAYOUT_CENTER_VERTICAL, childNode, infos);
-
- return infos;
- }
-
- private void addAttr(String propertyName, INode childNode, List<String> infos) {
- String a = childNode.getStringAttr(ANDROID_URI, propertyName);
- if (a != null && a.length() > 0) {
- // Display the layout parameters without the leading layout_ prefix
- // and id references without the @+id/ prefix
- if (propertyName.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX)) {
- propertyName = propertyName.substring(ATTR_LAYOUT_RESOURCE_PREFIX.length());
- }
- a = stripIdPrefix(a);
- String s = propertyName + ": " + a;
- infos.add(s);
- }
- }
-
- @Override
- public void paintSelectionFeedback(@NonNull IGraphics graphics, @NonNull INode parentNode,
- @NonNull List<? extends INode> childNodes, @Nullable Object view) {
- super.paintSelectionFeedback(graphics, parentNode, childNodes, view);
-
- boolean showDependents = true;
- if (sShowStructure) {
- childNodes = Arrays.asList(parentNode.getChildren());
- // Avoid painting twice - both as incoming and outgoing
- showDependents = false;
- } else if (!sShowConstraints) {
- return;
- }
-
- ConstraintPainter.paintSelectionFeedback(graphics, parentNode, childNodes, showDependents);
- }
-
- // ==== Drag'n'drop support ====
-
- @Override
- public DropFeedback onDropEnter(@NonNull INode targetNode, @Nullable Object targetView,
- @Nullable IDragElement[] elements) {
- return new DropFeedback(new MoveHandler(targetNode, elements, mRulesEngine),
- new GuidelinePainter());
- }
-
- @Override
- public DropFeedback onDropMove(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback, @NonNull Point p) {
- if (elements == null || elements.length == 0 || feedback == null) {
- return null;
- }
-
- MoveHandler state = (MoveHandler) feedback.userData;
- int offsetX = p.x + (feedback.dragBounds != null ? feedback.dragBounds.x : 0);
- int offsetY = p.y + (feedback.dragBounds != null ? feedback.dragBounds.y : 0);
- state.updateMove(feedback, elements, offsetX, offsetY, feedback.modifierMask);
-
- // Or maybe only do this if the results changed...
- feedback.requestPaint = true;
-
- return feedback;
- }
-
- @Override
- public void onDropLeave(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback) {
- }
-
- @Override
- public void onDropped(final @NonNull INode targetNode, final @NonNull IDragElement[] elements,
- final @Nullable DropFeedback feedback, final @NonNull Point p) {
- if (feedback == null) {
- return;
- }
-
- final MoveHandler state = (MoveHandler) feedback.userData;
-
- final Map<String, Pair<String, String>> idMap = getDropIdMap(targetNode, elements,
- feedback.isCopy || !feedback.sameCanvas);
-
- targetNode.editXml("Dropped", new INodeHandler() {
- @Override
- public void handle(@NonNull INode n) {
- int index = -1;
-
- // Remove cycles
- state.removeCycles();
-
- // Now write the new elements.
- INode previous = null;
- for (IDragElement element : elements) {
- String fqcn = element.getFqcn();
-
- // index==-1 means to insert at the end.
- // Otherwise increment the insertion position.
- if (index >= 0) {
- index++;
- }
-
- INode newChild = targetNode.insertChildAt(fqcn, index);
-
- // Copy all the attributes, modifying them as needed.
- addAttributes(newChild, element, idMap, BaseLayoutRule.DEFAULT_ATTR_FILTER);
- addInnerElements(newChild, element, idMap);
-
- if (previous == null) {
- state.applyConstraints(newChild);
- previous = newChild;
- } else {
- // Arrange the nodes next to each other, depending on which
- // edge we are attaching to. For example, if attaching to the
- // top edge, arrange the subsequent nodes in a column below it.
- //
- // TODO: Try to do something smarter here where we detect
- // constraints between the dragged edges, and we preserve these.
- // We have to do this carefully though because if the
- // constraints go through some other nodes not part of the
- // selection, this doesn't work right, and you might be
- // dragging several connected components, which we'd then
- // need to stitch together such that they are all visible.
-
- state.attachPrevious(previous, newChild);
- previous = newChild;
- }
- }
- }
- });
- }
-
- @Override
- public void onChildInserted(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- // TODO: Handle more generically some way to ensure that widgets with no
- // intrinsic size get some minimum size until they are attached on multiple
- // opposing sides.
- //String fqcn = node.getFqcn();
- //if (fqcn.equals(FQCN_EDIT_TEXT)) {
- // node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, "100dp"); //$NON-NLS-1$
- //}
- }
-
- @Override
- public void onRemovingChildren(@NonNull List<INode> deleted, @NonNull INode parent,
- boolean moved) {
- super.onRemovingChildren(deleted, parent, moved);
-
- if (!moved) {
- DeletionHandler handler = new DeletionHandler(deleted, Collections.<INode>emptyList(),
- parent);
- handler.updateConstraints();
- }
- }
-
- // ==== Resize Support ====
-
- @Override
- public DropFeedback onResizeBegin(@NonNull INode child, @NonNull INode parent,
- @Nullable SegmentType horizontalEdgeType, @Nullable SegmentType verticalEdgeType,
- @Nullable Object childView, @Nullable Object parentView) {
- ResizeHandler state = new ResizeHandler(parent, child, mRulesEngine,
- horizontalEdgeType, verticalEdgeType);
- return new DropFeedback(state, new GuidelinePainter());
- }
-
- @Override
- public void onResizeUpdate(@Nullable DropFeedback feedback, @NonNull INode child,
- @NonNull INode parent, @NonNull Rect newBounds,
- int modifierMask) {
- if (feedback == null) {
- return;
- }
-
- ResizeHandler state = (ResizeHandler) feedback.userData;
- state.updateResize(feedback, child, newBounds, modifierMask);
- }
-
- @Override
- public void onResizeEnd(@Nullable DropFeedback feedback, @NonNull INode child,
- @NonNull INode parent, final @NonNull Rect newBounds) {
- if (feedback == null) {
- return;
- }
- final ResizeHandler state = (ResizeHandler) feedback.userData;
-
- child.editXml("Resize", new INodeHandler() {
- @Override
- public void handle(@NonNull INode n) {
- state.removeCycles();
- state.applyConstraints(n);
- }
- });
- }
-
- // ==== Layout Actions Bar ====
-
- @Override
- public void addLayoutActions(
- @NonNull List<RuleAction> actions,
- final @NonNull INode parentNode,
- final @NonNull List<? extends INode> children) {
- super.addLayoutActions(actions, parentNode, children);
-
- actions.add(createGravityAction(Collections.<INode>singletonList(parentNode),
- ATTR_GRAVITY));
- actions.add(RuleAction.createSeparator(25));
- actions.add(createMarginAction(parentNode, children));
-
- IMenuCallback callback = new IMenuCallback() {
- @Override
- public void action(@NonNull RuleAction action,
- @NonNull List<? extends INode> selectedNodes,
- final @Nullable String valueId,
- final @Nullable Boolean newValue) {
- final String id = action.getId();
- if (id.equals(ACTION_CENTER_VERTICAL)|| id.equals(ACTION_CENTER_HORIZONTAL)) {
- parentNode.editXml("Center", new INodeHandler() {
- @Override
- public void handle(@NonNull INode n) {
- if (id.equals(ACTION_CENTER_VERTICAL)) {
- for (INode child : children) {
- centerVertically(child);
- }
- } else if (id.equals(ACTION_CENTER_HORIZONTAL)) {
- for (INode child : children) {
- centerHorizontally(child);
- }
- }
- mRulesEngine.redraw();
- }
-
- });
- } else if (id.equals(ACTION_SHOW_CONSTRAINTS)) {
- sShowConstraints = !sShowConstraints;
- mRulesEngine.redraw();
- } else {
- assert id.equals(ACTION_SHOW_STRUCTURE);
- sShowStructure = !sShowStructure;
- mRulesEngine.redraw();
- }
- }
- };
-
- // Centering actions
- if (children != null && children.size() > 0) {
- actions.add(RuleAction.createSeparator(150));
- actions.add(RuleAction.createAction(ACTION_CENTER_VERTICAL, "Center Vertically",
- callback, ICON_CENTER_VERTICALLY, 160, false));
- actions.add(RuleAction.createAction(ACTION_CENTER_HORIZONTAL, "Center Horizontally",
- callback, ICON_CENTER_HORIZONTALLY, 170, false));
- }
-
- actions.add(RuleAction.createSeparator(80));
- actions.add(RuleAction.createToggle(ACTION_SHOW_CONSTRAINTS, "Show Constraints",
- sShowConstraints, callback, ICON_SHOW_CONSTRAINTS, 180, false));
- actions.add(RuleAction.createToggle(ACTION_SHOW_STRUCTURE, "Show All Relationships",
- sShowStructure, callback, ICON_SHOW_STRUCTURE, 190, false));
- }
-
- private void centerHorizontally(INode node) {
- // Clear horizontal-oriented attributes from the node
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_PARENT_LEFT, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_LEFT, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_TO_RIGHT_OF, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_HORIZONTAL, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_PARENT_RIGHT, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_RIGHT, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_TO_LEFT_OF, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_HORIZONTAL, null);
-
- if (VALUE_TRUE.equals(node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_CENTER_IN_PARENT))) {
- // Already done
- } else if (VALUE_TRUE.equals(node.getStringAttr(ANDROID_URI,
- ATTR_LAYOUT_CENTER_VERTICAL))) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_VERTICAL, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_IN_PARENT, VALUE_TRUE);
- } else {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_HORIZONTAL, VALUE_TRUE);
- }
- }
-
- private void centerVertically(INode node) {
- // Clear vertical-oriented attributes from the node
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_PARENT_TOP, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_TOP, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_BELOW, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_PARENT_BOTTOM, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_BOTTOM, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ABOVE, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_VERTICAL, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_BASELINE, null);
-
- // Center vertically
- if (VALUE_TRUE.equals(node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_CENTER_IN_PARENT))) {
- // ALready done
- } else if (VALUE_TRUE.equals(node.getStringAttr(ANDROID_URI,
- ATTR_LAYOUT_CENTER_HORIZONTAL))) {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_HORIZONTAL, null);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_IN_PARENT, VALUE_TRUE);
- } else {
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_VERTICAL, VALUE_TRUE);
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ResizeState.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ResizeState.java
deleted file mode 100644
index 42b9083ad..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ResizeState.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2011 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.VALUE_N_DP;
-import static com.android.SdkConstants.VALUE_WRAP_CONTENT;
-
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.Segment;
-import com.android.ide.common.api.SegmentType;
-
-/** State held during resizing operations */
-class ResizeState {
- /**
- * The associated rule
- */
- private final BaseLayoutRule mRule;
-
- /**
- * The node being resized
- */
- public final INode node;
-
- /**
- * The layout containing the resized node
- */
- public final INode layout;
-
- /** The proposed resized bounds of the node */
- public Rect bounds;
-
- /** The preferred wrap_content bounds of the node */
- public Rect wrapBounds;
-
- /** The suggested horizontal fill_parent guideline position */
- public Segment horizontalFillSegment;
-
- /** The suggested vertical fill_parent guideline position */
- public Segment verticalFillSegment;
-
- /** The type of horizontal edge being resized, or null */
- public SegmentType horizontalEdgeType;
-
- /** The type of vertical edge being resized, or null */
- public SegmentType verticalEdgeType;
-
- /** Whether the user has snapped to the wrap_content width */
- public boolean wrapWidth;
-
- /** Whether the user has snapped to the wrap_content height */
- public boolean wrapHeight;
-
- /** Whether the user has snapped to the match_parent width */
- public boolean fillWidth;
-
- /** Whether the user has snapped to the match_parent height */
- public boolean fillHeight;
-
- /** Custom field for use by subclasses */
- public Object clientData;
-
- /** Keyboard mask */
- public int modifierMask;
-
- /**
- * The actual view object for the layout containing the resizing operation,
- * or null if not known
- */
- public Object layoutView;
-
- /**
- * Constructs a new {@link ResizeState}
- *
- * @param rule the associated rule
- * @param layout the parent layout containing the resized node
- * @param layoutView the actual View instance for the layout, or null if not known
- * @param node the node being resized
- */
- ResizeState(BaseLayoutRule rule, INode layout, Object layoutView, INode node) {
- mRule = rule;
-
- this.layout = layout;
- this.node = node;
- this.layoutView = layoutView;
- }
-
- /**
- * Returns the width attribute to be set to match the new bounds
- *
- * @return the width string, never null
- */
- public String getWidthAttribute() {
- if (wrapWidth) {
- return VALUE_WRAP_CONTENT;
- } else if (fillWidth) {
- return mRule.getFillParentValueName();
- } else {
- return String.format(VALUE_N_DP, mRule.mRulesEngine.pxToDp(bounds.w));
- }
- }
-
- /**
- * Returns the height attribute to be set to match the new bounds
- *
- * @return the height string, never null
- */
- public String getHeightAttribute() {
- if (wrapHeight) {
- return VALUE_WRAP_CONTENT;
- } else if (fillHeight) {
- return mRule.getFillParentValueName();
- } else {
- return String.format(VALUE_N_DP, mRule.mRulesEngine.pxToDp(bounds.h));
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java
deleted file mode 100644
index 9f2b4ae6f..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java
+++ /dev/null
@@ -1,96 +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_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-import static com.android.SdkConstants.ATTR_ORIENTATION;
-import static com.android.SdkConstants.FQCN_LINEAR_LAYOUT;
-import static com.android.SdkConstants.VALUE_VERTICAL;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-
-/**
- * An {@link IViewRule} for android.widget.ScrollView.
- */
-public class ScrollViewRule extends FrameLayoutRule {
-
- @Override
- public void onChildInserted(@NonNull INode child, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onChildInserted(child, parent, insertType);
-
- // The child of the ScrollView should fill in both directions
- String fillParent = getFillParentValueName();
- child.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
- child.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, fillParent);
- }
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- if (insertType.isCreate()) {
- // Insert a default linear layout (which will in turn be registered as
- // a child of this node and the create child method above will set its
- // fill parent attributes, its id, etc.
- INode linear = node.appendChild(FQCN_LINEAR_LAYOUT);
- linear.setAttribute(ANDROID_URI, ATTR_ORIENTATION, VALUE_VERTICAL);
- }
- }
-
- @Override
- public DropFeedback onDropMove(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback, @NonNull Point p) {
- DropFeedback f = super.onDropMove(targetNode, elements, feedback, p);
-
- // ScrollViews only allow a single child
- if (targetNode.getChildren().length > 0) {
- f.invalidTarget = true;
- }
- return f;
- }
-
- @Override
- protected void drawFeedback(
- IGraphics gc,
- INode targetNode,
- IDragElement[] elements,
- DropFeedback feedback) {
- if (targetNode.getChildren().length > 0) {
- Rect b = targetNode.getBounds();
- if (b.isValid()) {
- gc.useStyle(DrawingStyle.DROP_RECIPIENT);
- gc.drawRect(b);
- }
- } else {
- super.drawFeedback(gc, targetNode, elements, feedback);
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SeekBarRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SeekBarRule.java
deleted file mode 100644
index b88f8ab25..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SeekBarRule.java
+++ /dev/null
@@ -1,42 +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_LAYOUT_WIDTH;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for android.widget.SeekBar
- */
-public class SeekBarRule extends BaseViewRule {
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- // A SeekBar isn't useful with wrap_content because it packs itself down to
- // almost no usable width -- so just make it grow in all layouts
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, getFillParentValueName());
- }
-
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SlidingDrawerRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SlidingDrawerRule.java
deleted file mode 100644
index e4267bb10..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SlidingDrawerRule.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2011 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.ATTR_CONTENT;
-import static com.android.SdkConstants.ATTR_HANDLE;
-import static com.android.SdkConstants.ATTR_ID;
-import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-import static com.android.SdkConstants.ATTR_TEXT;
-
-
-import com.android.SdkConstants;
-import static com.android.SdkConstants.ANDROID_URI;
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for android.widget.SlidingDrawerRule which initializes new sliding
- * drawers with their mandatory children and default sizing attributes
- */
-public class SlidingDrawerRule extends BaseLayoutRule {
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- if (insertType.isCreate()) {
- String matchParent = getFillParentValueName();
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, matchParent);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, matchParent);
-
- // Create mandatory children and reference them from the handle and content
- // attributes of the sliding drawer
- String handleId = "@+id/handle"; //$NON-NLS-1$
- String contentId = "@+id/content"; //$NON-NLS-1$
- node.setAttribute(ANDROID_URI, ATTR_HANDLE, handleId);
- node.setAttribute(ANDROID_URI, ATTR_CONTENT, contentId);
-
- // Handle
- INode handle = node.appendChild(SdkConstants.FQCN_BUTTON);
- handle.setAttribute(ANDROID_URI, ATTR_TEXT, "Handle");
- handle.setAttribute(ANDROID_URI, ATTR_ID, handleId);
-
- // Content
- INode content = node.appendChild(SdkConstants.FQCN_LINEAR_LAYOUT);
- content.setAttribute(ANDROID_URI, ATTR_ID, contentId);
- content.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, matchParent);
- content.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, matchParent);
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabHostRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabHostRule.java
deleted file mode 100644
index cb2153b50..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabHostRule.java
+++ /dev/null
@@ -1,82 +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_ID;
-import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-import static com.android.SdkConstants.ATTR_ORIENTATION;
-import static com.android.SdkConstants.FQCN_FRAME_LAYOUT;
-import static com.android.SdkConstants.FQCN_LINEAR_LAYOUT;
-import static com.android.SdkConstants.FQCN_TAB_WIDGET;
-import static com.android.SdkConstants.VALUE_VERTICAL;
-import static com.android.SdkConstants.VALUE_WRAP_CONTENT;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for android.widget.TabHost.
- */
-public class TabHostRule extends IgnoredLayoutRule {
- // The TabHost layout states in its documentation that you typically
- // manipulate its children via the TabHost rather than directly manipulating
- // the child elements yourself, e.g. via addTab() etc.
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- if (insertType.isCreate()) {
- String fillParent = getFillParentValueName();
-
- // Configure default Table setup as described in the Table tutorial
- node.setAttribute(ANDROID_URI, ATTR_ID, "@android:id/tabhost"); //$NON-NLS-1$
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, fillParent);
-
- INode linear = node.appendChild(FQCN_LINEAR_LAYOUT);
- linear.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
- linear.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, fillParent);
- linear.setAttribute(ANDROID_URI, ATTR_ORIENTATION,
- VALUE_VERTICAL);
-
- INode tab = linear.appendChild(FQCN_TAB_WIDGET);
- tab.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
- tab.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_WRAP_CONTENT);
- tab.setAttribute(ANDROID_URI, ATTR_ID, "@android:id/tabs"); //$NON-NLS-1$
-
- INode frame = linear.appendChild(FQCN_FRAME_LAYOUT);
- frame.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
- frame.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, fillParent);
- frame.setAttribute(ANDROID_URI, ATTR_ID, "@android:id/tabcontent"); //$NON-NLS-1$
-
- for (int i = 0; i < 3; i++) {
- INode child = frame.appendChild(FQCN_LINEAR_LAYOUT);
- child.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, fillParent);
- child.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, fillParent);
- child.setAttribute(ANDROID_URI, ATTR_ID,
- String.format("@+id/tab%d", i + 1)); //$NON-NLS-1$
- }
- }
- }
-
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabWidgetRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabWidgetRule.java
deleted file mode 100755
index 7ebaea54d..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabWidgetRule.java
+++ /dev/null
@@ -1,27 +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 com.android.ide.common.api.IViewRule;
-
-/**
- * An {@link IViewRule} for android.widget.TabWidget.
- */
-public class TabWidgetRule extends IgnoredLayoutRule {
- // TabWidgets aren't configurable as plain LinearLayout since they
- // are supposed to be manipulated by their parent TabHost.
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java
deleted file mode 100644
index b6aeeb486..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java
+++ /dev/null
@@ -1,218 +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.FQCN_TABLE_ROW;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IClientRulesEngine;
-import com.android.ide.common.api.IMenuCallback;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.INodeHandler;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.RuleAction;
-import com.android.ide.common.api.SegmentType;
-
-import java.net.URL;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * An {@link IViewRule} for android.widget.TableLayout.
- */
-public class TableLayoutRule extends LinearLayoutRule {
- // A table is a linear layout, but with a few differences:
- // the default is vertical, not horizontal
- // The fill of all children should be wrap_content
-
- private static final String ACTION_ADD_ROW = "_addrow"; //$NON-NLS-1$
- private static final String ACTION_REMOVE_ROW = "_removerow"; //$NON-NLS-1$
- private static final URL ICON_ADD_ROW =
- TableLayoutRule.class.getResource("addrow.png"); //$NON-NLS-1$
- private static final URL ICON_REMOVE_ROW =
- TableLayoutRule.class.getResource("removerow.png"); //$NON-NLS-1$
-
- @Override
- protected boolean isVertical(INode node) {
- // Tables are always vertical
- return true;
- }
-
- @Override
- protected boolean supportsOrientation() {
- return false;
- }
-
- @Override
- public void onChildInserted(@NonNull INode child, @NonNull INode parent,
- @NonNull InsertType insertType) {
- // Overridden to inhibit the setting of layout_width/layout_height since
- // it should always be match_parent
- }
-
- /**
- * Add an explicit "Add Row" action to the context menu
- */
- @Override
- public void addContextMenuActions(@NonNull List<RuleAction> actions,
- final @NonNull INode selectedNode) {
- super.addContextMenuActions(actions, selectedNode);
-
- IMenuCallback addTab = new IMenuCallback() {
- @Override
- public void action(
- @NonNull RuleAction action,
- @NonNull List<? extends INode> selectedNodes,
- final @Nullable String valueId,
- @Nullable Boolean newValue) {
- final INode node = selectedNode;
- INode newRow = node.appendChild(FQCN_TABLE_ROW);
- mRulesEngine.select(Collections.singletonList(newRow));
- }
- };
- actions.add(RuleAction.createAction("_addrow", "Add Row", addTab, null, 5, false)); //$NON-NLS-1$
- }
-
- @Override
- public void addLayoutActions(
- @NonNull List<RuleAction> actions,
- final @NonNull INode parentNode,
- final @NonNull List<? extends INode> children) {
- super.addLayoutActions(actions, parentNode, children);
- addTableLayoutActions(mRulesEngine, actions, parentNode, children);
- }
-
- /**
- * Adds layout actions to add and remove toolbar items
- */
- static void addTableLayoutActions(final IClientRulesEngine rulesEngine,
- List<RuleAction> actions, final INode parentNode,
- final List<? extends INode> children) {
- IMenuCallback actionCallback = new IMenuCallback() {
- @Override
- public void action(
- final @NonNull RuleAction action,
- @NonNull List<? extends INode> selectedNodes,
- final @Nullable String valueId,
- final @Nullable Boolean newValue) {
- parentNode.editXml("Add/Remove Table Row", new INodeHandler() {
- @Override
- public void handle(@NonNull INode n) {
- if (action.getId().equals(ACTION_ADD_ROW)) {
- // Determine the index of the selection, if any; if there is
- // a selection, insert the row before the current row, otherwise
- // append it to the table.
- int index = -1;
- INode[] rows = parentNode.getChildren();
- if (children != null) {
- findTableIndex:
- for (INode child : children) {
- // Find direct child of table layout
- while (child != null && child.getParent() != parentNode) {
- child = child.getParent();
- }
- if (child != null) {
- // Compute index of direct child of table layout
- for (int i = 0; i < rows.length; i++) {
- if (rows[i] == child) {
- index = i;
- break findTableIndex;
- }
- }
- }
- }
- }
- INode newRow;
- if (index == -1) {
- newRow = parentNode.appendChild(FQCN_TABLE_ROW);
- } else {
- newRow = parentNode.insertChildAt(FQCN_TABLE_ROW, index);
- }
- rulesEngine.select(Collections.singletonList(newRow));
- } else if (action.getId().equals(ACTION_REMOVE_ROW)) {
- // Find the direct children of the TableLayout to delete;
- // this is necessary since TableRow might also use
- // this implementation, so the parentNode is the true
- // TableLayout but the children might be grand children.
- Set<INode> targets = new HashSet<INode>();
- for (INode child : children) {
- while (child != null && child.getParent() != parentNode) {
- child = child.getParent();
- }
- if (child != null) {
- targets.add(child);
- }
- }
- for (INode target : targets) {
- parentNode.removeChild(target);
- }
- }
- }
- });
- }
- };
-
- // Add Row
- actions.add(RuleAction.createSeparator(150));
- actions.add(RuleAction.createAction(ACTION_ADD_ROW, "Add Table Row", actionCallback,
- ICON_ADD_ROW, 160, false));
-
- // Remove Row (if something is selected)
- if (children != null && children.size() > 0) {
- actions.add(RuleAction.createAction(ACTION_REMOVE_ROW, "Remove Table Row",
- actionCallback, ICON_REMOVE_ROW, 170, false));
- }
- }
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- if (insertType.isCreate()) {
- // Start the table with 4 rows
- for (int i = 0; i < 4; i++) {
- node.appendChild(FQCN_TABLE_ROW);
- }
- }
- }
-
- @Override
- public DropFeedback onResizeBegin(@NonNull INode child, @NonNull INode parent,
- @Nullable SegmentType horizontalEdge, @Nullable SegmentType verticalEdge,
- @Nullable Object childView, @Nullable Object parentView) {
- // Children of a table layout cannot set their widths (it is controlled by column
- // settings on the table). They can set their heights (though for TableRow, the
- // height is always wrap_content).
- if (horizontalEdge == null) { // Widths are edited by vertical edges.
- // The user is not editing a vertical height so don't allow resizing at all
- return null;
- }
- if (child.getFqcn().equals(FQCN_TABLE_ROW)) {
- // TableRows are always WRAP_CONTENT
- return null;
- }
-
- // Allow resizing heights only
- return super.onResizeBegin(child, parent, horizontalEdge, null /*verticalEdge*/,
- childView, parentView);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableRowRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableRowRule.java
deleted file mode 100644
index 6e3f202ee..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableRowRule.java
+++ /dev/null
@@ -1,80 +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.FQCN_TABLE_LAYOUT;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.RuleAction;
-import com.android.ide.common.api.SegmentType;
-
-import java.util.List;
-
-/**
- * An {@link IViewRule} for android.widget.TableRow.
- */
-public class TableRowRule extends LinearLayoutRule {
- @Override
- protected boolean isVertical(INode node) {
- return false;
- }
-
- @Override
- protected boolean supportsOrientation() {
- return false;
- }
-
- @Override
- public void onChildInserted(@NonNull INode child, @NonNull INode parent,
- @NonNull InsertType insertType) {
- // Overridden to inhibit the setting of layout_width/layout_height since
- // the table row will enforce match_parent and wrap_content for width and height
- // respectively.
- }
-
- @Override
- public void addLayoutActions(
- @NonNull List<RuleAction> actions,
- final @NonNull INode parentNode,
- final @NonNull List<? extends INode> children) {
- super.addLayoutActions(actions, parentNode, children);
-
- // Also apply table-specific actions on the table row such that you can
- // select something in a table row and still get offered actions on the surrounding
- // table.
- if (children != null) {
- INode grandParent = parentNode.getParent();
- if (grandParent != null && grandParent.getFqcn().equals(FQCN_TABLE_LAYOUT)) {
- TableLayoutRule.addTableLayoutActions(mRulesEngine, actions, grandParent,
- children);
- }
- }
- }
-
- @Override
- public DropFeedback onResizeBegin(@NonNull INode child, @NonNull INode parent,
- @Nullable SegmentType horizontalEdge, @Nullable SegmentType verticalEdge,
- @Nullable Object childView, @Nullable Object parentView) {
- // No resizing in TableRows; the width is *always* match_parent and the height is
- // *always* wrap_content.
- return null;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TimePickerRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TimePickerRule.java
deleted file mode 100755
index 1eb603d43..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TimePickerRule.java
+++ /dev/null
@@ -1,27 +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 com.android.ide.common.api.IViewRule;
-
-/**
- * An {@link IViewRule} for android.widget.TimePicker.
- */
-public class TimePickerRule extends IgnoredLayoutRule {
- // A TimePicker inherits from FrameLayout but is not a general purpose
- // FrameLayout
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ViewRule.java
deleted file mode 100755
index a7b23ab75..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ViewRule.java
+++ /dev/null
@@ -1,31 +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 com.android.ide.common.api.IViewRule;
-
-/**
- * An {@link IViewRule} for android.view.View and all its derived classes. This
- * is the "root" rule, that is used whenever there is not more specific rule to
- * apply.
- * <p/>
- * There is no customization here, everything that is common to all views is
- * simply implemented in BaseViewRule.
- */
-public class ViewRule extends BaseViewRule {
-
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ViewTagRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ViewTagRule.java
deleted file mode 100644
index a89a3d851..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ViewTagRule.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2012 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.ATTR_CLASS;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
-
-/**
- * An {@link IViewRule} for the special XML {@code <view>} tag.
- */
-public class ViewTagRule extends BaseViewRule {
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- // When dropping a view tag, ask the user which custom view class to use
- if (insertType == InsertType.CREATE) { // NOT InsertType.CREATE_PREVIEW
- String fqcn = mRulesEngine.displayCustomViewClassInput();
- if (fqcn != null) {
- if (!ViewElementDescriptor.viewNeedsPackage(fqcn)) {
- fqcn = fqcn.substring(fqcn.lastIndexOf('.') + 1);
- }
- node.editXml("Set Custom View Class",
- new PropertySettingNodeHandler(null, ATTR_CLASS,
- fqcn.length() > 0 ? fqcn : null));
- } else {
- // Remove the view; the insertion was canceled
- parent.removeChild(node);
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/WebViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/WebViewRule.java
deleted file mode 100644
index 42b06e65b..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/WebViewRule.java
+++ /dev/null
@@ -1,46 +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_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-
-/**
- * An {@link IViewRule} for android.widget.ZoomControls.
- */
-public class WebViewRule extends IgnoredLayoutRule {
- // A WebView is not a general purpose AbsoluteLayout you should drop stuff
- // into; it's an AbsoluteLayout for implementation purposes.
-
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- if (insertType.isCreate()) {
- String matchParent = getFillParentValueName();
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, matchParent);
- node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, matchParent);
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ZoomButtonRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ZoomButtonRule.java
deleted file mode 100644
index 66cbd45f0..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ZoomButtonRule.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2011 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_SRC;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.InsertType;
-
-public class ZoomButtonRule extends BaseViewRule {
- @Override
- public void onCreate(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- super.onCreate(node, parent, insertType);
-
- if (insertType.isCreate()) {
- node.setAttribute(ANDROID_URI, ATTR_SRC, "@android:drawable/btn_plus"); //$NON-NLS-1$
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ZoomControlsRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ZoomControlsRule.java
deleted file mode 100755
index 226bf4eb5..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ZoomControlsRule.java
+++ /dev/null
@@ -1,27 +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 com.android.ide.common.api.IViewRule;
-
-/**
- * An {@link IViewRule} for android.widget.ZoomControls.
- */
-public class ZoomControlsRule extends IgnoredLayoutRule {
- // A ZoomControl is only a LinearLayout in terms of borrowing
- // implementation; it does not behave like one in terms of configurability.
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/addcol.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/addcol.png
deleted file mode 100644
index 21391ef53..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/addcol.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/addrow.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/addrow.png
deleted file mode 100644
index 0faa3e607..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/addrow.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/allweight.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/allweight.png
deleted file mode 100644
index 506c66320..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/allweight.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/baseline.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/baseline.png
deleted file mode 100644
index acb187ca0..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/baseline.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/centerHorizontally.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/centerHorizontally.png
deleted file mode 100644
index 5053cdadd..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/centerHorizontally.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/centerVertically.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/centerVertically.png
deleted file mode 100644
index ebba8e812..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/centerVertically.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/clearweights.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/clearweights.png
deleted file mode 100644
index ad27c174d..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/clearweights.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/constraints.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/constraints.png
deleted file mode 100644
index 7247d5a09..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/constraints.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/distribute.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/distribute.png
deleted file mode 100644
index eac2340f9..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/distribute.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/fillheight.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/fillheight.png
deleted file mode 100644
index 38e137deb..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/fillheight.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/fillwidth.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/fillwidth.png
deleted file mode 100644
index f272aab68..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/fillwidth.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/gravity.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/gravity.png
deleted file mode 100644
index 4f20928ad..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/gravity.png
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index 8bdb56bfe..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridDropHandler.java
+++ /dev/null
@@ -1,840 +0,0 @@
-/*
- * Copyright (C) 2011 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.grid;
-
-import static com.android.SdkConstants.ATTR_COLUMN_COUNT;
-import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN;
-import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN_SPAN;
-import static com.android.SdkConstants.ATTR_LAYOUT_GRAVITY;
-import static com.android.SdkConstants.ATTR_LAYOUT_ROW;
-import static com.android.SdkConstants.ATTR_LAYOUT_ROW_SPAN;
-import static com.android.ide.common.layout.GravityHelper.getGravity;
-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.grid.GridModel.UNDEFINED;
-import static java.lang.Math.abs;
-
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewMetadata;
-import com.android.ide.common.api.Margins;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.SegmentType;
-import com.android.ide.common.layout.BaseLayoutRule;
-import com.android.ide.common.layout.GravityHelper;
-import com.android.ide.common.layout.GridLayoutRule;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * The {@link GridDropHandler} handles drag and drop operations into and within a
- * GridLayout, computing guidelines, handling drops to edit the grid model, and so on.
- */
-public class GridDropHandler {
- private final GridModel mGrid;
- private final GridLayoutRule mRule;
- private GridMatch mColumnMatch;
- private GridMatch mRowMatch;
-
- /**
- * Creates a new {@link GridDropHandler} for
- * @param gridLayoutRule the corresponding {@link GridLayoutRule}
- * @param layout the GridLayout node
- * @param view the view instance of the grid layout receiving the drop
- */
- public GridDropHandler(GridLayoutRule gridLayoutRule, INode layout, Object view) {
- mRule = gridLayoutRule;
- mGrid = GridModel.get(mRule.getRulesEngine(), layout, view);
- }
-
- /**
- * Computes the best horizontal and vertical matches for a drag to the given position.
- *
- * @param feedback a {@link DropFeedback} object containing drag state like the drag
- * bounds and the drag baseline
- * @param p the mouse position
- */
- public void computeMatches(DropFeedback feedback, Point p) {
- mRowMatch = mColumnMatch = null;
- feedback.tooltip = null;
-
- Rect bounds = mGrid.layout.getBounds();
- int x1 = p.x;
- int y1 = p.y;
-
- Rect dragBounds = feedback.dragBounds;
- int w = dragBounds != null ? dragBounds.w : 0;
- int h = dragBounds != null ? dragBounds.h : 0;
- if (!GridLayoutRule.sGridMode) {
- if (dragBounds != null) {
- // Sometimes the items are centered under the mouse so
- // offset by the top left corner distance
- x1 += dragBounds.x;
- y1 += dragBounds.y;
- }
-
- int x2 = x1 + w;
- int y2 = y1 + h;
-
- if (x2 < bounds.x || y2 < bounds.y || x1 > bounds.x2() || y1 > bounds.y2()) {
- return;
- }
-
- List<GridMatch> columnMatches = new ArrayList<GridMatch>();
- List<GridMatch> rowMatches = new ArrayList<GridMatch>();
- int max = BaseLayoutRule.getMaxMatchDistance();
-
- // Column matches:
- addLeftSideMatch(x1, columnMatches, max);
- addRightSideMatch(x2, columnMatches, max);
- addCenterColumnMatch(bounds, x1, y1, x2, y2, columnMatches, max);
-
- // Row matches:
- int row = (mGrid.getViewCount() == 0) ? 0 : mGrid.getClosestRow(y1);
- int rowY = mGrid.getRowY(row);
- addTopMatch(y1, rowMatches, max, row, rowY);
- addBaselineMatch(feedback.dragBaseline, y1, rowMatches, max, row, rowY);
- addBottomMatch(y2, rowMatches, max);
-
- // Look for gap-matches: Predefined spacing between widgets.
- // TODO: Make this use metadata for predefined spacing between
- // pairs of types of components. For example, buttons have certain
- // inserts in their 9-patch files (depending on the theme) that should
- // be considered and subtracted from the overall proposed distance!
- addColumnGapMatch(bounds, x1, x2, columnMatches, max);
- addRowGapMatch(bounds, y1, y2, rowMatches, max);
-
- // Fallback: Split existing cell. Also do snap-to-grid.
- if (GridLayoutRule.sSnapToGrid) {
- x1 = ((x1 - MARGIN_SIZE - bounds.x) / GRID_SIZE) * GRID_SIZE
- + MARGIN_SIZE + bounds.x;
- y1 = ((y1 - MARGIN_SIZE - bounds.y) / GRID_SIZE) * GRID_SIZE
- + MARGIN_SIZE + bounds.y;
- x2 = x1 + w;
- y2 = y1 + h;
- }
-
-
- if (columnMatches.size() == 0 && x1 >= bounds.x) {
- // Split the current cell since we have no matches
- // TODO: Decide whether it should be gravity left or right...
- columnMatches.add(new GridMatch(SegmentType.LEFT, 0, x1, mGrid.getColumn(x1),
- true /* createCell */, UNDEFINED));
- }
- if (rowMatches.size() == 0 && y1 >= bounds.y) {
- rowMatches.add(new GridMatch(SegmentType.TOP, 0, y1, mGrid.getRow(y1),
- true /* createCell */, UNDEFINED));
- }
-
- // Pick best matches
- Collections.sort(rowMatches);
- Collections.sort(columnMatches);
-
- mColumnMatch = null;
- mRowMatch = null;
- String columnDescription = null;
- String rowDescription = null;
- if (columnMatches.size() > 0) {
- mColumnMatch = columnMatches.get(0);
- columnDescription = mColumnMatch.getDisplayName(mGrid.layout);
- }
- if (rowMatches.size() > 0) {
- mRowMatch = rowMatches.get(0);
- rowDescription = mRowMatch.getDisplayName(mGrid.layout);
- }
-
- if (columnDescription != null && rowDescription != null) {
- feedback.tooltip = columnDescription + '\n' + rowDescription;
- }
-
- feedback.invalidTarget = mColumnMatch == null || mRowMatch == null;
- } else {
- // Find which cell we're inside.
-
- // TODO: Find out where within the cell we are, and offer to tweak the gravity
- // based on the position.
- int column = mGrid.getColumn(x1);
- int row = mGrid.getRow(y1);
-
- int leftDistance = mGrid.getColumnDistance(column, x1);
- int rightDistance = mGrid.getColumnDistance(column + 1, x1);
- int topDistance = mGrid.getRowDistance(row, y1);
- int bottomDistance = mGrid.getRowDistance(row + 1, y1);
-
- int SLOP = 2;
- int radius = mRule.getNewCellSize();
- if (rightDistance < radius + SLOP) {
- column = Math.min(column + 1, mGrid.actualColumnCount);
- leftDistance = rightDistance;
- }
- if (bottomDistance < radius + SLOP) {
- row = Math.min(row + 1, mGrid.actualRowCount);
- topDistance = bottomDistance;
- }
-
- boolean createColumn = leftDistance < radius + SLOP;
- boolean createRow = topDistance < radius + SLOP;
- if (x1 >= bounds.x2()) {
- createColumn = true;
- }
- if (y1 >= bounds.y2()) {
- createRow = true;
- }
-
- int cellWidth = leftDistance + rightDistance;
- int cellHeight = topDistance + bottomDistance;
- SegmentType horizontalType = SegmentType.LEFT;
- SegmentType verticalType = SegmentType.TOP;
- int minDistance = 10; // Don't center or right/bottom align in tiny cells
- if (!createColumn && leftDistance > minDistance
- && dragBounds != null && dragBounds.w < cellWidth - 10) {
- if (rightDistance < leftDistance) {
- horizontalType = SegmentType.RIGHT;
- }
-
- int centerDistance = Math.abs(cellWidth / 2 - leftDistance);
- if (centerDistance < leftDistance / 2 && centerDistance < rightDistance / 2) {
- horizontalType = SegmentType.CENTER_HORIZONTAL;
- }
- }
- if (!createRow && topDistance > minDistance
- && dragBounds != null && dragBounds.h < cellHeight - 10) {
- if (bottomDistance < topDistance) {
- verticalType = SegmentType.BOTTOM;
- }
- int centerDistance = Math.abs(cellHeight / 2 - topDistance);
- if (centerDistance < topDistance / 2 && centerDistance < bottomDistance / 2) {
- verticalType = SegmentType.CENTER_VERTICAL;
- }
- }
-
- mColumnMatch = new GridMatch(horizontalType, 0, x1, column, createColumn, 0);
- mRowMatch = new GridMatch(verticalType, 0, y1, row, createRow, 0);
-
- StringBuilder description = new StringBuilder(50);
- String rowString = Integer.toString(mColumnMatch.cellIndex + 1);
- String columnString = Integer.toString(mRowMatch.cellIndex + 1);
- if (mRowMatch.createCell && mRowMatch.cellIndex < mGrid.actualRowCount) {
- description.append(String.format("Shift row %1$d down", mRowMatch.cellIndex + 1));
- description.append('\n');
- }
- if (mColumnMatch.createCell && mColumnMatch.cellIndex < mGrid.actualColumnCount) {
- description.append(String.format("Shift column %1$d right",
- mColumnMatch.cellIndex + 1));
- description.append('\n');
- }
- description.append(String.format("Insert into cell (%1$s,%2$s)",
- rowString, columnString));
- description.append('\n');
- description.append(String.format("Align %1$s, %2$s",
- horizontalType.name().toLowerCase(Locale.US),
- verticalType.name().toLowerCase(Locale.US)));
- feedback.tooltip = description.toString();
- }
- }
-
- /**
- * Adds a match to align the left edge with some other edge.
- */
- private void addLeftSideMatch(int x1, List<GridMatch> columnMatches, int max) {
- int column = (mGrid.getViewCount() == 0) ? 0 : mGrid.getClosestColumn(x1);
- int columnX = mGrid.getColumnX(column);
- int distance = abs(columnX - x1);
- if (distance <= max) {
- columnMatches.add(new GridMatch(SegmentType.LEFT, distance, columnX, column,
- false, UNDEFINED));
- }
- }
-
- /**
- * Adds a match to align the right edge with some other edge.
- */
- private void addRightSideMatch(int x2, List<GridMatch> columnMatches, int max) {
- // TODO: Only match the right hand side if the drag bounds fit fully within the
- // cell! Ditto for match below.
- int columnRight = (mGrid.getViewCount() == 0) ? 0 : mGrid.getClosestColumn(x2);
- int rightDistance = mGrid.getColumnDistance(columnRight, x2);
- if (rightDistance < max) {
- int columnX = mGrid.getColumnX(columnRight);
- if (columnX > mGrid.layout.getBounds().x) {
- columnMatches.add(new GridMatch(SegmentType.RIGHT, rightDistance, columnX,
- columnRight, false, UNDEFINED));
- }
- }
- }
-
- /**
- * Adds a horizontal match with the center axis of the GridLayout
- */
- private void addCenterColumnMatch(Rect bounds, int x1, int y1, int x2, int y2,
- List<GridMatch> columnMatches, int max) {
- Collection<INode> intersectsRow = mGrid.getIntersectsRow(y1, y2);
- if (intersectsRow.size() == 0) {
- // Offer centering on this row since there isn't anything there
- int matchedLine = bounds.centerX();
- int distance = abs((x1 + x2) / 2 - matchedLine);
- if (distance <= 2 * max) {
- boolean createCell = false; // always just put in column 0
- columnMatches.add(new GridMatch(SegmentType.CENTER_HORIZONTAL, distance,
- matchedLine, 0 /* column */, createCell, UNDEFINED));
- }
- }
- }
-
- /**
- * Adds a match to align the top edge with some other edge.
- */
- private void addTopMatch(int y1, List<GridMatch> rowMatches, int max, int row, int rowY) {
- int distance = mGrid.getRowDistance(row, y1);
- if (distance <= max) {
- rowMatches.add(new GridMatch(SegmentType.TOP, distance, rowY, row, false,
- UNDEFINED));
- }
- }
-
- /**
- * Adds a match to align the bottom edge with some other edge.
- */
- private void addBottomMatch(int y2, List<GridMatch> rowMatches, int max) {
- int rowBottom = (mGrid.getViewCount() == 0) ? 0 : mGrid.getClosestRow(y2);
- int distance = mGrid.getRowDistance(rowBottom, y2);
- if (distance < max) {
- int rowY = mGrid.getRowY(rowBottom);
- if (rowY > mGrid.layout.getBounds().y) {
- rowMatches.add(new GridMatch(SegmentType.BOTTOM, distance, rowY,
- rowBottom, false, UNDEFINED));
- }
- }
- }
-
- /**
- * Adds a baseline match, if applicable.
- */
- private void addBaselineMatch(int dragBaseline, int y1, List<GridMatch> rowMatches, int max,
- int row, int rowY) {
- int dragBaselineY = y1 + dragBaseline;
- int rowBaseline = mGrid.getBaseline(row);
- if (rowBaseline != -1) {
- int rowBaselineY = rowY + rowBaseline;
- int distance = abs(dragBaselineY - rowBaselineY);
- if (distance < max) {
- rowMatches.add(new GridMatch(SegmentType.BASELINE, distance, rowBaselineY, row,
- false, UNDEFINED));
- }
- }
- }
-
- /**
- * Computes a horizontal "gap" match - a preferred distance from the nearest edge,
- * including margin edges
- */
- private void addColumnGapMatch(Rect bounds, int x1, int x2, List<GridMatch> columnMatches,
- int max) {
- if (x1 < bounds.x + MARGIN_SIZE + max) {
- int matchedLine = bounds.x + MARGIN_SIZE;
- int distance = abs(matchedLine - x1);
- if (distance <= max) {
- boolean createCell = mGrid.getColumnX(mGrid.getColumn(matchedLine)) != matchedLine;
- columnMatches.add(new GridMatch(SegmentType.LEFT, distance, matchedLine,
- 0, createCell, MARGIN_SIZE));
- }
- } else if (x2 > bounds.x2() - MARGIN_SIZE - max) {
- int matchedLine = bounds.x2() - MARGIN_SIZE;
- int distance = abs(matchedLine - x2);
- if (distance <= max) {
- // This does not yet work properly; we need to use columnWeights to achieve this
- //boolean createCell = mGrid.getColumnX(mGrid.getColumn(matchedLine)) != matchedLine;
- //columnMatches.add(new GridMatch(SegmentType.RIGHT, distance, matchedLine,
- // mGrid.actualColumnCount - 1, createCell, MARGIN_SIZE));
- }
- } else {
- int columnRight = mGrid.getColumn(x1 - SHORT_GAP_DP);
- int columnX = mGrid.getColumnMaxX(columnRight);
- int matchedLine = columnX + SHORT_GAP_DP;
- int distance = abs(matchedLine - x1);
- if (distance <= max) {
- boolean createCell = mGrid.getColumnX(mGrid.getColumn(matchedLine)) != matchedLine;
- columnMatches.add(new GridMatch(SegmentType.LEFT, distance, matchedLine,
- columnRight, createCell, SHORT_GAP_DP));
- }
-
- // Add a column directly adjacent (no gap)
- columnRight = mGrid.getColumn(x1);
- columnX = mGrid.getColumnMaxX(columnRight);
- matchedLine = columnX;
- distance = abs(matchedLine - x1);
-
- // Let's say you have this arrangement:
- // [button1][button2]
- // This is two columns, where the right hand side edge of column 1 is
- // flush with the left side edge of column 2, because in fact the width of
- // button1 is what defines the width of column 1, and that in turn is what
- // defines the left side position of column 2.
- //
- // In this case we don't want to consider inserting a new column at the
- // right hand side of button1 a better match than matching left on column 2.
- // Therefore, to ensure that this doesn't happen, we "penalize" right column
- // matches such that they don't get preferential treatment when the matching
- // line is on the left side of the column.
- distance += 2;
-
- if (distance <= max) {
- boolean createCell = mGrid.getColumnX(mGrid.getColumn(matchedLine)) != matchedLine;
- columnMatches.add(new GridMatch(SegmentType.LEFT, distance, matchedLine,
- columnRight, createCell, 0));
- }
- }
- }
-
- /**
- * Computes a vertical "gap" match - a preferred distance from the nearest edge,
- * including margin edges
- */
- private void addRowGapMatch(Rect bounds, int y1, int y2, List<GridMatch> rowMatches, int max) {
- if (y1 < bounds.y + MARGIN_SIZE + max) {
- int matchedLine = bounds.y + MARGIN_SIZE;
- int distance = abs(matchedLine - y1);
- if (distance <= max) {
- boolean createCell = mGrid.getRowY(mGrid.getRow(matchedLine)) != matchedLine;
- rowMatches.add(new GridMatch(SegmentType.TOP, distance, matchedLine,
- 0, createCell, MARGIN_SIZE));
- }
- } else if (y2 > bounds.y2() - MARGIN_SIZE - max) {
- int matchedLine = bounds.y2() - MARGIN_SIZE;
- int distance = abs(matchedLine - y2);
- if (distance <= max) {
- // This does not yet work properly; we need to use columnWeights to achieve this
- //boolean createCell = mGrid.getRowY(mGrid.getRow(matchedLine)) != matchedLine;
- //rowMatches.add(new GridMatch(SegmentType.BOTTOM, distance, matchedLine,
- // mGrid.actualRowCount - 1, createCell, MARGIN_SIZE));
- }
- } else {
- int rowBottom = mGrid.getRow(y1 - SHORT_GAP_DP);
- int rowY = mGrid.getRowMaxY(rowBottom);
- int matchedLine = rowY + SHORT_GAP_DP;
- int distance = abs(matchedLine - y1);
- if (distance <= max) {
- boolean createCell = mGrid.getRowY(mGrid.getRow(matchedLine)) != matchedLine;
- rowMatches.add(new GridMatch(SegmentType.TOP, distance, matchedLine,
- rowBottom, createCell, SHORT_GAP_DP));
- }
-
- // Add a row directly adjacent (no gap)
- rowBottom = mGrid.getRow(y1);
- rowY = mGrid.getRowMaxY(rowBottom);
- matchedLine = rowY;
- distance = abs(matchedLine - y1);
- distance += 2; // See explanation in addColumnGapMatch
- if (distance <= max) {
- boolean createCell = mGrid.getRowY(mGrid.getRow(matchedLine)) != matchedLine;
- rowMatches.add(new GridMatch(SegmentType.TOP, distance, matchedLine,
- rowBottom, createCell, 0));
- }
-
- }
- }
-
- /**
- * Called when a node is dropped in free-form mode. This will insert the dragged
- * element into the grid and returns the newly created node.
- *
- * @param targetNode the GridLayout node
- * @param element the dragged element
- * @return the newly created {@link INode}
- */
- public INode handleFreeFormDrop(INode targetNode, IDragElement element) {
- assert mRowMatch != null;
- assert mColumnMatch != null;
-
- String fqcn = element.getFqcn();
-
- INode newChild = null;
-
- Rect bounds = element.getBounds();
- int row = mRowMatch.cellIndex;
- int column = mColumnMatch.cellIndex;
-
- if (targetNode.getChildren().length == 0) {
- //
- // Set up the initial structure:
- //
- //
- // Fixed Fixed
- // Size Size
- // Column Expanding Column Column
- // +-----+-------------------------------+-----+
- // | | | |
- // | 0,0 | 0,1 | 0,2 | Fixed Size Row
- // | | | |
- // +-----+-------------------------------+-----+
- // | | | |
- // | | | |
- // | | | |
- // | 1,0 | 1,1 | 1,2 | Expanding Row
- // | | | |
- // | | | |
- // | | | |
- // +-----+-------------------------------+-----+
- // | | | |
- // | 2,0 | 2,1 | 2,2 | Fixed Size Row
- // | | | |
- // +-----+-------------------------------+-----+
- //
- // This is implemented in GridLayout by the following grid, where
- // SC1 has columnWeight=1 and SR1 has rowWeight=1.
- // (SC=Space for Column, SR=Space for Row)
- //
- // +------+-------------------------------+------+
- // | | | |
- // | SCR0 | SC1 | SC2 |
- // | | | |
- // +------+-------------------------------+------+
- // | | | |
- // | | | |
- // | | | |
- // | SR1 | | |
- // | | | |
- // | | | |
- // | | | |
- // +------+-------------------------------+------+
- // | | | |
- // | SR2 | | |
- // | | | |
- // +------+-------------------------------+------+
- //
- // Note that when we split columns and rows here, if splitting the expanding
- // row or column then the row or column weight should be moved to the right or
- // bottom half!
-
-
- //int columnX = mGrid.getColumnX(column);
- //int rowY = mGrid.getRowY(row);
-
- 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);
- //mGrid.setGridAttribute(sc1, ATTR_LAYOUT_GRAVITY, VALUE_FILL_HORIZONTAL);
- //mGrid.setGridAttribute(sr1, ATTR_LAYOUT_GRAVITY, VALUE_FILL_VERTICAL);
- //
- //mGrid.loadFromXml();
- //column = mGrid.getColumn(columnX);
- //row = mGrid.getRow(rowY);
- }
-
- int startX, endX;
- if (mColumnMatch.type == SegmentType.RIGHT) {
- endX = mColumnMatch.matchedLine - 1;
- startX = endX - bounds.w;
- column = mGrid.getColumn(startX);
- } else {
- startX = mColumnMatch.matchedLine; // TODO: What happens on type=RIGHT?
- endX = startX + bounds.w;
- }
- int startY, endY;
- if (mRowMatch.type == SegmentType.BOTTOM) {
- endY = mRowMatch.matchedLine - 1;
- startY = endY - bounds.h;
- row = mGrid.getRow(startY);
- } else if (mRowMatch.type == SegmentType.BASELINE) {
- // TODO: The rowSpan should always be 1 for baseline alignments, since
- // otherwise the alignment won't work!
- startY = endY = mRowMatch.matchedLine;
- } else {
- startY = mRowMatch.matchedLine;
- endY = startY + bounds.h;
- }
- int endColumn = mGrid.getColumn(endX);
- int endRow = mGrid.getRow(endY);
- int columnSpan = endColumn - column + 1;
- int rowSpan = endRow - row + 1;
-
- // Make sure my math was right:
- assert mRowMatch.type != SegmentType.BASELINE || rowSpan == 1 : rowSpan;
-
- // If the item almost fits into the row (at most N % bigger) then just enlarge
- // the row; don't add a rowspan since that will defeat baseline alignment etc
- if (!mRowMatch.createCell && bounds.h <= MAX_CELL_DIFFERENCE * mGrid.getRowHeight(
- mRowMatch.type == SegmentType.BOTTOM ? endRow : row, 1)) {
- if (mRowMatch.type == SegmentType.BOTTOM) {
- row += rowSpan - 1;
- }
- rowSpan = 1;
- }
- if (!mColumnMatch.createCell && bounds.w <= MAX_CELL_DIFFERENCE * mGrid.getColumnWidth(
- mColumnMatch.type == SegmentType.RIGHT ? endColumn : column, 1)) {
- if (mColumnMatch.type == SegmentType.RIGHT) {
- column += columnSpan - 1;
- }
- columnSpan = 1;
- }
-
- if (mColumnMatch.type == SegmentType.CENTER_HORIZONTAL) {
- column = 0;
- columnSpan = mGrid.actualColumnCount;
- }
-
- // Temporary: Ensure we don't get in trouble with implicit positions
- mGrid.applyPositionAttributes();
-
- // Split cells to make a new column
- if (mColumnMatch.createCell) {
- int columnWidthPx = mGrid.getColumnDistance(column, mColumnMatch.matchedLine);
- //assert columnWidthPx == columnMatch.distance; // TBD? IF so simplify
- int columnWidthDp = mRule.getRulesEngine().pxToDp(columnWidthPx);
-
- int maxX = mGrid.getColumnMaxX(column);
- boolean insertMarginColumn = false;
- if (mColumnMatch.margin == 0) {
- columnWidthDp = 0;
- } else if (mColumnMatch.margin != UNDEFINED) {
- int distance = abs(mColumnMatch.matchedLine - (maxX + mColumnMatch.margin));
- insertMarginColumn = column > 0 && distance < 2;
- if (insertMarginColumn) {
- int margin = mColumnMatch.margin;
- if (ViewMetadataRepository.INSETS_SUPPORTED) {
- IViewMetadata metadata = mRule.getRulesEngine().getMetadata(fqcn);
- if (metadata != null) {
- Margins insets = metadata.getInsets();
- if (insets != null) {
- // TODO:
- // Consider left or right side attachment
- // TODO: Also consider inset of element on cell to the left
- margin -= insets.left;
- }
- }
- }
-
- columnWidthDp = mRule.getRulesEngine().pxToDp(margin);
- }
- }
-
- column++;
- mGrid.splitColumn(column, insertMarginColumn, columnWidthDp, mColumnMatch.matchedLine);
- if (insertMarginColumn) {
- column++;
- }
- }
-
- // Split cells to make a new row
- if (mRowMatch.createCell) {
- int rowHeightPx = mGrid.getRowDistance(row, mRowMatch.matchedLine);
- //assert rowHeightPx == rowMatch.distance; // TBD? If so simplify
- int rowHeightDp = mRule.getRulesEngine().pxToDp(rowHeightPx);
-
- int maxY = mGrid.getRowMaxY(row);
- boolean insertMarginRow = false;
- if (mRowMatch.margin == 0) {
- rowHeightDp = 0;
- } else if (mRowMatch.margin != UNDEFINED) {
- int distance = abs(mRowMatch.matchedLine - (maxY + mRowMatch.margin));
- insertMarginRow = row > 0 && distance < 2;
- if (insertMarginRow) {
- int margin = mRowMatch.margin;
- IViewMetadata metadata = mRule.getRulesEngine().getMetadata(element.getFqcn());
- if (metadata != null) {
- Margins insets = metadata.getInsets();
- if (insets != null) {
- // TODO:
- // Consider left or right side attachment
- // TODO: Also consider inset of element on cell to the left
- margin -= insets.top;
- }
- }
-
- rowHeightDp = mRule.getRulesEngine().pxToDp(margin);
- }
- }
-
- row++;
- mGrid.splitRow(row, insertMarginRow, rowHeightDp, mRowMatch.matchedLine);
- if (insertMarginRow) {
- row++;
- }
- }
-
- // Figure out where to insert the new child
-
- int index = mGrid.getInsertIndex(row, column);
- if (index == -1) {
- // Couldn't find a later place to insert
- newChild = targetNode.appendChild(fqcn);
- } else {
- GridModel.ViewData next = mGrid.getView(index);
-
- newChild = targetNode.insertChildAt(fqcn, index);
-
- // Must also apply positions to the following child to ensure
- // that the new child doesn't affect the implicit numbering!
- // TODO: We can later check whether the implied number is equal to
- // what it already is such that we don't need this
- next.applyPositionAttributes();
- }
-
- // Set the cell position (gravity) of the new widget
- int gravity = 0;
- if (mColumnMatch.type == SegmentType.RIGHT) {
- gravity |= GravityHelper.GRAVITY_RIGHT;
- } else if (mColumnMatch.type == SegmentType.CENTER_HORIZONTAL) {
- gravity |= GravityHelper.GRAVITY_CENTER_HORIZ;
- }
- mGrid.setGridAttribute(newChild, ATTR_LAYOUT_COLUMN, column);
- if (mRowMatch.type == SegmentType.BASELINE) {
- // There *is* no baseline gravity constant, instead, leave the
- // vertical gravity unspecified and GridLayout will treat it as
- // baseline alignment
- //gravity |= GravityHelper.GRAVITY_BASELINE;
- } else if (mRowMatch.type == SegmentType.BOTTOM) {
- gravity |= GravityHelper.GRAVITY_BOTTOM;
- } else if (mRowMatch.type == SegmentType.CENTER_VERTICAL) {
- gravity |= GravityHelper.GRAVITY_CENTER_VERT;
- }
- // Ensure that we have at least one horizontal and vertical constraint, otherwise
- // the new item will be fixed. As an example, if we have a single button in the
- // table which we inserted *without* a gravity, and we then insert a button
- // above it with a vertical gravity, then only the top column would be considered
- // stretchable, and it will fill all available vertical space and the previous
- // button will jump to the bottom.
- if (!GravityHelper.isConstrainedHorizontally(gravity)) {
- gravity |= GravityHelper.GRAVITY_LEFT;
- }
- /* This causes problems: Try placing two buttons vertically from the top of the layout.
- We need to solve the free column/free row problem first.
- if (!GravityHelper.isConstrainedVertically(gravity)
- // There is no baseline constant, so we have to leave it unconstrained instead
- && mRowMatch.type != SegmentType.BASELINE
- // You also can't baseline align one element with another that has vertical
- // alignment top or bottom, so when we first "freely" place views (e.g.
- // at a particular y location), also place it freely (no constraint).
- && !mRowMatch.createCell) {
- gravity |= GravityHelper.GRAVITY_TOP;
- }
- */
- mGrid.setGridAttribute(newChild, ATTR_LAYOUT_GRAVITY, getGravity(gravity));
-
- mGrid.setGridAttribute(newChild, ATTR_LAYOUT_ROW, row);
-
- // Apply spans to ensure that the widget can fit without pushing columns
- if (columnSpan > 1) {
- mGrid.setGridAttribute(newChild, ATTR_LAYOUT_COLUMN_SPAN, columnSpan);
- }
- if (rowSpan > 1) {
- mGrid.setGridAttribute(newChild, ATTR_LAYOUT_ROW_SPAN, rowSpan);
- }
-
- // Ensure that we don't store columnCount=0
- if (mGrid.actualColumnCount == 0) {
- mGrid.setGridAttribute(mGrid.layout, ATTR_COLUMN_COUNT, Math.max(1, column + 1));
- }
-
- return newChild;
- }
-
- /**
- * Called when a drop is completed and we're in grid-editing mode. This will insert
- * the dragged element into the target cell.
- *
- * @param targetNode the GridLayout node
- * @param element the dragged element
- * @return the newly created node
- */
- public INode handleGridModeDrop(INode targetNode, IDragElement element) {
- String fqcn = element.getFqcn();
- INode newChild = targetNode.appendChild(fqcn);
-
- int column = mColumnMatch.cellIndex;
- if (mColumnMatch.createCell) {
- mGrid.addColumn(column,
- newChild, UNDEFINED, false, UNDEFINED, UNDEFINED);
- }
- int row = mRowMatch.cellIndex;
- if (mRowMatch.createCell) {
- mGrid.addRow(row, newChild, UNDEFINED, false, UNDEFINED, UNDEFINED);
- }
-
- mGrid.setGridAttribute(newChild, ATTR_LAYOUT_COLUMN, column);
- mGrid.setGridAttribute(newChild, ATTR_LAYOUT_ROW, row);
-
- int gravity = 0;
- if (mColumnMatch.type == SegmentType.RIGHT) {
- gravity |= GravityHelper.GRAVITY_RIGHT;
- } else if (mColumnMatch.type == SegmentType.CENTER_HORIZONTAL) {
- gravity |= GravityHelper.GRAVITY_CENTER_HORIZ;
- }
- if (mRowMatch.type == SegmentType.BASELINE) {
- // There *is* no baseline gravity constant, instead, leave the
- // vertical gravity unspecified and GridLayout will treat it as
- // baseline alignment
- //gravity |= GravityHelper.GRAVITY_BASELINE;
- } else if (mRowMatch.type == SegmentType.BOTTOM) {
- gravity |= GravityHelper.GRAVITY_BOTTOM;
- } else if (mRowMatch.type == SegmentType.CENTER_VERTICAL) {
- gravity |= GravityHelper.GRAVITY_CENTER_VERT;
- }
- if (!GravityHelper.isConstrainedHorizontally(gravity)) {
- gravity |= GravityHelper.GRAVITY_LEFT;
- }
- if (!GravityHelper.isConstrainedVertically(gravity)) {
- gravity |= GravityHelper.GRAVITY_TOP;
- }
- mGrid.setGridAttribute(newChild, ATTR_LAYOUT_GRAVITY, getGravity(gravity));
-
- if (mGrid.declaredColumnCount == UNDEFINED || mGrid.declaredColumnCount < column + 1) {
- mGrid.setGridAttribute(mGrid.layout, ATTR_COLUMN_COUNT, column + 1);
- }
-
- return newChild;
- }
-
- /**
- * Returns the best horizontal match
- *
- * @return the best horizontal match, or null if there is no match
- */
- public GridMatch getColumnMatch() {
- return mColumnMatch;
- }
-
- /**
- * Returns the best vertical match
- *
- * @return the best vertical match, or null if there is no match
- */
- public GridMatch getRowMatch() {
- return mRowMatch;
- }
-
- /**
- * Returns the grid used by the drop handler
- *
- * @return the grid used by the drop handler, never null
- */
- public GridModel getGrid() {
- return mGrid;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridLayoutPainter.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridLayoutPainter.java
deleted file mode 100644
index 7e2d3a799..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridLayoutPainter.java
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Copyright (C) 2011 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.grid;
-
-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.grid.GridModel.UNDEFINED;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IFeedbackPainter;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.SegmentType;
-import com.android.ide.common.layout.GridLayoutRule;
-import com.android.utils.Pair;
-
-/**
- * Painter which paints feedback during drag, drop and resizing operations, as well as
- * static selection feedback
- */
-public class GridLayoutPainter {
-
- /**
- * Creates a painter for drop feedback
- *
- * @param rule the corresponding {@link GridLayoutRule}
- * @param elements the dragged elements
- * @return a {@link IFeedbackPainter} which can paint the drop feedback
- */
- public static IFeedbackPainter createDropFeedbackPainter(GridLayoutRule rule,
- IDragElement[] elements) {
- return new DropFeedbackPainter(rule, elements);
- }
-
- /**
- * Paints the structure (the grid model) of the given GridLayout.
- *
- * @param style the drawing style to use to paint the structure lines
- * @param layout the grid layout node
- * @param gc the graphics context to paint into
- * @param grid the grid model to be visualized
- */
- public static void paintStructure(DrawingStyle style, INode layout, IGraphics gc,
- GridModel grid) {
- Rect b = layout.getBounds();
-
- gc.useStyle(style);
- for (int row = 0; row < grid.actualRowCount; row++) {
- int y = grid.getRowY(row);
- gc.drawLine(b.x, y, b.x2(), y);
- }
- for (int column = 0; column < grid.actualColumnCount; column++) {
- int x = grid.getColumnX(column);
- gc.drawLine(x, b.y, x, b.y2());
- }
- }
-
- /**
- * Paints a regular grid according to the {@link GridLayoutRule#GRID_SIZE} and
- * {@link GridLayoutRule#MARGIN_SIZE} dimensions. These are the same lines that
- * snap-to-grid will align with.
- *
- * @param layout the GridLayout node
- * @param gc the graphics context to paint the grid into
- */
- public static void paintGrid(INode layout, IGraphics gc) {
- Rect b = layout.getBounds();
-
- int oldAlpha = gc.getAlpha();
- gc.useStyle(DrawingStyle.GUIDELINE);
- gc.setAlpha(128);
-
- int y1 = b.y + MARGIN_SIZE;
- int y2 = b.y2() - MARGIN_SIZE;
- for (int y = y1; y < y2; y += GRID_SIZE) {
- int x1 = b.x + MARGIN_SIZE;
- int x2 = b.x2() - MARGIN_SIZE;
- for (int x = x1; x < x2; x += GRID_SIZE) {
- gc.drawPoint(x, y);
- }
- }
- gc.setAlpha(oldAlpha);
- }
-
- /**
- * Paint resizing feedback (which currently paints the grid model faintly.)
- *
- * @param gc the graphics context
- * @param layout the GridLayout
- * @param grid the grid model
- */
- public static void paintResizeFeedback(IGraphics gc, INode layout, GridModel grid) {
- paintStructure(DrawingStyle.GRID, layout, gc, grid);
- }
-
- /**
- * A painter which can paint the drop feedback for elements being dragged into or
- * within a GridLayout.
- */
- private static class DropFeedbackPainter implements IFeedbackPainter {
- private final GridLayoutRule mRule;
- private final IDragElement[] mElements;
-
- /** Constructs a new {@link GridLayoutPainter} bound to the given {@link GridLayoutRule}
- * @param rule the corresponding rule
- * @param elements the elements to draw */
- public DropFeedbackPainter(GridLayoutRule rule, IDragElement[] elements) {
- mRule = rule;
- mElements = elements;
- }
-
- // Implements IFeedbackPainter
- @Override
- public void paint(@NonNull IGraphics gc, @NonNull INode node,
- @NonNull DropFeedback feedback) {
- Rect b = node.getBounds();
- if (!b.isValid()) {
- return;
- }
-
- // Highlight the receiver
- gc.useStyle(DrawingStyle.DROP_RECIPIENT);
- gc.drawRect(b);
- GridDropHandler data = (GridDropHandler) feedback.userData;
-
- if (!GridLayoutRule.sGridMode) {
- paintFreeFormDropFeedback(gc, node, feedback, b, data);
- } else {
- paintGridModeDropFeedback(gc, b, data);
- }
- }
-
- /**
- * Paints the drag feedback for a free-form mode drag
- */
- private void paintFreeFormDropFeedback(IGraphics gc, INode node, DropFeedback feedback,
- Rect b, GridDropHandler data) {
- GridModel grid = data.getGrid();
- if (GridLayoutRule.sSnapToGrid) {
- GridLayoutPainter.paintGrid(node, gc);
- }
- GridLayoutPainter.paintStructure(DrawingStyle.GRID, node, gc, grid);
-
- GridMatch rowMatch = data.getRowMatch();
- GridMatch columnMatch = data.getColumnMatch();
-
- if (rowMatch == null || columnMatch == null) {
- return;
- }
-
- IDragElement first = mElements[0];
- Rect dragBounds = first.getBounds();
- int offsetX = 0;
- int offsetY = 0;
- if (rowMatch.type == SegmentType.BOTTOM) {
- offsetY -= dragBounds.h;
- } else if (rowMatch.type == SegmentType.BASELINE) {
- offsetY -= feedback.dragBaseline;
- }
- if (columnMatch.type == SegmentType.RIGHT) {
- offsetX -= dragBounds.w;
- } else if (columnMatch.type == SegmentType.CENTER_HORIZONTAL) {
- offsetX -= dragBounds.w / 2;
- }
-
- // Draw guidelines for matches
- int y = rowMatch.matchedLine;
- int x = columnMatch.matchedLine;
- Rect bounds = first.getBounds();
-
- // Draw margin
- if (rowMatch.margin != UNDEFINED && rowMatch.margin > 0) {
- gc.useStyle(DrawingStyle.DISTANCE);
- int centerX = bounds.w / 2 + offsetX + x;
- int y1;
- int y2;
- if (rowMatch.type == SegmentType.TOP) {
- y1 = offsetY + y - 1;
- y2 = rowMatch.matchedLine - rowMatch.margin;
- } else {
- assert rowMatch.type == SegmentType.BOTTOM;
- y1 = bounds.h + offsetY + y - 1;
- y2 = rowMatch.matchedLine + rowMatch.margin;
- }
- gc.drawLine(b.x, y1, b.x2(), y1);
- gc.drawLine(b.x, y2, b.x2(), y2);
- gc.drawString(Integer.toString(rowMatch.margin),
- centerX - 3, y1 + (y2 - y1 - 16) / 2);
- } else {
- gc.useStyle(rowMatch.margin == 0 ? DrawingStyle.DISTANCE
- : rowMatch.createCell ? DrawingStyle.GUIDELINE_DASHED
- : DrawingStyle.GUIDELINE);
- gc.drawLine(b.x, y, b.x2(), y );
- }
-
- if (columnMatch.margin != UNDEFINED && columnMatch.margin > 0) {
- gc.useStyle(DrawingStyle.DISTANCE);
- int centerY = bounds.h / 2 + offsetY + y;
- int x1;
- int x2;
- if (columnMatch.type == SegmentType.LEFT) {
- x1 = offsetX + x - 1;
- x2 = columnMatch.matchedLine - columnMatch.margin;
- } else {
- assert columnMatch.type == SegmentType.RIGHT;
- x1 = bounds.w + offsetX + x - 1;
- x2 = columnMatch.matchedLine + columnMatch.margin;
- }
- gc.drawLine(x1, b.y, x1, b.y2());
- gc.drawLine(x2, b.y, x2, b.y2());
- gc.drawString(Integer.toString(columnMatch.margin),
- x1 + (x2 - x1 - 16) / 2, centerY - 3);
- } else {
- gc.useStyle(columnMatch.margin == 0 ? DrawingStyle.DISTANCE
- : columnMatch.createCell ? DrawingStyle.GUIDELINE_DASHED
- : DrawingStyle.GUIDELINE);
- gc.drawLine(x, b.y, x, b.y2());
- }
-
- // Draw preview rectangles for all the dragged elements
- gc.useStyle(DrawingStyle.DROP_PREVIEW);
- offsetX += x - bounds.x;
- offsetY += y - bounds.y;
-
- for (IDragElement element : mElements) {
- if (element == first) {
- mRule.drawElement(gc, first, offsetX, offsetY);
- // Preview baseline as well
- if (feedback.dragBaseline != -1) {
- int x1 = dragBounds.x + offsetX;
- int y1 = dragBounds.y + offsetY + feedback.dragBaseline;
- gc.drawLine(x1, y1, x1 + dragBounds.w, y1);
- }
- } else {
- b = element.getBounds();
- if (b.isValid()) {
- gc.drawRect(b.x + offsetX, b.y + offsetY,
- b.x + offsetX + b.w, b.y + offsetY + b.h);
- }
- }
- }
- }
-
- /**
- * Paints the drag feedback for a grid-mode drag
- */
- private void paintGridModeDropFeedback(IGraphics gc, Rect b, GridDropHandler data) {
- int radius = mRule.getNewCellSize();
- GridModel grid = data.getGrid();
-
- gc.useStyle(DrawingStyle.GUIDELINE);
- // Paint grid
- for (int row = 1; row < grid.actualRowCount; row++) {
- int y = grid.getRowY(row);
- gc.drawLine(b.x, y - radius, b.x2(), y - radius);
- gc.drawLine(b.x, y + radius, b.x2(), y + radius);
-
- }
- for (int column = 1; column < grid.actualColumnCount; column++) {
- int x = grid.getColumnX(column);
- gc.drawLine(x - radius, b.y, x - radius, b.y2());
- gc.drawLine(x + radius, b.y, x + radius, b.y2());
- }
- gc.drawRect(b.x, b.y, b.x2(), b.y2());
- gc.drawRect(b.x + 2 * radius, b.y + 2 * radius,
- b.x2() - 2 * radius, b.y2() - 2 * radius);
-
- GridMatch columnMatch = data.getColumnMatch();
- GridMatch rowMatch = data.getRowMatch();
- int column = columnMatch.cellIndex;
- int row = rowMatch.cellIndex;
- boolean createColumn = columnMatch.createCell;
- boolean createRow = rowMatch.createCell;
-
- Rect cellBounds = grid.getCellBounds(row, column, 1, 1);
-
- IDragElement first = mElements[0];
- Rect dragBounds = first.getBounds();
- int offsetX = cellBounds.x - dragBounds.x;
- int offsetY = cellBounds.y - dragBounds.y;
-
- gc.useStyle(DrawingStyle.DROP_ZONE_ACTIVE);
- if (createColumn) {
- gc.fillRect(new Rect(cellBounds.x - radius,
- cellBounds.y + (createRow ? -radius : radius),
- 2 * radius + 1, cellBounds.h - (createRow ? 0 : 2 * radius)));
- offsetX -= radius + dragBounds.w / 2;
- }
- if (createRow) {
- gc.fillRect(new Rect(cellBounds.x + radius, cellBounds.y - radius,
- cellBounds.w - 2 * radius, 2 * radius + 1));
- offsetY -= radius + dragBounds.h / 2;
- } else if (!createColumn) {
- // Choose this cell
- gc.fillRect(new Rect(cellBounds.x + radius, cellBounds.y + radius,
- cellBounds.w - 2 * radius, cellBounds.h - 2 * radius));
- }
-
- gc.useStyle(DrawingStyle.DROP_PREVIEW);
-
- Rect bounds = first.getBounds();
- int x = offsetX;
- int y = offsetY;
- if (columnMatch.type == SegmentType.RIGHT) {
- x += cellBounds.w - bounds.w;
- } else if (columnMatch.type == SegmentType.CENTER_HORIZONTAL) {
- x += cellBounds.w / 2 - bounds.w / 2;
- }
- if (rowMatch.type == SegmentType.BOTTOM) {
- y += cellBounds.h - bounds.h;
- } else if (rowMatch.type == SegmentType.CENTER_VERTICAL) {
- y += cellBounds.h / 2 - bounds.h / 2;
- }
-
- mRule.drawElement(gc, first, x, y);
- }
- }
-
- /**
- * Paints the structure (the row and column boundaries) of the given
- * GridLayout
- *
- * @param view the instance of the GridLayout whose structure should be
- * painted
- * @param style the drawing style to use for the cell boundaries
- * @param layout the layout element
- * @param gc the graphics context
- * @return true if the structure was successfully inferred from the view and
- * painted
- */
- public static boolean paintStructure(Object view, DrawingStyle style, INode layout,
- IGraphics gc) {
- Pair<int[],int[]> cellBounds = GridModel.getAxisBounds(view);
- if (cellBounds != null) {
- int[] xs = cellBounds.getFirst();
- int[] ys = cellBounds.getSecond();
- Rect b = layout.getBounds();
- gc.useStyle(style);
- for (int row = 0; row < ys.length; row++) {
- int y = ys[row] + b.y;
- gc.drawLine(b.x, y, b.x2(), y);
- }
- for (int column = 0; column < xs.length; column++) {
- int x = xs[column] + b.x;
- gc.drawLine(x, b.y, x, b.y2());
- }
-
- return true;
- } else {
- return false;
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridMatch.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridMatch.java
deleted file mode 100644
index 9bee34345..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridMatch.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2011 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.grid;
-
-import static com.android.ide.common.layout.grid.GridModel.UNDEFINED;
-
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.SegmentType;
-
-/**
- * A match for a drag within a GridLayout, corresponding to an alignment with another
- * edge, or a margin, or centering, or a gap distance from another edge and so on.
- */
-class GridMatch implements Comparable<GridMatch> {
- /** The distance to the matched edge - used to pick best matches */
- public final int distance;
-
- /** Type of edge that was matched (this refers to the edge on the dragged node,
- * not on the matched node/row/cell etc) */
- public final SegmentType type;
-
- /** Row or column for the match */
- public int cellIndex;
-
- /** If true, create a new row/column */
- public boolean createCell;
-
- /** The actual x or y position of the matched segment */
- public int matchedLine;
-
- /** Amount of margin between the matched edges */
- public int margin;
-
- /**
- * Constructs a match.
- *
- * @param type the edge of the dragged element that was matched
- * @param distance the absolute distance from the ideal match - used to find the best
- * match
- * @param matchedLine the actual X or Y location of the ideal match
- * @param cellIndex the index of the row or column we matched with
- * @param createCell if true, create a new cell by splitting the existing cell at the
- * matchedLine position
- * @param margin a margin distance to add to the actual location from the matched line
- */
- GridMatch(SegmentType type, int distance, int matchedLine, int cellIndex,
- boolean createCell, int margin) {
- super();
- this.type = type;
- this.distance = distance;
- this.matchedLine = matchedLine;
- this.cellIndex = cellIndex;
- this.createCell = createCell;
- this.margin = margin;
- }
-
- // Implements Comparable<GridMatch>
- @Override
- public int compareTo(GridMatch o) {
- // Pick closest matches first
- if (distance != o.distance) {
- return distance - o.distance;
- }
-
- // Prefer some types of matches over other matches
- return getPriority() - o.getPriority();
- }
-
- /**
- * Describes the match for the user
- *
- * @param layout the GridLayout containing the match
- * @return a short description for the user of the match
- */
- public String getDisplayName(INode layout) {
- switch (type) {
- case BASELINE:
- return String.format("Align baseline in row %1$d", cellIndex + 1);
- case CENTER_HORIZONTAL:
- return "Center horizontally";
- case LEFT:
- if (!createCell) {
- return String.format("Insert into column %1$d", cellIndex + 1);
- }
- if (margin != UNDEFINED) {
- if (cellIndex == 0 && margin != 0) {
- return "Add one margin distance from the left";
- }
- return String.format("Add next to column %1$d", cellIndex + 1);
- }
- return String.format("Align left at x=%1$d", matchedLine - layout.getBounds().x);
- case RIGHT:
- if (!createCell) {
- return String.format("Insert right-aligned into column %1$d", cellIndex + 1);
- }
- return String.format("Align right at x=%1$d", matchedLine - layout.getBounds().x);
- case TOP:
- if (!createCell) {
- return String.format("Insert into row %1$d", cellIndex + 1);
- }
- if (margin != UNDEFINED) {
- if (cellIndex == 0 && margin != 0) {
- return "Add one margin distance from the top";
- }
- return String.format("Add below row %1$d", cellIndex + 1);
- }
- return String.format("Align top at y=%1d", matchedLine - layout.getBounds().y);
- case BOTTOM:
- if (!createCell) {
- return String.format("Insert into bottom of row %1$d", cellIndex + 1);
- }
- return String.format("Align bottom at y=%1d", matchedLine - layout.getBounds().y);
- case CENTER_VERTICAL:
- return "Center vertically";
- case UNKNOWN:
- default:
- return null;
- }
- }
-
- /**
- * Computes the sorting priority of this match, giving baseline matches higher
- * precedence than centering which in turn is ordered before external edge matches
- */
- private int getPriority() {
- switch (type) {
- case BASELINE:
- return 0;
- case CENTER_HORIZONTAL:
- case CENTER_VERTICAL:
- return 1;
- case BOTTOM:
- case LEFT:
- case RIGHT:
- case TOP:
- return 2;
- }
-
- return 3;
- }
-} \ No newline at end of file
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
deleted file mode 100644
index 46770e82c..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/grid/GridModel.java
+++ /dev/null
@@ -1,2384 +0,0 @@
-/*
- * Copyright (C) 2011 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.grid;
-
-import static com.android.SdkConstants.ANDROID_URI;
-import static com.android.SdkConstants.ATTR_COLUMN_COUNT;
-import static com.android.SdkConstants.ATTR_ID;
-import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN;
-import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN_SPAN;
-import static com.android.SdkConstants.ATTR_LAYOUT_GRAVITY;
-import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ROW;
-import static com.android.SdkConstants.ATTR_LAYOUT_ROW_SPAN;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-import static com.android.SdkConstants.ATTR_ORIENTATION;
-import static com.android.SdkConstants.ATTR_ROW_COUNT;
-import static com.android.SdkConstants.FQCN_GRID_LAYOUT;
-import static com.android.SdkConstants.FQCN_SPACE;
-import static com.android.SdkConstants.FQCN_SPACE_V7;
-import static com.android.SdkConstants.GRID_LAYOUT;
-import static com.android.SdkConstants.NEW_ID_PREFIX;
-import static com.android.SdkConstants.SPACE;
-import static com.android.SdkConstants.VALUE_BOTTOM;
-import static com.android.SdkConstants.VALUE_CENTER_VERTICAL;
-import static com.android.SdkConstants.VALUE_N_DP;
-import static com.android.SdkConstants.VALUE_TOP;
-import static com.android.SdkConstants.VALUE_VERTICAL;
-import static com.android.ide.common.layout.GravityHelper.GRAVITY_BOTTOM;
-import static com.android.ide.common.layout.GravityHelper.GRAVITY_CENTER_HORIZ;
-import static com.android.ide.common.layout.GravityHelper.GRAVITY_CENTER_VERT;
-import static com.android.ide.common.layout.GravityHelper.GRAVITY_RIGHT;
-import static java.lang.Math.abs;
-import static java.lang.Math.max;
-import static java.lang.Math.min;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.IClientRulesEngine;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewMetadata;
-import com.android.ide.common.api.Margins;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.layout.GravityHelper;
-import com.android.ide.common.layout.GridLayoutRule;
-import com.android.utils.Pair;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Multimap;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/** Models a GridLayout */
-public class GridModel {
- /** Marker value used to indicate values (rows, columns, etc) which have not been set */
- static final int UNDEFINED = Integer.MIN_VALUE;
-
- /** The size of spacers in the dimension that they are not defining */
- static final int SPACER_SIZE_DP = 1;
-
- /** Attribute value used for {@link #SPACER_SIZE_DP} */
- private static final String SPACER_SIZE = String.format(VALUE_N_DP, SPACER_SIZE_DP);
-
- /** Width assigned to a newly added column with the Add Column action */
- private static final int DEFAULT_CELL_WIDTH = 100;
-
- /** Height assigned to a newly added row with the Add Row action */
- private static final int DEFAULT_CELL_HEIGHT = 15;
-
- /** The GridLayout node, never null */
- public final INode layout;
-
- /** True if this is a vertical layout, and false if it is horizontal (the default) */
- public boolean vertical;
-
- /** The declared count of rows (which may be {@link #UNDEFINED} if not specified) */
- public int declaredRowCount;
-
- /** The declared count of columns (which may be {@link #UNDEFINED} if not specified) */
- public int declaredColumnCount;
-
- /** The actual count of rows found in the grid */
- public int actualRowCount;
-
- /** The actual count of columns found in the grid */
- public int actualColumnCount;
-
- /**
- * Array of positions (indexed by column) of the left edge of table cells; this
- * corresponds to the column positions in the grid
- */
- private int[] mLeft;
-
- /**
- * Array of positions (indexed by row) of the top edge of table cells; this
- * corresponds to the row positions in the grid
- */
- private int[] mTop;
-
- /**
- * Array of positions (indexed by column) of the maximum right hand side bounds of a
- * node in the given column; this represents the visual edge of a column even when the
- * actual column is wider
- */
- private int[] mMaxRight;
-
- /**
- * Array of positions (indexed by row) of the maximum bottom bounds of a node in the
- * given row; this represents the visual edge of a row even when the actual row is
- * taller
- */
- private int[] mMaxBottom;
-
- /**
- * Array of baselines computed for the rows. This array is populated lazily and should
- * not be accessed directly; call {@link #getBaseline(int)} instead.
- */
- private int[] mBaselines;
-
- /** List of all the view data for the children in this layout */
- private List<ViewData> mChildViews;
-
- /** The {@link IClientRulesEngine} */
- private final IClientRulesEngine mRulesEngine;
-
- /**
- * An actual instance of a GridLayout object that this grid model corresponds to.
- */
- private Object mViewObject;
-
- /** The namespace to use for attributes */
- private String mNamespace;
-
- /**
- * Constructs a {@link GridModel} for the given layout
- *
- * @param rulesEngine the associated rules engine
- * @param node the GridLayout node
- * @param viewObject an actual GridLayout instance, or null
- */
- private GridModel(IClientRulesEngine rulesEngine, INode node, Object viewObject) {
- mRulesEngine = rulesEngine;
- layout = node;
- mViewObject = viewObject;
- loadFromXml();
- }
-
- // Factory cache for most recent item (used primarily because during paints and drags
- // the grid model is called repeatedly for the same view object.)
- private static WeakReference<Object> sCachedViewObject = new WeakReference<Object>(null);
- private static WeakReference<GridModel> sCachedViewModel;
-
- /**
- * Factory which returns a grid model for the given node.
- *
- * @param rulesEngine the associated rules engine
- * @param node the GridLayout node
- * @param viewObject an actual GridLayout instance, or null
- * @return a new model
- */
- @NonNull
- public static GridModel get(
- @NonNull IClientRulesEngine rulesEngine,
- @NonNull INode node,
- @Nullable Object viewObject) {
- if (viewObject != null && viewObject == sCachedViewObject.get()) {
- GridModel model = sCachedViewModel.get();
- if (model != null) {
- return model;
- }
- }
-
- GridModel model = new GridModel(rulesEngine, node, viewObject);
- sCachedViewModel = new WeakReference<GridModel>(model);
- sCachedViewObject = new WeakReference<Object>(viewObject);
- return model;
- }
-
- /**
- * Returns the {@link ViewData} for the child at the given index
- *
- * @param index the position of the child node whose view we want to look up
- * @return the corresponding {@link ViewData}
- */
- public ViewData getView(int index) {
- return mChildViews.get(index);
- }
-
- /**
- * Returns the {@link ViewData} for the given child node.
- *
- * @param node the node for which we want the view info
- * @return the view info for the node, or null if not found
- */
- public ViewData getView(INode node) {
- for (ViewData view : mChildViews) {
- if (view.node == node) {
- return view;
- }
- }
-
- return null;
- }
-
- /**
- * Computes the index (among the children nodes) to insert a new node into which
- * should be positioned at the given row and column. This will skip over any nodes
- * that have implicit positions earlier than the given node, and will also ensure that
- * all nodes are placed before the spacer nodes.
- *
- * @param row the target row of the new node
- * @param column the target column of the new node
- * @return the insert position to use or -1 if no preference is found
- */
- public int getInsertIndex(int row, int column) {
- if (vertical) {
- for (ViewData view : mChildViews) {
- if (view.column > column || view.column == column && view.row >= row) {
- return view.index;
- }
- }
- } else {
- for (ViewData view : mChildViews) {
- if (view.row > row || view.row == row && view.column >= column) {
- return view.index;
- }
- }
- }
-
- // Place it before the first spacer
- for (ViewData view : mChildViews) {
- if (view.isSpacer()) {
- return view.index;
- }
- }
-
- return -1;
- }
-
- /**
- * Returns the baseline of the given row, or -1 if none is found. This looks for views
- * in the row which have baseline vertical alignment and also define their own
- * baseline, and returns the first such match.
- *
- * @param row the row to look up a baseline for
- * @return the baseline relative to the row position, or -1 if not defined
- */
- public int getBaseline(int row) {
- if (row < 0 || row >= mBaselines.length) {
- return -1;
- }
-
- int baseline = mBaselines[row];
- if (baseline == UNDEFINED) {
- baseline = -1;
-
- // TBD: Consider stringing together row information in the view data
- // so I can quickly identify the views in a given row instead of searching
- // among all?
- for (ViewData view : mChildViews) {
- // We only count baselines for views with rowSpan=1 because
- // baseline alignment doesn't work for cell spanning views
- if (view.row == row && view.rowSpan == 1) {
- baseline = view.node.getBaseline();
- if (baseline != -1) {
- // Even views that do have baselines do not count towards a row
- // baseline if they have a vertical gravity
- String gravity = getGridAttribute(view.node, ATTR_LAYOUT_GRAVITY);
- if (gravity == null
- || !(gravity.contains(VALUE_TOP)
- || gravity.contains(VALUE_BOTTOM)
- || gravity.contains(VALUE_CENTER_VERTICAL))) {
- // Compute baseline relative to the row, not the view itself
- baseline += view.node.getBounds().y - getRowY(row);
- break;
- }
- }
- }
- }
- mBaselines[row] = baseline;
- }
-
- return baseline;
- }
-
- /** Applies the row and column values into the XML */
- void applyPositionAttributes() {
- for (ViewData view : mChildViews) {
- view.applyPositionAttributes();
- }
-
- // Also fix the columnCount
- if (getGridAttribute(layout, ATTR_COLUMN_COUNT) != null &&
- declaredColumnCount > 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;
-
- String fqcn = layout.getFqcn();
- if (!fqcn.equals(GRID_LAYOUT) && !fqcn.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)) {
- return null;
- }
- // Handle spaces between pipes and flag are a prefix, suffix and interior occurrences
- int index = value.indexOf(flag);
- if (index != -1) {
- int pipe = value.lastIndexOf('|', index);
- int endIndex = index + flag.length();
- if (pipe != -1) {
- value = value.substring(0, pipe).trim() + value.substring(endIndex).trim();
- } else {
- pipe = value.indexOf('|', endIndex);
- if (pipe != -1) {
- value = value.substring(0, index).trim() + value.substring(pipe + 1).trim();
- } else {
- value = value.substring(0, index).trim() + value.substring(endIndex).trim();
- }
- }
- }
-
- return value;
- }
-
- /**
- * Loads a {@link GridModel} from the XML model.
- */
- private void loadFromXml() {
- INode[] children = layout.getChildren();
-
- 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(getGridAttribute(layout, ATTR_ORIENTATION));
-
- mChildViews = new ArrayList<ViewData>(children.length);
- int index = 0;
- for (INode child : children) {
- ViewData view = new ViewData(child, index++);
- mChildViews.add(view);
- }
-
- // Assign row/column positions to all cells that do not explicitly define them
- if (!assignRowsAndColumnsFromViews(mChildViews)) {
- assignRowsAndColumnsFromXml(
- declaredRowCount == UNDEFINED ? children.length : declaredRowCount,
- declaredColumnCount == UNDEFINED ? children.length : declaredColumnCount);
- }
-
- assignCellBounds();
-
- for (int i = 0; i <= actualRowCount; i++) {
- mBaselines[i] = UNDEFINED;
- }
- }
-
- private Pair<Map<Integer, Integer>, Map<Integer, Integer>> findCellsOutsideDeclaredBounds() {
- // See if we have any (row,column) pairs that fall outside the declared
- // bounds; for these we identify the number of unique values and assign these
- // consecutive values
- Map<Integer, Integer> extraColumnsMap = null;
- Map<Integer, Integer> extraRowsMap = null;
- if (declaredRowCount != UNDEFINED) {
- Set<Integer> extraRows = null;
- for (ViewData view : mChildViews) {
- if (view.row >= declaredRowCount) {
- if (extraRows == null) {
- extraRows = new HashSet<Integer>();
- }
- extraRows.add(view.row);
- }
- }
- if (extraRows != null && declaredRowCount != UNDEFINED) {
- List<Integer> rows = new ArrayList<Integer>(extraRows);
- Collections.sort(rows);
- int row = declaredRowCount;
- extraRowsMap = new HashMap<Integer, Integer>();
- for (Integer declared : rows) {
- extraRowsMap.put(declared, row++);
- }
- }
- }
- if (declaredColumnCount != UNDEFINED) {
- Set<Integer> extraColumns = null;
- for (ViewData view : mChildViews) {
- if (view.column >= declaredColumnCount) {
- if (extraColumns == null) {
- extraColumns = new HashSet<Integer>();
- }
- extraColumns.add(view.column);
- }
- }
- if (extraColumns != null && declaredColumnCount != UNDEFINED) {
- List<Integer> columns = new ArrayList<Integer>(extraColumns);
- Collections.sort(columns);
- int column = declaredColumnCount;
- extraColumnsMap = new HashMap<Integer, Integer>();
- for (Integer declared : columns) {
- extraColumnsMap.put(declared, column++);
- }
- }
- }
-
- return Pair.of(extraRowsMap, extraColumnsMap);
- }
-
- /**
- * Figure out actual row and column numbers for views that do not specify explicit row
- * and/or column numbers
- * TODO: Consolidate with the algorithm in GridLayout to ensure we get the
- * exact same results!
- */
- private void assignRowsAndColumnsFromXml(int rowCount, int columnCount) {
- Pair<Map<Integer, Integer>, Map<Integer, Integer>> p = findCellsOutsideDeclaredBounds();
- Map<Integer, Integer> extraRowsMap = p.getFirst();
- Map<Integer, Integer> extraColumnsMap = p.getSecond();
-
- if (!vertical) {
- // Horizontal GridLayout: this is the default. Row and column numbers
- // are assigned by assuming that the children are assigned successive
- // column numbers until we get to the column count of the grid, at which
- // point we jump to the next row. If any cell specifies either an explicit
- // row number of column number, we jump to the next available position.
- // Note also that if there are any rowspans on the current row, then the
- // next row we jump to is below the largest such rowspan - in other words,
- // the algorithm does not fill holes in the middle!
-
- // TODO: Ensure that we don't run into trouble if a later element specifies
- // an earlier number... find out what the layout does in that case!
- int row = 0;
- int column = 0;
- int nextRow = 1;
- for (ViewData view : mChildViews) {
- int declaredColumn = view.column;
- if (declaredColumn != UNDEFINED) {
- if (declaredColumn >= columnCount) {
- assert extraColumnsMap != null;
- declaredColumn = extraColumnsMap.get(declaredColumn);
- view.column = declaredColumn;
- }
- if (declaredColumn < column) {
- // Must jump to the next row to accommodate the new row
- assert nextRow > row;
- //row++;
- row = nextRow;
- }
- column = declaredColumn;
- } else {
- view.column = column;
- }
- if (view.row != UNDEFINED) {
- // TODO: Should this adjust the column number too? (If so must
- // also update view.column since we've already processed the local
- // column number)
- row = view.row;
- } else {
- view.row = row;
- }
-
- nextRow = Math.max(nextRow, view.row + view.rowSpan);
-
- // Advance
- column += view.columnSpan;
- if (column >= columnCount) {
- column = 0;
- assert nextRow > row;
- //row++;
- row = nextRow;
- }
- }
- } else {
- // Vertical layout: successive children are assigned to the same column in
- // successive rows.
- int row = 0;
- int column = 0;
- int nextColumn = 1;
- for (ViewData view : mChildViews) {
- int declaredRow = view.row;
- if (declaredRow != UNDEFINED) {
- if (declaredRow >= rowCount) {
- declaredRow = extraRowsMap.get(declaredRow);
- view.row = declaredRow;
- }
- if (declaredRow < row) {
- // Must jump to the next column to accommodate the new column
- assert nextColumn > column;
- column = nextColumn;
- }
- row = declaredRow;
- } else {
- view.row = row;
- }
- if (view.column != UNDEFINED) {
- // TODO: Should this adjust the row number too? (If so must
- // also update view.row since we've already processed the local
- // row number)
- column = view.column;
- } else {
- view.column = column;
- }
-
- nextColumn = Math.max(nextColumn, view.column + view.columnSpan);
-
- // Advance
- row += view.rowSpan;
- if (row >= rowCount) {
- row = 0;
- assert nextColumn > column;
- //row++;
- column = nextColumn;
- }
- }
- }
- }
-
- private static boolean sAttemptSpecReflection = true;
-
- private boolean assignRowsAndColumnsFromViews(List<ViewData> views) {
- if (!sAttemptSpecReflection) {
- return false;
- }
-
- try {
- // Lazily initialized reflection methods
- Field spanField = null;
- Field rowSpecField = null;
- Field colSpecField = null;
- Field minField = null;
- Field maxField = null;
- Method getLayoutParams = null;
-
- for (ViewData view : views) {
- // TODO: If the element *specifies* anything in XML, use that instead
- Object child = mRulesEngine.getViewObject(view.node);
- if (child == null) {
- // Fallback to XML model
- return false;
- }
-
- if (getLayoutParams == null) {
- getLayoutParams = child.getClass().getMethod("getLayoutParams"); //$NON-NLS-1$
- }
- Object layoutParams = getLayoutParams.invoke(child);
- if (rowSpecField == null) {
- Class<? extends Object> layoutParamsClass = layoutParams.getClass();
- rowSpecField = layoutParamsClass.getDeclaredField("rowSpec"); //$NON-NLS-1$
- colSpecField = layoutParamsClass.getDeclaredField("columnSpec"); //$NON-NLS-1$
- rowSpecField.setAccessible(true);
- colSpecField.setAccessible(true);
- }
- assert colSpecField != null;
-
- Object rowSpec = rowSpecField.get(layoutParams);
- Object colSpec = colSpecField.get(layoutParams);
- if (spanField == null) {
- spanField = rowSpec.getClass().getDeclaredField("span"); //$NON-NLS-1$
- spanField.setAccessible(true);
- }
- assert spanField != null;
- Object rowInterval = spanField.get(rowSpec);
- Object colInterval = spanField.get(colSpec);
- if (minField == null) {
- Class<? extends Object> intervalClass = rowInterval.getClass();
- minField = intervalClass.getDeclaredField("min"); //$NON-NLS-1$
- maxField = intervalClass.getDeclaredField("max"); //$NON-NLS-1$
- minField.setAccessible(true);
- maxField.setAccessible(true);
- }
- assert maxField != null;
-
- int row = minField.getInt(rowInterval);
- int col = minField.getInt(colInterval);
- int rowEnd = maxField.getInt(rowInterval);
- int colEnd = maxField.getInt(colInterval);
-
- view.column = col;
- view.row = row;
- view.columnSpan = colEnd - col;
- view.rowSpan = rowEnd - row;
- }
-
- return true;
-
- } catch (Throwable e) {
- sAttemptSpecReflection = false;
- return false;
- }
- }
-
- /**
- * Computes the positions of the column and row boundaries
- */
- private void assignCellBounds() {
- if (!assignCellBoundsFromView()) {
- assignCellBoundsFromBounds();
- }
- initializeMaxBounds();
- mBaselines = new int[actualRowCount + 1];
- }
-
- /**
- * Computes the positions of the column and row boundaries, using actual
- * layout data from the associated GridLayout instance (stored in
- * {@link #mViewObject})
- */
- private boolean assignCellBoundsFromView() {
- if (mViewObject != null) {
- Pair<int[], int[]> cellBounds = GridModel.getAxisBounds(mViewObject);
- if (cellBounds != null) {
- int[] xs = cellBounds.getFirst();
- int[] ys = cellBounds.getSecond();
- Rect layoutBounds = layout.getBounds();
-
- // Handle "blank" grid layouts: insert a fake grid of CELL_COUNT^2 cells
- // where the user can do initial placement
- if (actualColumnCount <= 1 && actualRowCount <= 1 && mChildViews.isEmpty()) {
- final int CELL_COUNT = 1;
- xs = new int[CELL_COUNT + 1];
- ys = new int[CELL_COUNT + 1];
- int cellWidth = layoutBounds.w / CELL_COUNT;
- int cellHeight = layoutBounds.h / CELL_COUNT;
-
- for (int i = 0; i <= CELL_COUNT; i++) {
- xs[i] = i * cellWidth;
- ys[i] = i * cellHeight;
- }
- }
-
- actualColumnCount = xs.length - 1;
- actualRowCount = ys.length - 1;
-
- int layoutBoundsX = layoutBounds.x;
- int layoutBoundsY = layoutBounds.y;
- mLeft = new int[xs.length];
- mTop = new int[ys.length];
- for (int i = 0; i < xs.length; i++) {
- mLeft[i] = xs[i] + layoutBoundsX;
- }
- for (int i = 0; i < ys.length; i++) {
- mTop[i] = ys[i] + layoutBoundsY;
- }
-
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Computes the boundaries of the rows and columns by considering the bounds of the
- * children.
- */
- private void assignCellBoundsFromBounds() {
- Rect layoutBounds = layout.getBounds();
-
- // Compute the actualColumnCount and actualRowCount. This -should- be
- // as easy as declaredColumnCount + extraColumnsMap.size(),
- // but the user doesn't *have* to declare a column count (or a row count)
- // and we need both, so go and find the actual row and column maximums.
- int maxColumn = 0;
- int maxRow = 0;
- for (ViewData view : mChildViews) {
- maxColumn = max(maxColumn, view.column);
- maxRow = max(maxRow, view.row);
- }
- actualColumnCount = maxColumn + 1;
- actualRowCount = maxRow + 1;
-
- mLeft = new int[actualColumnCount + 1];
- for (int i = 1; i < actualColumnCount; i++) {
- mLeft[i] = UNDEFINED;
- }
- mLeft[0] = layoutBounds.x;
- mLeft[actualColumnCount] = layoutBounds.x2();
- mTop = new int[actualRowCount + 1];
- for (int i = 1; i < actualRowCount; i++) {
- mTop[i] = UNDEFINED;
- }
- mTop[0] = layoutBounds.y;
- mTop[actualRowCount] = layoutBounds.y2();
-
- for (ViewData view : mChildViews) {
- Rect bounds = view.node.getBounds();
- if (!bounds.isValid()) {
- continue;
- }
- int column = view.column;
- int row = view.row;
-
- if (mLeft[column] == UNDEFINED) {
- mLeft[column] = bounds.x;
- } else {
- mLeft[column] = Math.min(bounds.x, mLeft[column]);
- }
- if (mTop[row] == UNDEFINED) {
- mTop[row] = bounds.y;
- } else {
- mTop[row] = Math.min(bounds.y, mTop[row]);
- }
- }
-
- // Ensure that any empty columns/rows have a valid boundary value; for now,
- for (int i = actualColumnCount - 1; i >= 0; i--) {
- if (mLeft[i] == UNDEFINED) {
- if (i == 0) {
- mLeft[i] = layoutBounds.x;
- } else if (i < actualColumnCount - 1) {
- mLeft[i] = mLeft[i + 1] - 1;
- if (mLeft[i - 1] != UNDEFINED && mLeft[i] < mLeft[i - 1]) {
- mLeft[i] = mLeft[i - 1];
- }
- } else {
- mLeft[i] = layoutBounds.x2();
- }
- }
- }
- for (int i = actualRowCount - 1; i >= 0; i--) {
- if (mTop[i] == UNDEFINED) {
- if (i == 0) {
- mTop[i] = layoutBounds.y;
- } else if (i < actualRowCount - 1) {
- mTop[i] = mTop[i + 1] - 1;
- if (mTop[i - 1] != UNDEFINED && mTop[i] < mTop[i - 1]) {
- mTop[i] = mTop[i - 1];
- }
- } else {
- mTop[i] = layoutBounds.y2();
- }
- }
- }
-
- // The bounds should be in ascending order now
- if (false && GridLayoutRule.sDebugGridLayout) {
- for (int i = 1; i < actualRowCount; i++) {
- assert mTop[i + 1] >= mTop[i];
- }
- for (int i = 0; i < actualColumnCount; i++) {
- assert mLeft[i + 1] >= mLeft[i];
- }
- }
- }
-
- /**
- * Determine, for each row and column, what the largest x and y edges are
- * within that row or column. This is used to find a natural split point to
- * suggest when adding something "to the right of" or "below" another view.
- */
- private void initializeMaxBounds() {
- mMaxRight = new int[actualColumnCount + 1];
- mMaxBottom = new int[actualRowCount + 1];
-
- for (ViewData view : mChildViews) {
- Rect bounds = view.node.getBounds();
- if (!bounds.isValid()) {
- continue;
- }
-
- if (!view.isSpacer()) {
- int x2 = bounds.x2();
- int y2 = bounds.y2();
- int column = view.column;
- int row = view.row;
- int targetColumn = min(actualColumnCount - 1,
- column + view.columnSpan - 1);
- int targetRow = min(actualRowCount - 1, row + view.rowSpan - 1);
- IViewMetadata metadata = mRulesEngine.getMetadata(view.node.getFqcn());
- if (metadata != null) {
- Margins insets = metadata.getInsets();
- if (insets != null) {
- x2 -= insets.right;
- y2 -= insets.bottom;
- }
- }
- if (mMaxRight[targetColumn] < x2
- && ((view.gravity & (GRAVITY_CENTER_HORIZ | GRAVITY_RIGHT)) == 0)) {
- mMaxRight[targetColumn] = x2;
- }
- if (mMaxBottom[targetRow] < y2
- && ((view.gravity & (GRAVITY_CENTER_VERT | GRAVITY_BOTTOM)) == 0)) {
- mMaxBottom[targetRow] = y2;
- }
- }
- }
- }
-
- /**
- * Looks up the x[] and y[] locations of the columns and rows in the given GridLayout
- * instance.
- *
- * @param view the GridLayout object, which should already have performed layout
- * @return a pair of x[] and y[] integer arrays, or null if it could not be found
- */
- public static Pair<int[], int[]> getAxisBounds(Object view) {
- try {
- Class<?> clz = view.getClass();
- String verticalAxisName = "verticalAxis";
- Field horizontalAxis;
- try {
- horizontalAxis = clz.getDeclaredField("horizontalAxis"); //$NON-NLS-1$
- } catch (NoSuchFieldException e) {
- // Field names changed in KitKat
- horizontalAxis = clz.getDeclaredField("mHorizontalAxis"); //$NON-NLS-1$
- verticalAxisName = "mVerticalAxis";
- }
- Field verticalAxis = clz.getDeclaredField(verticalAxisName);
- horizontalAxis.setAccessible(true);
- verticalAxis.setAccessible(true);
- Object horizontal = horizontalAxis.get(view);
- Object vertical = verticalAxis.get(view);
- Field locations = horizontal.getClass().getDeclaredField("locations"); //$NON-NLS-1$
- assert locations.getType().isArray() : locations.getType();
- locations.setAccessible(true);
- Object horizontalLocations = locations.get(horizontal);
- Object verticalLocations = locations.get(vertical);
- int[] xs = (int[]) horizontalLocations;
- int[] ys = (int[]) verticalLocations;
- return Pair.of(xs, ys);
- } catch (Throwable t) {
- // Probably trying to show a GridLayout on a platform that does not support it.
- // Return null to indicate that the grid bounds must be computed from view bounds.
- return null;
- }
- }
-
- /**
- * Add a new column.
- *
- * @param selectedChildren if null or empty, add the column at the end of the grid,
- * and otherwise add it before the column of the first selected child
- * @return the newly added column spacer
- */
- public INode addColumn(List<? extends INode> selectedChildren) {
- // Determine insert index
- int newColumn = actualColumnCount;
- if (selectedChildren != null && selectedChildren.size() > 0) {
- INode first = selectedChildren.get(0);
- ViewData view = getView(first);
- newColumn = view.column;
- }
-
- INode newView = addColumn(newColumn, null, UNDEFINED, false, UNDEFINED, UNDEFINED);
- if (newView != null) {
- mRulesEngine.select(Collections.singletonList(newView));
- }
-
- return newView;
- }
-
- /**
- * Adds a new column.
- *
- * @param newColumn the column index to insert before
- * @param newView the {@link INode} to insert as the column spacer, which may be null
- * (in which case a spacer is automatically created)
- * @param columnWidthDp the width, in device independent pixels, of the column to be
- * added (which may be {@link #UNDEFINED}
- * @param split if true, split the existing column into two at the given x position
- * @param row the row to add the newView to
- * @param x the x position of the column we're inserting
- * @return the column spacer
- */
- public INode addColumn(int newColumn, INode newView, int columnWidthDp,
- boolean split, int row, int x) {
- // Insert a new column
- actualColumnCount++;
- if (declaredColumnCount != UNDEFINED) {
- declaredColumnCount++;
- setGridAttribute(layout, ATTR_COLUMN_COUNT, declaredColumnCount);
- }
-
- boolean isLastColumn = true;
- for (ViewData view : mChildViews) {
- if (view.column >= newColumn) {
- isLastColumn = false;
- break;
- }
- }
-
- for (ViewData view : mChildViews) {
- boolean columnSpanSet = false;
-
- int endColumn = view.column + view.columnSpan;
- if (view.column >= newColumn || endColumn == newColumn) {
- if (view.column == newColumn || endColumn == newColumn) {
- //if (view.row == 0) {
- if (newView == null && !isLastColumn) {
- // Insert a new spacer
- int index = getChildIndex(layout.getChildren(), view.node);
- assert view.index == index; // TODO: Get rid of getter
- if (endColumn == newColumn) {
- // This cell -ends- at the desired position: insert it after
- index++;
- }
-
- ViewData newViewData = addSpacer(layout, index,
- split ? row : UNDEFINED,
- split ? newColumn - 1 : UNDEFINED,
- columnWidthDp != UNDEFINED ? columnWidthDp : DEFAULT_CELL_WIDTH,
- DEFAULT_CELL_HEIGHT);
- newViewData.column = newColumn - 1;
- newViewData.row = row;
- newView = newViewData.node;
- }
-
- // Set the actual row number on the first cell on the new row.
- // 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.
- if (view.column == newColumn) {
- view.column++;
- setGridAttribute(view.node, ATTR_LAYOUT_COLUMN, view.column);
- } // else: endColumn == newColumn: handled below
- } else if (getGridAttribute(view.node, ATTR_LAYOUT_COLUMN) != null) {
- view.column++;
- setGridAttribute(view.node, ATTR_LAYOUT_COLUMN, view.column);
- }
- } else if (endColumn > newColumn) {
- view.columnSpan++;
- setColumnSpanAttribute(view.node, view.columnSpan);
- columnSpanSet = true;
- }
-
- if (split && !columnSpanSet && view.node.getBounds().x2() > x) {
- if (view.node.getBounds().x < x) {
- view.columnSpan++;
- setColumnSpanAttribute(view.node, view.columnSpan);
- }
- }
- }
-
- // Hardcode the row numbers if the last column is a new column such that
- // they don't jump back to backfill the previous row's new last cell
- if (isLastColumn) {
- for (ViewData view : mChildViews) {
- if (view.column == 0 && view.row > 0) {
- setGridAttribute(view.node, ATTR_LAYOUT_ROW, view.row);
- }
- }
- if (split) {
- assert newView == null;
- addSpacer(layout, -1, row, newColumn -1,
- columnWidthDp != UNDEFINED ? columnWidthDp : DEFAULT_CELL_WIDTH,
- SPACER_SIZE_DP);
- }
- }
-
- return newView;
- }
-
- /**
- * Removes the columns containing the given selection
- *
- * @param selectedChildren a list of nodes whose columns should be deleted
- */
- public void removeColumns(List<? extends INode> selectedChildren) {
- if (selectedChildren.size() == 0) {
- return;
- }
-
- // Figure out which columns should be removed
- Set<Integer> removeColumns = new HashSet<Integer>();
- Set<ViewData> removedViews = new HashSet<ViewData>();
- for (INode child : selectedChildren) {
- ViewData view = getView(child);
- removedViews.add(view);
- removeColumns.add(view.column);
- }
- // Sort them in descending order such that we can process each
- // deletion independently
- List<Integer> removed = new ArrayList<Integer>(removeColumns);
- Collections.sort(removed, Collections.reverseOrder());
-
- for (int removedColumn : removed) {
- // Remove column.
- // First, adjust column count.
- // TODO: Don't do this if the column being deleted is outside
- // the declared column range!
- // TODO: Do this under a write lock? / editXml lock?
- actualColumnCount--;
- if (declaredColumnCount != UNDEFINED) {
- declaredColumnCount--;
- }
-
- // Remove any elements that begin in the deleted columns...
- // If they have colspan > 1, then we must insert a spacer instead.
- // For any other elements that overlap, we need to subtract from the span.
-
- for (ViewData view : mChildViews) {
- if (view.column == removedColumn) {
- int index = getChildIndex(layout.getChildren(), view.node);
- assert view.index == index; // TODO: Get rid of getter
- if (view.columnSpan > 1) {
- // Make a new spacer which is the width of the following
- // columns
- int columnWidth = getColumnWidth(removedColumn, view.columnSpan) -
- getColumnWidth(removedColumn, 1);
- int columnWidthDip = mRulesEngine.pxToDp(columnWidth);
- ViewData spacer = addSpacer(layout, index, UNDEFINED, UNDEFINED,
- columnWidthDip, SPACER_SIZE_DP);
- spacer.row = 0;
- spacer.column = removedColumn;
- }
- layout.removeChild(view.node);
- } else if (view.column < removedColumn
- && view.column + view.columnSpan > removedColumn) {
- // Subtract column span to skip this item
- view.columnSpan--;
- setColumnSpanAttribute(view.node, view.columnSpan);
- } else if (view.column > removedColumn) {
- view.column--;
- if (getGridAttribute(view.node, ATTR_LAYOUT_COLUMN) != null) {
- setGridAttribute(view.node, ATTR_LAYOUT_COLUMN, view.column);
- }
- }
- }
- }
-
- // Remove children from child list!
- if (removedViews.size() <= 2) {
- mChildViews.removeAll(removedViews);
- } else {
- List<ViewData> remaining =
- new ArrayList<ViewData>(mChildViews.size() - removedViews.size());
- for (ViewData view : mChildViews) {
- if (!removedViews.contains(view)) {
- remaining.add(view);
- }
- }
- mChildViews = remaining;
- }
-
- //if (declaredColumnCount != UNDEFINED) {
- setGridAttribute(layout, ATTR_COLUMN_COUNT, actualColumnCount);
- //}
-
- }
-
- /**
- * Add a new row.
- *
- * @param selectedChildren if null or empty, add the row at the bottom of the grid,
- * and otherwise add it before the row of the first selected child
- * @return the newly added row spacer
- */
- public INode addRow(List<? extends INode> selectedChildren) {
- // Determine insert index
- int newRow = actualRowCount;
- if (selectedChildren.size() > 0) {
- INode first = selectedChildren.get(0);
- ViewData view = getView(first);
- newRow = view.row;
- }
-
- INode newView = addRow(newRow, null, UNDEFINED, false, UNDEFINED, UNDEFINED);
- if (newView != null) {
- mRulesEngine.select(Collections.singletonList(newView));
- }
-
- return newView;
- }
-
- /**
- * Adds a new column.
- *
- * @param newRow the row index to insert before
- * @param newView the {@link INode} to insert as the row spacer, which may be null (in
- * which case a spacer is automatically created)
- * @param rowHeightDp the height, in device independent pixels, of the row to be added
- * (which may be {@link #UNDEFINED}
- * @param split if true, split the existing row into two at the given y position
- * @param column the column to add the newView to
- * @param y the y position of the row we're inserting
- * @return the row spacer
- */
- public INode addRow(int newRow, INode newView, int rowHeightDp, boolean split,
- int column, int y) {
- actualRowCount++;
- if (declaredRowCount != UNDEFINED) {
- declaredRowCount++;
- setGridAttribute(layout, ATTR_ROW_COUNT, declaredRowCount);
- }
-
- boolean added = false;
- for (ViewData view : mChildViews) {
- if (view.row >= newRow) {
- // Adjust the column count
- if (view.row == newRow && view.column == 0) {
- // Insert a new spacer
- if (newView == null) {
- int index = getChildIndex(layout.getChildren(), view.node);
- assert view.index == index; // TODO: Get rid of getter
- if (declaredColumnCount != UNDEFINED && !split) {
- setGridAttribute(layout, ATTR_COLUMN_COUNT, declaredColumnCount);
- }
- ViewData newViewData = addSpacer(layout, index,
- split ? newRow - 1 : UNDEFINED,
- split ? column : UNDEFINED,
- SPACER_SIZE_DP,
- rowHeightDp != UNDEFINED ? rowHeightDp : DEFAULT_CELL_HEIGHT);
- newViewData.column = column;
- newViewData.row = newRow - 1;
- newView = newViewData.node;
- }
-
- // Set the actual row number on the first cell on the new row.
- // 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.row++;
- setGridAttribute(view.node, ATTR_LAYOUT_ROW, view.row);
-
- added = true;
- } else if (getGridAttribute(view.node, ATTR_LAYOUT_ROW) != null) {
- view.row++;
- setGridAttribute(view.node, ATTR_LAYOUT_ROW, view.row);
- }
- } else {
- int endRow = view.row + view.rowSpan;
- if (endRow > newRow) {
- view.rowSpan++;
- setRowSpanAttribute(view.node, view.rowSpan);
- } else if (split && view.node.getBounds().y2() > y) {
- if (view.node.getBounds().y < y) {
- view.rowSpan++;
- setRowSpanAttribute(view.node, view.rowSpan);
- }
- }
- }
- }
-
- if (!added) {
- // Append a row at the end
- if (newView == null) {
- ViewData newViewData = addSpacer(layout, -1, UNDEFINED, UNDEFINED,
- SPACER_SIZE_DP,
- rowHeightDp != UNDEFINED ? rowHeightDp : DEFAULT_CELL_HEIGHT);
- newViewData.column = column;
- // TODO: MAke sure this row number is right!
- newViewData.row = split ? newRow - 1 : newRow;
- newView = newViewData.node;
- }
- if (declaredColumnCount != UNDEFINED && !split) {
- setGridAttribute(layout, ATTR_COLUMN_COUNT, declaredColumnCount);
- }
- if (split) {
- setGridAttribute(newView, ATTR_LAYOUT_ROW, newRow - 1);
- setGridAttribute(newView, ATTR_LAYOUT_COLUMN, column);
- }
- }
-
- return newView;
- }
-
- /**
- * Removes the rows containing the given selection
- *
- * @param selectedChildren a list of nodes whose rows should be deleted
- */
- public void removeRows(List<? extends INode> selectedChildren) {
- if (selectedChildren.size() == 0) {
- return;
- }
-
- // Figure out which rows should be removed
- Set<ViewData> removedViews = new HashSet<ViewData>();
- Set<Integer> removedRows = new HashSet<Integer>();
- for (INode child : selectedChildren) {
- ViewData view = getView(child);
- removedViews.add(view);
- removedRows.add(view.row);
- }
- // Sort them in descending order such that we can process each
- // deletion independently
- List<Integer> removed = new ArrayList<Integer>(removedRows);
- Collections.sort(removed, Collections.reverseOrder());
-
- for (int removedRow : removed) {
- // Remove row.
- // First, adjust row count.
- // TODO: Don't do this if the row being deleted is outside
- // the declared row range!
- actualRowCount--;
- if (declaredRowCount != UNDEFINED) {
- declaredRowCount--;
- setGridAttribute(layout, ATTR_ROW_COUNT, declaredRowCount);
- }
-
- // Remove any elements that begin in the deleted rows...
- // If they have colspan > 1, then we must hardcode a new row number
- // instead.
- // For any other elements that overlap, we need to subtract from the span.
-
- for (ViewData view : mChildViews) {
- if (view.row == removedRow) {
- // We don't have to worry about a rowSpan > 1 here, because even
- // if it is, those rowspans are not used to assign default row/column
- // positions for other cells
-// TODO: Check this; it differs from the removeColumns logic!
- layout.removeChild(view.node);
- } else if (view.row > removedRow) {
- view.row--;
- if (getGridAttribute(view.node, ATTR_LAYOUT_ROW) != null) {
- setGridAttribute(view.node, ATTR_LAYOUT_ROW, view.row);
- }
- } else if (view.row < removedRow
- && view.row + view.rowSpan > removedRow) {
- // Subtract row span to skip this item
- view.rowSpan--;
- setRowSpanAttribute(view.node, view.rowSpan);
- }
- }
- }
-
- // Remove children from child list!
- if (removedViews.size() <= 2) {
- mChildViews.removeAll(removedViews);
- } else {
- List<ViewData> remaining =
- new ArrayList<ViewData>(mChildViews.size() - removedViews.size());
- for (ViewData view : mChildViews) {
- if (!removedViews.contains(view)) {
- remaining.add(view);
- }
- }
- mChildViews = remaining;
- }
- }
-
- /**
- * Returns the row containing the given y line
- *
- * @param y the vertical position
- * @return the row containing the given line
- */
- public int getRow(int y) {
- int row = Arrays.binarySearch(mTop, y);
- if (row == -1) {
- // Smaller than the first element; just use the first row
- return 0;
- } else if (row < 0) {
- row = -(row + 2);
- }
-
- return row;
- }
-
- /**
- * Returns the column containing the given x line
- *
- * @param x the horizontal position
- * @return the column containing the given line
- */
- public int getColumn(int x) {
- int column = Arrays.binarySearch(mLeft, x);
- if (column == -1) {
- // Smaller than the first element; just use the first column
- return 0;
- } else if (column < 0) {
- column = -(column + 2);
- }
-
- return column;
- }
-
- /**
- * Returns the closest row to the given y line. This is
- * either the row containing the line, or the row below it.
- *
- * @param y the vertical position
- * @return the closest row
- */
- public int getClosestRow(int y) {
- int row = Arrays.binarySearch(mTop, y);
- if (row == -1) {
- // Smaller than the first element; just use the first column
- return 0;
- } else if (row < 0) {
- row = -(row + 2);
- }
-
- if (getRowDistance(row, y) < getRowDistance(row + 1, y)) {
- return row;
- } else {
- return row + 1;
- }
- }
-
- /**
- * Returns the closest column to the given x line. This is
- * either the column containing the line, or the column following it.
- *
- * @param x the horizontal position
- * @return the closest column
- */
- public int getClosestColumn(int x) {
- int column = Arrays.binarySearch(mLeft, x);
- if (column == -1) {
- // Smaller than the first element; just use the first column
- return 0;
- } else if (column < 0) {
- column = -(column + 2);
- }
-
- if (getColumnDistance(column, x) < getColumnDistance(column + 1, x)) {
- return column;
- } else {
- return column + 1;
- }
- }
-
- /**
- * Returns the distance between the given x position and the beginning of the given column
- *
- * @param column the column
- * @param x the x position
- * @return the distance between the two
- */
- public int getColumnDistance(int column, int x) {
- return abs(getColumnX(column) - x);
- }
-
- /**
- * Returns the actual width of the given column. This returns the difference between
- * the rightmost edge of the views (not including spacers) and the left edge of the
- * column.
- *
- * @param column the column
- * @return the actual width of the non-spacer views in the column
- */
- public int getColumnActualWidth(int column) {
- return getColumnMaxX(column) - getColumnX(column);
- }
-
- /**
- * Returns the distance between the given y position and the top of the given row
- *
- * @param row the row
- * @param y the y position
- * @return the distance between the two
- */
- public int getRowDistance(int row, int y) {
- return abs(getRowY(row) - y);
- }
-
- /**
- * Returns the y position of the top of the given row
- *
- * @param row the target row
- * @return the y position of its top edge
- */
- public int getRowY(int row) {
- return mTop[min(mTop.length - 1, max(0, row))];
- }
-
- /**
- * Returns the bottom-most edge of any of the non-spacer children in the given row
- *
- * @param row the target row
- * @return the bottom-most edge of any of the non-spacer children in the row
- */
- public int getRowMaxY(int row) {
- return mMaxBottom[min(mMaxBottom.length - 1, max(0, row))];
- }
-
- /**
- * Returns the actual height of the given row. This returns the difference between
- * the bottom-most edge of the views (not including spacers) and the top edge of the
- * row.
- *
- * @param row the row
- * @return the actual height of the non-spacer views in the row
- */
- public int getRowActualHeight(int row) {
- return getRowMaxY(row) - getRowY(row);
- }
-
- /**
- * Returns a list of all the nodes that intersects the rows in the range
- * {@code y1 <= y <= y2}.
- *
- * @param y1 the starting y, inclusive
- * @param y2 the ending y, inclusive
- * @return a list of nodes intersecting the given rows, never null but possibly empty
- */
- public Collection<INode> getIntersectsRow(int y1, int y2) {
- List<INode> nodes = new ArrayList<INode>();
-
- for (ViewData view : mChildViews) {
- if (!view.isSpacer()) {
- Rect bounds = view.node.getBounds();
- if (bounds.y2() >= y1 && bounds.y <= y2) {
- nodes.add(view.node);
- }
- }
- }
-
- return nodes;
- }
-
- /**
- * Returns the height of the given row or rows (if the rowSpan is greater than 1)
- *
- * @param row the target row
- * @param rowSpan the row span
- * @return the height in pixels of the given rows
- */
- public int getRowHeight(int row, int rowSpan) {
- return getRowY(row + rowSpan) - getRowY(row);
- }
-
- /**
- * Returns the x position of the left edge of the given column
- *
- * @param column the target column
- * @return the x position of its left edge
- */
- public int getColumnX(int column) {
- return mLeft[min(mLeft.length - 1, max(0, column))];
- }
-
- /**
- * Returns the rightmost edge of any of the non-spacer children in the given row
- *
- * @param column the target column
- * @return the rightmost edge of any of the non-spacer children in the column
- */
- public int getColumnMaxX(int column) {
- return mMaxRight[min(mMaxRight.length - 1, max(0, column))];
- }
-
- /**
- * Returns the width of the given column or columns (if the columnSpan is greater than 1)
- *
- * @param column the target column
- * @param columnSpan the column span
- * @return the width in pixels of the given columns
- */
- public int getColumnWidth(int column, int columnSpan) {
- return getColumnX(column + columnSpan) - getColumnX(column);
- }
-
- /**
- * Returns the bounds of the cell at the given row and column position, with the given
- * row and column spans.
- *
- * @param row the target row
- * @param column the target column
- * @param rowSpan the row span
- * @param columnSpan the column span
- * @return the bounds, in pixels, of the given cell
- */
- public Rect getCellBounds(int row, int column, int rowSpan, int columnSpan) {
- return new Rect(getColumnX(column), getRowY(row),
- getColumnWidth(column, columnSpan),
- getRowHeight(row, rowSpan));
- }
-
- /**
- * Produces a display of view contents along with the pixel positions of each
- * row/column, like the following (used for diagnostics only)
- *
- * <pre>
- * |0 |49 |143 |192 |240
- * 36| | |button2 |
- * 72| |radioButton1 |button2 |
- * 74|button1 |radioButton1 |button2 |
- * 108|button1 | |button2 |
- * 110| | |button2 |
- * 149| | | |
- * 320
- * </pre>
- */
- @Override
- public String toString() {
- // Dump out the view table
- int cellWidth = 25;
-
- List<List<List<ViewData>>> rowList = new ArrayList<List<List<ViewData>>>(mTop.length);
- for (int row = 0; row < mTop.length; row++) {
- List<List<ViewData>> columnList = new ArrayList<List<ViewData>>(mLeft.length);
- for (int col = 0; col < mLeft.length; col++) {
- columnList.add(new ArrayList<ViewData>(4));
- }
- rowList.add(columnList);
- }
- for (ViewData view : mChildViews) {
- for (int i = 0; i < view.rowSpan; i++) {
- if (view.row + i > mTop.length) { // Guard against bogus span values
- break;
- }
- if (rowList.size() <= view.row + i) {
- break;
- }
- for (int j = 0; j < view.columnSpan; j++) {
- List<List<ViewData>> columnList = rowList.get(view.row + i);
- if (columnList.size() <= view.column + j) {
- break;
- }
- columnList.get(view.column + j).add(view);
- }
- }
- }
-
- StringWriter stringWriter = new StringWriter();
- PrintWriter out = new PrintWriter(stringWriter);
- out.printf("%" + cellWidth + "s", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- for (int col = 0; col < actualColumnCount + 1; col++) {
- out.printf("|%-" + (cellWidth - 1) + "d", mLeft[col]); //$NON-NLS-1$ //$NON-NLS-2$
- }
- out.printf("\n"); //$NON-NLS-1$
- for (int row = 0; row < actualRowCount + 1; row++) {
- out.printf("%" + cellWidth + "d", mTop[row]); //$NON-NLS-1$ //$NON-NLS-2$
- if (row == actualRowCount) {
- break;
- }
- for (int col = 0; col < actualColumnCount; col++) {
- List<ViewData> views = rowList.get(row).get(col);
-
- StringBuilder sb = new StringBuilder();
- for (ViewData view : views) {
- String id = view != null ? view.getId() : ""; //$NON-NLS-1$
- if (id.startsWith(NEW_ID_PREFIX)) {
- id = id.substring(NEW_ID_PREFIX.length());
- }
- if (id.length() > cellWidth - 2) {
- id = id.substring(0, cellWidth - 2);
- }
- if (sb.length() > 0) {
- sb.append(',');
- }
- sb.append(id);
- }
- String cellString = sb.toString();
- if (cellString.contains(",") && cellString.length() > cellWidth - 2) { //$NON-NLS-1$
- cellString = cellString.substring(0, cellWidth - 6) + "...,"; //$NON-NLS-1$
- }
- out.printf("|%-" + (cellWidth - 2) + "s ", cellString); //$NON-NLS-1$ //$NON-NLS-2$
- }
- out.printf("\n"); //$NON-NLS-1$
- }
-
- out.flush();
- return stringWriter.toString();
- }
-
- /**
- * Split a cell into two or three columns.
- *
- * @param newColumn The column number to insert before
- * @param insertMarginColumn If false, then the cell at newColumn -1 is split with the
- * left part taking up exactly columnWidthDp dips. If true, then the column
- * is split twice; the left part is the implicit width of the column, the
- * new middle (margin) column is exactly the columnWidthDp size and the
- * right column is the remaining space of the old cell.
- * @param columnWidthDp The width of the column inserted before the new column (or if
- * insertMarginColumn is false, then the width of the margin column)
- * @param x the x coordinate of the new column
- */
- public void splitColumn(int newColumn, boolean insertMarginColumn, int columnWidthDp, int x) {
- actualColumnCount++;
-
- // Insert a new column
- if (declaredColumnCount != UNDEFINED) {
- declaredColumnCount++;
- if (insertMarginColumn) {
- declaredColumnCount++;
- }
- setGridAttribute(layout, ATTR_COLUMN_COUNT, declaredColumnCount);
- }
-
- // Are we inserting a new last column in the grid? That requires some special handling...
- boolean isLastColumn = true;
- for (ViewData view : mChildViews) {
- if (view.column >= newColumn) {
- isLastColumn = false;
- break;
- }
- }
-
- // Hardcode the row numbers if the last column is a new column such that
- // they don't jump back to backfill the previous row's new last cell:
- // TODO: Only do this for horizontal layouts!
- if (isLastColumn) {
- for (ViewData view : mChildViews) {
- if (view.column == 0 && view.row > 0) {
- if (getGridAttribute(view.node, ATTR_LAYOUT_ROW) == null) {
- setGridAttribute(view.node, ATTR_LAYOUT_ROW, view.row);
- }
- }
- }
- }
-
- // Find the spacer which marks this column, and if found, mark it as a split
- ViewData prevColumnSpacer = null;
- for (ViewData view : mChildViews) {
- if (view.column == newColumn - 1 && view.isColumnSpacer()) {
- prevColumnSpacer = view;
- break;
- }
- }
-
- // Process all existing grid elements:
- // * Increase column numbers for all columns that have a hardcoded column number
- // greater than the new column
- // * Set an explicit column=0 where needed (TODO: Implement this)
- // * Increase the columnSpan for all columns that overlap the newly inserted column edge
- // * Split the spacer which defined the size of this column into two
- // (and if not found, create a new spacer)
- //
- for (ViewData view : mChildViews) {
- if (view == prevColumnSpacer) {
- continue;
- }
-
- INode node = view.node;
- int column = view.column;
- if (column > newColumn || (column == newColumn && view.node.getBounds().x2() > x)) {
- // ALWAYS set the column, because
- // (1) if it has been set, it needs to be corrected
- // (2) if it has not been set, it needs to be set to cause this column
- // to skip over the new column (there may be no views for the new
- // column on this row).
- // TODO: Enhance this such that we only set the column to a skip number
- // where necessary, e.g. only on the FIRST view on this row following the
- // skipped column!
-
- //if (getGridAttribute(node, ATTR_LAYOUT_COLUMN) != null) {
- view.column += insertMarginColumn ? 2 : 1;
- setGridAttribute(node, ATTR_LAYOUT_COLUMN, view.column);
- //}
- } else if (!view.isSpacer()) {
- // Adjust the column span? We must increase it if
- // (1) the new column is inside the range [column, column + columnSpan]
- // (2) the new column is within the last cell in the column span,
- // and the exact X location of the split is within the horizontal
- // *bounds* of this node (provided it has gravity=left)
- // (3) the new column is within the last cell and the cell has gravity
- // right or gravity center
- int endColumn = column + view.columnSpan;
- if (endColumn > newColumn
- || endColumn == newColumn && (view.node.getBounds().x2() > x
- || GravityHelper.isConstrainedHorizontally(view.gravity)
- && !GravityHelper.isLeftAligned(view.gravity))) {
- // This cell spans the new insert position, so increment the column span
- view.columnSpan += insertMarginColumn ? 2 : 1;
- setColumnSpanAttribute(node, view.columnSpan);
- }
- }
- }
-
- // Insert new spacer:
- if (prevColumnSpacer != null) {
- int px = getColumnWidth(newColumn - 1, 1);
- if (insertMarginColumn || columnWidthDp == 0) {
- px -= getColumnActualWidth(newColumn - 1);
- }
- int dp = mRulesEngine.pxToDp(px);
- int remaining = dp - columnWidthDp;
- if (remaining > 0) {
- prevColumnSpacer.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH,
- String.format(VALUE_N_DP, remaining));
- prevColumnSpacer.column = insertMarginColumn ? newColumn + 1 : newColumn;
- setGridAttribute(prevColumnSpacer.node, ATTR_LAYOUT_COLUMN,
- prevColumnSpacer.column);
- }
- }
-
- if (columnWidthDp > 0) {
- int index = prevColumnSpacer != null ? prevColumnSpacer.index : -1;
-
- addSpacer(layout, index, 0, insertMarginColumn ? newColumn : newColumn - 1,
- columnWidthDp, SPACER_SIZE_DP);
- }
- }
-
- /**
- * Split a cell into two or three rows.
- *
- * @param newRow The row number to insert before
- * @param insertMarginRow If false, then the cell at newRow -1 is split with the above
- * part taking up exactly rowHeightDp dips. If true, then the row is split
- * twice; the top part is the implicit height of the row, the new middle
- * (margin) row is exactly the rowHeightDp size and the bottom column is
- * the remaining space of the old cell.
- * @param rowHeightDp The height of the row inserted before the new row (or if
- * insertMarginRow is false, then the height of the margin row)
- * @param y the y coordinate of the new row
- */
- public void splitRow(int newRow, boolean insertMarginRow, int rowHeightDp, int y) {
- actualRowCount++;
-
- // Insert a new row
- if (declaredRowCount != UNDEFINED) {
- declaredRowCount++;
- if (insertMarginRow) {
- declaredRowCount++;
- }
- setGridAttribute(layout, ATTR_ROW_COUNT, declaredRowCount);
- }
-
- // Find the spacer which marks this row, and if found, mark it as a split
- ViewData prevRowSpacer = null;
- for (ViewData view : mChildViews) {
- if (view.row == newRow - 1 && view.isRowSpacer()) {
- prevRowSpacer = view;
- break;
- }
- }
-
- // Se splitColumn() for details
- for (ViewData view : mChildViews) {
- if (view == prevRowSpacer) {
- continue;
- }
-
- INode node = view.node;
- int row = view.row;
- if (row > newRow || (row == newRow && view.node.getBounds().y2() > y)) {
- //if (getGridAttribute(node, ATTR_LAYOUT_ROW) != null) {
- view.row += insertMarginRow ? 2 : 1;
- setGridAttribute(node, ATTR_LAYOUT_ROW, view.row);
- //}
- } else if (!view.isSpacer()) {
- int endRow = row + view.rowSpan;
- if (endRow > newRow
- || endRow == newRow && (view.node.getBounds().y2() > y
- || GravityHelper.isConstrainedVertically(view.gravity)
- && !GravityHelper.isTopAligned(view.gravity))) {
- // This cell spans the new insert position, so increment the row span
- view.rowSpan += insertMarginRow ? 2 : 1;
- setRowSpanAttribute(node, view.rowSpan);
- }
- }
- }
-
- // Insert new spacer:
- if (prevRowSpacer != null) {
- int px = getRowHeight(newRow - 1, 1);
- if (insertMarginRow || rowHeightDp == 0) {
- px -= getRowActualHeight(newRow - 1);
- }
- int dp = mRulesEngine.pxToDp(px);
- int remaining = dp - rowHeightDp;
- if (remaining > 0) {
- prevRowSpacer.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT,
- String.format(VALUE_N_DP, remaining));
- prevRowSpacer.row = insertMarginRow ? newRow + 1 : newRow;
- setGridAttribute(prevRowSpacer.node, ATTR_LAYOUT_ROW, prevRowSpacer.row);
- }
- }
-
- if (rowHeightDp > 0) {
- int index = prevRowSpacer != null ? prevRowSpacer.index : -1;
- addSpacer(layout, index, insertMarginRow ? newRow : newRow - 1,
- 0, SPACER_SIZE_DP, rowHeightDp);
- }
- }
-
- /**
- * 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.
- */
- public class ViewData {
- public final INode node;
- public final int index;
- public int row;
- public int column;
- public int rowSpan;
- public int columnSpan;
- public int gravity;
-
- ViewData(INode n, int index) {
- node = n;
- this.index = index;
-
- 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() {
- setGridAttribute(node, ATTR_LAYOUT_COLUMN, column);
- setGridAttribute(node, ATTR_LAYOUT_ROW, row);
- }
-
- /** Returns the id of this node, or makes one up for display purposes */
- String getId() {
- String id = node.getStringAttr(ANDROID_URI, ATTR_ID);
- if (id == null) {
- id = "<unknownid>"; //$NON-NLS-1$
- String fqn = node.getFqcn();
- fqn = fqn.substring(fqn.lastIndexOf('.') + 1);
- id = fqn + "-"
- + Integer.toString(System.identityHashCode(node)).substring(0, 3);
- }
-
- return id;
- }
-
- /** Returns true if this {@link ViewData} represents a spacer */
- boolean isSpacer() {
- return isSpace(node.getFqcn());
- }
-
- /**
- * Returns true if this {@link ViewData} represents a column spacer
- */
- boolean isColumnSpacer() {
- return isSpacer() &&
- // Any spacer not found in column 0 is a column spacer since we
- // place all horizontal spacers in column 0
- ((column > 0)
- // TODO: Find a cleaner way. Maybe set ids on the elements in (0,0) and
- // for column distinguish by id. Or at least only do this for column 0!
- || !SPACER_SIZE.equals(node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_WIDTH)));
- }
-
- /**
- * Returns true if this {@link ViewData} represents a row spacer
- */
- boolean isRowSpacer() {
- return isSpacer() &&
- // Any spacer not found in row 0 is a row spacer since we
- // place all vertical spacers in row 0
- ((row > 0)
- // TODO: Find a cleaner way. Maybe set ids on the elements in (0,0) and
- // for column distinguish by id. Or at least only do this for column 0!
- || !SPACER_SIZE.equals(node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_HEIGHT)));
- }
- }
-
- /**
- * Sets the column span of the given node to the given value (or if the value is 1,
- * removes it)
- *
- * @param node the target node
- * @param span the new column span
- */
- public void setColumnSpanAttribute(INode node, int span) {
- setGridAttribute(node, ATTR_LAYOUT_COLUMN_SPAN, span > 1 ? Integer.toString(span) : null);
- }
-
- /**
- * Sets the row span of the given node to the given value (or if the value is 1,
- * removes it)
- *
- * @param node the target node
- * @param span the new row span
- */
- 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 */
- static int getChildIndex(INode[] children, INode target) {
- int index = 0;
- for (INode child : children) {
- if (child == target) {
- return index;
- }
- index++;
- }
-
- return -1;
- }
-
- /**
- * Update the model to account for the given nodes getting deleted. The nodes
- * are not actually deleted by this method; that is assumed to be performed by the
- * caller. Instead this method performs whatever model updates are necessary to
- * preserve the grid structure.
- *
- * @param nodes the nodes to be deleted
- */
- public void onDeleted(@NonNull List<INode> nodes) {
- if (nodes.size() == 0) {
- return;
- }
-
- // Attempt to clean up spacer objects for any newly-empty rows or columns
- // as the result of this deletion
-
- Set<INode> deleted = new HashSet<INode>();
-
- for (INode child : nodes) {
- // We don't care about deletion of spacers
- String fqcn = child.getFqcn();
- if (fqcn.equals(FQCN_SPACE) || fqcn.equals(FQCN_SPACE_V7)) {
- continue;
- }
- deleted.add(child);
- }
-
- Set<Integer> usedColumns = new HashSet<Integer>(actualColumnCount);
- Set<Integer> usedRows = new HashSet<Integer>(actualRowCount);
- Multimap<Integer, ViewData> columnSpacers = ArrayListMultimap.create(actualColumnCount, 2);
- Multimap<Integer, ViewData> rowSpacers = ArrayListMultimap.create(actualRowCount, 2);
- Set<ViewData> removedViews = new HashSet<ViewData>();
-
- for (ViewData view : mChildViews) {
- if (deleted.contains(view.node)) {
- removedViews.add(view);
- } else if (view.isColumnSpacer()) {
- columnSpacers.put(view.column, view);
- } else if (view.isRowSpacer()) {
- rowSpacers.put(view.row, view);
- } else {
- usedColumns.add(Integer.valueOf(view.column));
- usedRows.add(Integer.valueOf(view.row));
- }
- }
-
- if (usedColumns.size() == 0 || usedRows.size() == 0) {
- // No more views - just remove all the spacers
- for (ViewData spacer : columnSpacers.values()) {
- layout.removeChild(spacer.node);
- }
- for (ViewData spacer : rowSpacers.values()) {
- layout.removeChild(spacer.node);
- }
- mChildViews.clear();
- actualColumnCount = 0;
- declaredColumnCount = 2;
- actualRowCount = 0;
- declaredRowCount = UNDEFINED;
- setGridAttribute(layout, ATTR_COLUMN_COUNT, 2);
-
- return;
- }
-
- // Determine columns to introduce spacers into:
- // This is tricky; I should NOT combine spacers if there are cells tied to
- // individual ones
-
- // TODO: Invalidate column sizes too! Otherwise repeated updates might get confused!
- // Similarly, inserts need to do the same!
-
- // Produce map of old column numbers to new column numbers
- // Collapse regions of consecutive space and non-space ranges together
- int[] columnMap = new int[actualColumnCount + 1]; // +1: Easily handle columnSpans as well
- int newColumn = 0;
- boolean prevUsed = usedColumns.contains(0);
- for (int column = 1; column < actualColumnCount; column++) {
- boolean used = usedColumns.contains(column);
- if (used || prevUsed != used) {
- newColumn++;
- prevUsed = used;
- }
- columnMap[column] = newColumn;
- }
- newColumn++;
- columnMap[actualColumnCount] = newColumn;
- assert columnMap[0] == 0;
-
- int[] rowMap = new int[actualRowCount + 1]; // +1: Easily handle rowSpans as well
- int newRow = 0;
- prevUsed = usedRows.contains(0);
- for (int row = 1; row < actualRowCount; row++) {
- boolean used = usedRows.contains(row);
- if (used || prevUsed != used) {
- newRow++;
- prevUsed = used;
- }
- rowMap[row] = newRow;
- }
- newRow++;
- rowMap[actualRowCount] = newRow;
- assert rowMap[0] == 0;
-
-
- // Adjust column and row numbers to account for deletions: for a given cell, if it
- // is to the right of a deleted column, reduce its column number, and if it only
- // spans across the deleted column, reduce its column span.
- for (ViewData view : mChildViews) {
- if (removedViews.contains(view)) {
- continue;
- }
- int newColumnStart = columnMap[Math.min(columnMap.length - 1, view.column)];
- // Gracefully handle rogue/invalid columnSpans in the XML
- int newColumnEnd = columnMap[Math.min(columnMap.length - 1,
- view.column + view.columnSpan)];
- if (newColumnStart != view.column) {
- view.column = newColumnStart;
- setGridAttribute(view.node, ATTR_LAYOUT_COLUMN, view.column);
- }
-
- int columnSpan = newColumnEnd - newColumnStart;
- if (columnSpan != view.columnSpan) {
- if (columnSpan >= 1) {
- view.columnSpan = columnSpan;
- setColumnSpanAttribute(view.node, view.columnSpan);
- } // else: merging spacing columns together
- }
-
-
- int newRowStart = rowMap[Math.min(rowMap.length - 1, view.row)];
- int newRowEnd = rowMap[Math.min(rowMap.length - 1, view.row + view.rowSpan)];
- if (newRowStart != view.row) {
- view.row = newRowStart;
- setGridAttribute(view.node, ATTR_LAYOUT_ROW, view.row);
- }
-
- int rowSpan = newRowEnd - newRowStart;
- if (rowSpan != view.rowSpan) {
- if (rowSpan >= 1) {
- view.rowSpan = rowSpan;
- setRowSpanAttribute(view.node, view.rowSpan);
- } // else: merging spacing rows together
- }
- }
-
- // Merge spacers (and add spacers for newly empty columns)
- int start = 0;
- while (start < actualColumnCount) {
- // Find next unused span
- while (start < actualColumnCount && usedColumns.contains(start)) {
- start++;
- }
- if (start == actualColumnCount) {
- break;
- }
- assert !usedColumns.contains(start);
- // Find the next span of unused columns and produce a SINGLE
- // spacer for that range (unless it's a zero-sized columns)
- int end = start + 1;
- for (; end < actualColumnCount; end++) {
- if (usedColumns.contains(end)) {
- break;
- }
- }
-
- // Add up column sizes
- int width = getColumnWidth(start, end - start);
-
- // Find all spacers: the first one found should be moved to the start column
- // and assigned to the full height of the columns, and
- // the column count reduced by the corresponding amount
-
- // TODO: if width = 0, fully remove
-
- boolean isFirstSpacer = true;
- for (int column = start; column < end; column++) {
- Collection<ViewData> spacers = columnSpacers.get(column);
- if (spacers != null && !spacers.isEmpty()) {
- // Avoid ConcurrentModificationException since we're inserting into the
- // map within this loop (always at a different index, but the map doesn't
- // know that)
- spacers = new ArrayList<ViewData>(spacers);
- for (ViewData spacer : spacers) {
- if (isFirstSpacer) {
- isFirstSpacer = false;
- spacer.column = columnMap[start];
- setGridAttribute(spacer.node, ATTR_LAYOUT_COLUMN, spacer.column);
- if (end - start > 1) {
- // Compute a merged width for all the spacers (not needed if
- // there's just one spacer; it should already have the correct width)
- int columnWidthDp = mRulesEngine.pxToDp(width);
- spacer.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH,
- String.format(VALUE_N_DP, columnWidthDp));
- }
- columnSpacers.put(start, spacer);
- } else {
- removedViews.add(spacer); // Mark for model removal
- layout.removeChild(spacer.node);
- }
- }
- }
- }
-
- if (isFirstSpacer) {
- // No spacer: create one
- int columnWidthDp = mRulesEngine.pxToDp(width);
- addSpacer(layout, -1, UNDEFINED, columnMap[start], columnWidthDp, DEFAULT_CELL_HEIGHT);
- }
-
- start = end;
- }
- actualColumnCount = newColumn;
-//if (usedColumns.contains(newColumn)) {
-// // TODO: This may be totally wrong for right aligned content!
-// actualColumnCount++;
-//}
-
- // Merge spacers for rows
- start = 0;
- while (start < actualRowCount) {
- // Find next unused span
- while (start < actualRowCount && usedRows.contains(start)) {
- start++;
- }
- if (start == actualRowCount) {
- break;
- }
- assert !usedRows.contains(start);
- // Find the next span of unused rows and produce a SINGLE
- // spacer for that range (unless it's a zero-sized rows)
- int end = start + 1;
- for (; end < actualRowCount; end++) {
- if (usedRows.contains(end)) {
- break;
- }
- }
-
- // Add up row sizes
- int height = getRowHeight(start, end - start);
-
- // Find all spacers: the first one found should be moved to the start row
- // and assigned to the full height of the rows, and
- // the row count reduced by the corresponding amount
-
- // TODO: if width = 0, fully remove
-
- boolean isFirstSpacer = true;
- for (int row = start; row < end; row++) {
- Collection<ViewData> spacers = rowSpacers.get(row);
- if (spacers != null && !spacers.isEmpty()) {
- // Avoid ConcurrentModificationException since we're inserting into the
- // map within this loop (always at a different index, but the map doesn't
- // know that)
- spacers = new ArrayList<ViewData>(spacers);
- for (ViewData spacer : spacers) {
- if (isFirstSpacer) {
- isFirstSpacer = false;
- spacer.row = rowMap[start];
- setGridAttribute(spacer.node, ATTR_LAYOUT_ROW, spacer.row);
- if (end - start > 1) {
- // Compute a merged width for all the spacers (not needed if
- // there's just one spacer; it should already have the correct height)
- int rowHeightDp = mRulesEngine.pxToDp(height);
- spacer.node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT,
- String.format(VALUE_N_DP, rowHeightDp));
- }
- rowSpacers.put(start, spacer);
- } else {
- removedViews.add(spacer); // Mark for model removal
- layout.removeChild(spacer.node);
- }
- }
- }
- }
-
- if (isFirstSpacer) {
- // No spacer: create one
- int rowWidthDp = mRulesEngine.pxToDp(height);
- addSpacer(layout, -1, rowMap[start], UNDEFINED, DEFAULT_CELL_WIDTH, rowWidthDp);
- }
-
- start = end;
- }
- actualRowCount = newRow;
-// if (usedRows.contains(newRow)) {
-// actualRowCount++;
-// }
-
- // Update the model: remove removed children from the view data list
- if (removedViews.size() <= 2) {
- mChildViews.removeAll(removedViews);
- } else {
- List<ViewData> remaining =
- new ArrayList<ViewData>(mChildViews.size() - removedViews.size());
- for (ViewData view : mChildViews) {
- if (!removedViews.contains(view)) {
- remaining.add(view);
- }
- }
- mChildViews = remaining;
- }
-
- // Update the final column and row declared attributes
- if (declaredColumnCount != UNDEFINED) {
- declaredColumnCount = actualColumnCount;
- setGridAttribute(layout, ATTR_COLUMN_COUNT, actualColumnCount);
- }
- if (declaredRowCount != UNDEFINED) {
- declaredRowCount = actualRowCount;
- setGridAttribute(layout, ATTR_ROW_COUNT, actualRowCount);
- }
- }
-
- /**
- * Adds a spacer to the given parent, at the given index.
- *
- * @param parent the GridLayout
- * @param index the index to insert the spacer at, or -1 to append
- * @param row the row to add the spacer to (or {@link #UNDEFINED} to not set a row yet
- * @param column the column to add the spacer to (or {@link #UNDEFINED} to not set a
- * column yet
- * @param widthDp the width in device independent pixels to assign to the spacer
- * @param heightDp the height in device independent pixels to assign to the spacer
- * @return the newly added spacer
- */
- ViewData 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(tag, index);
- } else {
- spacer = parent.appendChild(tag);
- }
-
- ViewData view = new ViewData(spacer, index != -1 ? index : mChildViews.size());
- mChildViews.add(view);
-
- if (row != UNDEFINED) {
- view.row = row;
- setGridAttribute(spacer, ATTR_LAYOUT_ROW, row);
- }
- if (column != UNDEFINED) {
- view.column = column;
- setGridAttribute(spacer, ATTR_LAYOUT_COLUMN, column);
- }
- if (widthDp > 0) {
- spacer.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH,
- String.format(VALUE_N_DP, widthDp));
- }
- if (heightDp > 0) {
- spacer.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT,
- String.format(VALUE_N_DP, heightDp));
- }
-
- // Temporary hack
- if (GridLayoutRule.sDebugGridLayout) {
- //String id = NEW_ID_PREFIX + "s";
- //if (row == 0) {
- // id += "c";
- //}
- //if (column == 0) {
- // id += "r";
- //}
- //if (row > 0) {
- // id += Integer.toString(row);
- //}
- //if (column > 0) {
- // id += Integer.toString(column);
- //}
- String id = NEW_ID_PREFIX + "spacer_" //$NON-NLS-1$
- + Integer.toString(System.identityHashCode(spacer)).substring(0, 3);
- spacer.setAttribute(ANDROID_URI, ATTR_ID, id);
- }
-
-
- return view;
- }
-
- /**
- * 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 name the attribute name (which must be in the android: namespace)
- * @return the attribute value or null
- */
-
- public String getGridAttribute(INode node, String name) {
- return node.getStringAttr(getNamespace(), name);
- }
-
- /**
- * 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 integer value
- */
- private int getGridAttribute(INode node, String attribute, int defaultValue) {
- String valueString = node.getStringAttr(getNamespace(), attribute);
- if (valueString != null) {
- try {
- return Integer.decode(valueString);
- } catch (NumberFormatException nufe) {
- // Ignore - error in user's XML
- }
- }
-
- return defaultValue;
- }
-
- /**
- * Returns the number of children views in the GridLayout
- *
- * @return the number of children views in the GridLayout
- */
- public int getViewCount() {
- return mChildViews.size();
- }
-
- /**
- * Returns true if the given class name represents a spacer
- *
- * @param fqcn the fully qualified class name
- * @return true if this is a spacer
- */
- public static boolean isSpace(String fqcn) {
- return FQCN_SPACE.equals(fqcn) || FQCN_SPACE_V7.equals(fqcn);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/gridmode.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/gridmode.png
deleted file mode 100644
index 59e0a4511..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/gridmode.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/hlinear.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/hlinear.png
deleted file mode 100644
index b293fe7c5..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/hlinear.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/margins.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/margins.png
deleted file mode 100644
index b0d814116..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/margins.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ConstraintPainter.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ConstraintPainter.java
deleted file mode 100644
index 447d2d880..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ConstraintPainter.java
+++ /dev/null
@@ -1,783 +0,0 @@
-/*
- * Copyright (C) 2011 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.relative;
-
-import static com.android.ide.common.api.DrawingStyle.DEPENDENCY;
-import static com.android.ide.common.api.DrawingStyle.GUIDELINE;
-import static com.android.ide.common.api.DrawingStyle.GUIDELINE_DASHED;
-import static com.android.ide.common.api.SegmentType.BASELINE;
-import static com.android.ide.common.api.SegmentType.BOTTOM;
-import static com.android.ide.common.api.SegmentType.CENTER_HORIZONTAL;
-import static com.android.ide.common.api.SegmentType.CENTER_VERTICAL;
-import static com.android.ide.common.api.SegmentType.LEFT;
-import static com.android.ide.common.api.SegmentType.RIGHT;
-import static com.android.ide.common.api.SegmentType.TOP;
-import static com.android.ide.common.api.SegmentType.UNKNOWN;
-import static com.android.ide.common.layout.relative.ConstraintType.ALIGN_BASELINE;
-import static com.android.ide.common.layout.relative.ConstraintType.ALIGN_BOTTOM;
-import static com.android.ide.common.layout.relative.ConstraintType.LAYOUT_ABOVE;
-import static com.android.ide.common.layout.relative.ConstraintType.LAYOUT_BELOW;
-import static com.android.ide.common.layout.relative.ConstraintType.LAYOUT_LEFT_OF;
-import static com.android.ide.common.layout.relative.ConstraintType.LAYOUT_RIGHT_OF;
-
-import com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.Margins;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.SegmentType;
-import com.android.ide.common.layout.relative.DependencyGraph.Constraint;
-import com.android.ide.common.layout.relative.DependencyGraph.ViewData;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * The {@link ConstraintPainter} is responsible for painting relative layout constraints -
- * such as a source node having its top edge constrained to a target node with a given margin.
- * This painter is used both to show static constraints, as well as visualizing proposed
- * constraints during a move or resize operation.
- */
-public class ConstraintPainter {
- /** The size of the arrow head */
- private static final int ARROW_SIZE = 5;
- /** Size (height for horizontal, and width for vertical) parent feedback rectangles */
- private static final int PARENT_RECT_SIZE = 12;
-
- /**
- * Paints a given match as a constraint.
- *
- * @param graphics the graphics context
- * @param sourceBounds the source bounds
- * @param match the match
- */
- static void paintConstraint(IGraphics graphics, Rect sourceBounds, Match match) {
- Rect targetBounds = match.edge.node.getBounds();
- ConstraintType type = match.type;
- assert type != null;
- paintConstraint(graphics, type, match.with.node, sourceBounds, match.edge.node,
- targetBounds, null /* allConstraints */, true /* highlightTargetEdge */);
- }
-
- /**
- * Paints a constraint.
- * <p>
- * TODO: when there are multiple links originating in the same direction from
- * center, maybe offset them slightly from each other?
- *
- * @param graphics the graphics context to draw into
- * @param constraint The constraint to be drawn
- */
- private static void paintConstraint(IGraphics graphics, Constraint constraint,
- Set<Constraint> allConstraints) {
- ViewData source = constraint.from;
- ViewData target = constraint.to;
-
- INode sourceNode = source.node;
- INode targetNode = target.node;
- if (sourceNode == targetNode) {
- // Self reference - don't visualize
- return;
- }
-
- Rect sourceBounds = sourceNode.getBounds();
- Rect targetBounds = targetNode.getBounds();
- paintConstraint(graphics, constraint.type, sourceNode, sourceBounds, targetNode,
- targetBounds, allConstraints, false /* highlightTargetEdge */);
- }
-
- /**
- * Paint selection feedback by painting constraints for the selected nodes
- *
- * @param graphics the graphics context
- * @param parentNode the parent relative layout
- * @param childNodes the nodes whose constraints should be painted
- * @param showDependents whether incoming constraints should be shown as well
- */
- public static void paintSelectionFeedback(IGraphics graphics, INode parentNode,
- List<? extends INode> childNodes, boolean showDependents) {
-
- DependencyGraph dependencyGraph = new DependencyGraph(parentNode);
- Set<INode> horizontalDeps = dependencyGraph.dependsOn(childNodes, false /* vertical */);
- Set<INode> verticalDeps = dependencyGraph.dependsOn(childNodes, true /* vertical */);
- Set<INode> deps = new HashSet<INode>(horizontalDeps.size() + verticalDeps.size());
- deps.addAll(horizontalDeps);
- deps.addAll(verticalDeps);
- if (deps.size() > 0) {
- graphics.useStyle(DEPENDENCY);
- for (INode node : deps) {
- // Don't highlight the selected nodes themselves
- if (childNodes.contains(node)) {
- continue;
- }
- Rect bounds = node.getBounds();
- graphics.fillRect(bounds);
- }
- }
-
- graphics.useStyle(GUIDELINE);
- for (INode childNode : childNodes) {
- ViewData view = dependencyGraph.getView(childNode);
- if (view == null) {
- continue;
- }
-
- // Paint all incoming constraints
- if (showDependents) {
- paintConstraints(graphics, view.dependedOnBy);
- }
-
- // Paint all outgoing constraints
- paintConstraints(graphics, view.dependsOn);
- }
- }
-
- /**
- * Paints a set of constraints.
- */
- private static void paintConstraints(IGraphics graphics, List<Constraint> constraints) {
- Set<Constraint> mutableConstraintSet = new HashSet<Constraint>(constraints);
-
- // WORKAROUND! Hide alignBottom attachments if we also have a alignBaseline
- // constraint; this is because we also *add* alignBottom attachments when you add
- // alignBaseline constraints to work around a surprising behavior of baseline
- // constraints.
- for (Constraint constraint : constraints) {
- if (constraint.type == ALIGN_BASELINE) {
- // Remove any baseline
- for (Constraint c : constraints) {
- if (c.type == ALIGN_BOTTOM && c.to.node == constraint.to.node) {
- mutableConstraintSet.remove(c);
- }
- }
- }
- }
-
- for (Constraint constraint : constraints) {
- // paintConstraint can digest more than one constraint, so we need to keep
- // checking to see if the given constraint is still relevant.
- if (mutableConstraintSet.contains(constraint)) {
- paintConstraint(graphics, constraint, mutableConstraintSet);
- }
- }
- }
-
- /**
- * Paints a constraint of the given type from the given source node, to the
- * given target node, with the specified bounds.
- */
- private static void paintConstraint(IGraphics graphics, ConstraintType type, INode sourceNode,
- Rect sourceBounds, INode targetNode, Rect targetBounds,
- Set<Constraint> allConstraints, boolean highlightTargetEdge) {
-
- SegmentType sourceSegmentTypeX = type.sourceSegmentTypeX;
- SegmentType sourceSegmentTypeY = type.sourceSegmentTypeY;
- SegmentType targetSegmentTypeX = type.targetSegmentTypeX;
- SegmentType targetSegmentTypeY = type.targetSegmentTypeY;
-
- // Horizontal center constraint?
- if (sourceSegmentTypeX == CENTER_VERTICAL && targetSegmentTypeX == CENTER_VERTICAL) {
- paintHorizontalCenterConstraint(graphics, sourceBounds, targetBounds);
- return;
- }
-
- // Vertical center constraint?
- if (sourceSegmentTypeY == CENTER_HORIZONTAL && targetSegmentTypeY == CENTER_HORIZONTAL) {
- paintVerticalCenterConstraint(graphics, sourceBounds, targetBounds);
- return;
- }
-
- // Corner constraint?
- if (allConstraints != null
- && (type == LAYOUT_ABOVE || type == LAYOUT_BELOW
- || type == LAYOUT_LEFT_OF || type == LAYOUT_RIGHT_OF)) {
- if (paintCornerConstraint(graphics, type, sourceNode, sourceBounds, targetNode,
- targetBounds, allConstraints)) {
- return;
- }
- }
-
- // Vertical constraint?
- if (sourceSegmentTypeX == UNKNOWN) {
- paintVerticalConstraint(graphics, type, sourceNode, sourceBounds, targetNode,
- targetBounds, highlightTargetEdge);
- return;
- }
-
- // Horizontal constraint?
- if (sourceSegmentTypeY == UNKNOWN) {
- paintHorizontalConstraint(graphics, type, sourceNode, sourceBounds, targetNode,
- targetBounds, highlightTargetEdge);
- return;
- }
-
- // This shouldn't happen - it means we have a constraint that defines all sides
- // and is not a centering constraint
- assert false;
- }
-
- /**
- * Paints a corner constraint, or returns false if this constraint is not a corner.
- * A corner is one where there are two constraints from this source node to the
- * same target node, one horizontal and one vertical, to the closest edges on
- * the target node.
- * <p>
- * Corners are a common occurrence. If we treat the horizontal and vertical
- * constraints separately (below & toRightOf), then we end up with a lot of
- * extra lines and arrows -- e.g. two shared edges and arrows pointing to these
- * shared edges:
- *
- * <pre>
- * +--------+ |
- * | Target -->
- * +----|---+ |
- * v
- * - - - - - -|- - - - - -
- * ^
- * | +---|----+
- * <-- Source |
- * | +--------+
- *
- * Instead, we can simply draw a diagonal arrow here to represent BOTH constraints and
- * reduce clutter:
- *
- * +---------+
- * | Target _|
- * +-------|\+
- * \
- * \--------+
- * | Source |
- * +--------+
- * </pre>
- *
- * @param graphics the graphics context to draw
- * @param type the constraint to be drawn
- * @param sourceNode the source node
- * @param sourceBounds the bounds of the source node
- * @param targetNode the target node
- * @param targetBounds the bounds of the target node
- * @param allConstraints the set of all constraints; if a corner is found and painted the
- * matching corner constraint is removed from the set
- * @return true if the constraint was handled and painted as a corner, false otherwise
- */
- private static boolean paintCornerConstraint(IGraphics graphics, ConstraintType type,
- INode sourceNode, Rect sourceBounds, INode targetNode, Rect targetBounds,
- Set<Constraint> allConstraints) {
-
- SegmentType sourceSegmentTypeX = type.sourceSegmentTypeX;
- SegmentType sourceSegmentTypeY = type.sourceSegmentTypeY;
- SegmentType targetSegmentTypeX = type.targetSegmentTypeX;
- SegmentType targetSegmentTypeY = type.targetSegmentTypeY;
-
- ConstraintType opposite1 = null, opposite2 = null;
- switch (type) {
- case LAYOUT_BELOW:
- case LAYOUT_ABOVE:
- opposite1 = LAYOUT_LEFT_OF;
- opposite2 = LAYOUT_RIGHT_OF;
- break;
- case LAYOUT_LEFT_OF:
- case LAYOUT_RIGHT_OF:
- opposite1 = LAYOUT_ABOVE;
- opposite2 = LAYOUT_BELOW;
- break;
- default:
- return false;
- }
- Constraint pair = null;
- for (Constraint constraint : allConstraints) {
- if ((constraint.type == opposite1 || constraint.type == opposite2) &&
- constraint.to.node == targetNode && constraint.from.node == sourceNode) {
- pair = constraint;
- break;
- }
- }
-
- // TODO -- ensure that the nodes are adjacent! In other words, that
- // their bounds are within N pixels.
-
- if (pair != null) {
- // Visualize the corner constraint
- if (sourceSegmentTypeX == UNKNOWN) {
- sourceSegmentTypeX = pair.type.sourceSegmentTypeX;
- }
- if (sourceSegmentTypeY == UNKNOWN) {
- sourceSegmentTypeY = pair.type.sourceSegmentTypeY;
- }
- if (targetSegmentTypeX == UNKNOWN) {
- targetSegmentTypeX = pair.type.targetSegmentTypeX;
- }
- if (targetSegmentTypeY == UNKNOWN) {
- targetSegmentTypeY = pair.type.targetSegmentTypeY;
- }
-
- int x1, y1, x2, y2;
- if (sourceSegmentTypeX == LEFT) {
- x1 = sourceBounds.x + 1 * sourceBounds.w / 4;
- } else {
- x1 = sourceBounds.x + 3 * sourceBounds.w / 4;
- }
- if (sourceSegmentTypeY == TOP) {
- y1 = sourceBounds.y + 1 * sourceBounds.h / 4;
- } else {
- y1 = sourceBounds.y + 3 * sourceBounds.h / 4;
- }
- if (targetSegmentTypeX == LEFT) {
- x2 = targetBounds.x + 1 * targetBounds.w / 4;
- } else {
- x2 = targetBounds.x + 3 * targetBounds.w / 4;
- }
- if (targetSegmentTypeY == TOP) {
- y2 = targetBounds.y + 1 * targetBounds.h / 4;
- } else {
- y2 = targetBounds.y + 3 * targetBounds.h / 4;
- }
-
- graphics.useStyle(GUIDELINE);
- graphics.drawArrow(x1, y1, x2, y2, ARROW_SIZE);
-
- // Don't process this constraint on its own later.
- allConstraints.remove(pair);
-
- return true;
- }
-
- return false;
- }
-
- /**
- * Paints a vertical constraint, handling the various scenarios where there are
- * margins, or where the two nodes overlap horizontally and where they don't, etc.
- * <p>
- * Here's an example of what will be shown for a "below" constraint where the
- * nodes do not overlap horizontally and the target node has a bottom margin:
- * <pre>
- * +--------+
- * | Target |
- * +--------+
- * |
- * v
- * - - - - - - - - - - - - - -
- * ^
- * |
- * +--------+
- * | Source |
- * +--------+
- * </pre>
- */
- private static void paintVerticalConstraint(IGraphics graphics, ConstraintType type,
- INode sourceNode, Rect sourceBounds, INode targetNode, Rect targetBounds,
- boolean highlightTargetEdge) {
- SegmentType sourceSegmentTypeY = type.sourceSegmentTypeY;
- SegmentType targetSegmentTypeY = type.targetSegmentTypeY;
- Margins targetMargins = targetNode.getMargins();
-
- assert sourceSegmentTypeY != UNKNOWN;
- assert targetBounds != null;
-
- int sourceY = sourceSegmentTypeY.getY(sourceNode, sourceBounds);
- int targetY = targetSegmentTypeY ==
- UNKNOWN ? sourceY : targetSegmentTypeY.getY(targetNode, targetBounds);
-
- if (highlightTargetEdge && type.isRelativeToParentEdge()) {
- graphics.useStyle(DrawingStyle.DROP_ZONE_ACTIVE);
- graphics.fillRect(targetBounds.x, targetY - PARENT_RECT_SIZE / 2,
- targetBounds.x2(), targetY + PARENT_RECT_SIZE / 2);
- }
-
- // First see if the two views overlap horizontally. If so, we can just draw a direct
- // arrow from the source up to (or down to) the target.
- //
- // +--------+
- // | Target |
- // +--------+
- // ^
- // |
- // |
- // +--------+
- // | Source |
- // +--------+
- //
- int maxLeft = Math.max(sourceBounds.x, targetBounds.x);
- int minRight = Math.min(sourceBounds.x2(), targetBounds.x2());
-
- int center = (maxLeft + minRight) / 2;
- if (center > sourceBounds.x && center < sourceBounds.x2()) {
- // Yes, the lines overlap -- just draw a straight arrow
- //
- //
- // If however there is a margin on the target edge, it should be drawn like this:
- //
- // +--------+
- // | Target |
- // +--------+
- // |
- // |
- // v
- // - - - - - - -
- // ^
- // |
- // |
- // +--------+
- // | Source |
- // +--------+
- //
- // Use a minimum threshold for this visualization since it doesn't look good
- // for small margins
- if (targetSegmentTypeY == BOTTOM && targetMargins.bottom > 5) {
- int sharedY = targetY + targetMargins.bottom;
- if (sourceY > sharedY + 2) { // Skip when source falls on the margin line
- graphics.useStyle(GUIDELINE_DASHED);
- graphics.drawLine(targetBounds.x, sharedY, targetBounds.x2(), sharedY);
- graphics.useStyle(GUIDELINE);
- graphics.drawArrow(center, sourceY, center, sharedY + 2, ARROW_SIZE);
- graphics.drawArrow(center, targetY, center, sharedY - 3, ARROW_SIZE);
- } else {
- graphics.useStyle(GUIDELINE);
- // Draw reverse arrow to make it clear the node is as close
- // at it can be
- graphics.drawArrow(center, targetY, center, sourceY, ARROW_SIZE);
- }
- return;
- } else if (targetSegmentTypeY == TOP && targetMargins.top > 5) {
- int sharedY = targetY - targetMargins.top;
- if (sourceY < sharedY - 2) {
- graphics.useStyle(GUIDELINE_DASHED);
- graphics.drawLine(targetBounds.x, sharedY, targetBounds.x2(), sharedY);
- graphics.useStyle(GUIDELINE);
- graphics.drawArrow(center, sourceY, center, sharedY - 3, ARROW_SIZE);
- graphics.drawArrow(center, targetY, center, sharedY + 3, ARROW_SIZE);
- } else {
- graphics.useStyle(GUIDELINE);
- graphics.drawArrow(center, targetY, center, sourceY, ARROW_SIZE);
- }
- return;
- }
-
- // TODO: If the center falls smack in the center of the sourceBounds,
- // AND the source node is part of the selection, then adjust the
- // center location such that it is off to the side, let's say 1/4 or 3/4 of
- // the overlap region, to ensure that it does not overlap the center selection
- // handle
-
- // When the constraint is for two immediately adjacent edges, we
- // need to make some adjustments to make sure the arrow points in the right
- // direction
- if (sourceY == targetY) {
- if (sourceSegmentTypeY == BOTTOM || sourceSegmentTypeY == BASELINE) {
- sourceY -= 2 * ARROW_SIZE;
- } else if (sourceSegmentTypeY == TOP) {
- sourceY += 2 * ARROW_SIZE;
- } else {
- assert sourceSegmentTypeY == CENTER_HORIZONTAL : sourceSegmentTypeY;
- sourceY += sourceBounds.h / 2 - 2 * ARROW_SIZE;
- }
- } else if (sourceSegmentTypeY == BASELINE) {
- sourceY = targetY - 2 * ARROW_SIZE;
- }
-
- // Center the vertical line in the overlap region
- graphics.useStyle(GUIDELINE);
- graphics.drawArrow(center, sourceY, center, targetY, ARROW_SIZE);
-
- return;
- }
-
- // If there is no horizontal overlap in the vertical constraints, then we
- // will show the attachment relative to a dashed line that extends beyond
- // the target bounds, like this:
- //
- // +--------+
- // | Target |
- // +--------+ - - - - - - - - -
- // ^
- // |
- // +--------+
- // | Source |
- // +--------+
- //
- // However, if the target node has a vertical margin, we may need to offset
- // the line:
- //
- // +--------+
- // | Target |
- // +--------+
- // |
- // v
- // - - - - - - - - - - - - - -
- // ^
- // |
- // +--------+
- // | Source |
- // +--------+
- //
- // If not, we'll need to indicate a shared edge. This is the edge that separate
- // them (but this will require me to evaluate margins!)
-
- // Compute overlap region and pick the middle
- int sharedY = targetSegmentTypeY ==
- UNKNOWN ? sourceY : targetSegmentTypeY.getY(targetNode, targetBounds);
- if (type.relativeToMargin) {
- if (targetSegmentTypeY == TOP) {
- sharedY -= targetMargins.top;
- } else if (targetSegmentTypeY == BOTTOM) {
- sharedY += targetMargins.bottom;
- }
- }
-
- int startX;
- int endX;
- if (center <= sourceBounds.x) {
- startX = targetBounds.x + targetBounds.w / 4;
- endX = sourceBounds.x2();
- } else {
- assert (center >= sourceBounds.x2());
- startX = sourceBounds.x;
- endX = targetBounds.x + 3 * targetBounds.w / 4;
- }
- // Must draw segmented line instead
- // Place the arrow 1/4 instead of 1/2 in the source to avoid overlapping with the
- // selection handles
- graphics.useStyle(GUIDELINE_DASHED);
- graphics.drawLine(startX, sharedY, endX, sharedY);
-
- // Adjust position of source arrow such that it does not sit across edge; it
- // should point directly at the edge
- if (Math.abs(sharedY - sourceY) < 2 * ARROW_SIZE) {
- if (sourceSegmentTypeY == BASELINE) {
- sourceY = sharedY - 2 * ARROW_SIZE;
- } else if (sourceSegmentTypeY == TOP) {
- sharedY = sourceY;
- sourceY = sharedY + 2 * ARROW_SIZE;
- } else {
- sharedY = sourceY;
- sourceY = sharedY - 2 * ARROW_SIZE;
- }
- }
-
- graphics.useStyle(GUIDELINE);
-
- // Draw the line from the source anchor to the shared edge
- int x = sourceBounds.x + ((sourceSegmentTypeY == BASELINE) ?
- sourceBounds.w / 2 : sourceBounds.w / 4);
- graphics.drawArrow(x, sourceY, x, sharedY, ARROW_SIZE);
-
- // Draw the line from the target to the horizontal shared edge
- int tx = targetBounds.centerX();
- if (targetSegmentTypeY == TOP) {
- int ty = targetBounds.y;
- int margin = targetMargins.top;
- if (margin == 0 || !type.relativeToMargin) {
- graphics.drawArrow(tx, ty + 2 * ARROW_SIZE, tx, ty, ARROW_SIZE);
- } else {
- graphics.drawArrow(tx, ty, tx, ty - margin, ARROW_SIZE);
- }
- } else if (targetSegmentTypeY == BOTTOM) {
- int ty = targetBounds.y2();
- int margin = targetMargins.bottom;
- if (margin == 0 || !type.relativeToMargin) {
- graphics.drawArrow(tx, ty - 2 * ARROW_SIZE, tx, ty, ARROW_SIZE);
- } else {
- graphics.drawArrow(tx, ty, tx, ty + margin, ARROW_SIZE);
- }
- } else {
- assert targetSegmentTypeY == BASELINE : targetSegmentTypeY;
- int ty = targetSegmentTypeY.getY(targetNode, targetBounds);
- graphics.drawArrow(tx, ty - 2 * ARROW_SIZE, tx, ty, ARROW_SIZE);
- }
-
- return;
- }
-
- /**
- * Paints a horizontal constraint, handling the various scenarios where there are margins,
- * or where the two nodes overlap horizontally and where they don't, etc.
- */
- private static void paintHorizontalConstraint(IGraphics graphics, ConstraintType type,
- INode sourceNode, Rect sourceBounds, INode targetNode, Rect targetBounds,
- boolean highlightTargetEdge) {
- SegmentType sourceSegmentTypeX = type.sourceSegmentTypeX;
- SegmentType targetSegmentTypeX = type.targetSegmentTypeX;
- Margins targetMargins = targetNode.getMargins();
-
- assert sourceSegmentTypeX != UNKNOWN;
- assert targetBounds != null;
-
- // See paintVerticalConstraint for explanations of the various cases.
-
- int sourceX = sourceSegmentTypeX.getX(sourceNode, sourceBounds);
- int targetX = targetSegmentTypeX == UNKNOWN ?
- sourceX : targetSegmentTypeX.getX(targetNode, targetBounds);
-
- if (highlightTargetEdge && type.isRelativeToParentEdge()) {
- graphics.useStyle(DrawingStyle.DROP_ZONE_ACTIVE);
- graphics.fillRect(targetX - PARENT_RECT_SIZE / 2, targetBounds.y,
- targetX + PARENT_RECT_SIZE / 2, targetBounds.y2());
- }
-
- int maxTop = Math.max(sourceBounds.y, targetBounds.y);
- int minBottom = Math.min(sourceBounds.y2(), targetBounds.y2());
-
- // First see if the two views overlap vertically. If so, we can just draw a direct
- // arrow from the source over to the target.
- int center = (maxTop + minBottom) / 2;
- if (center > sourceBounds.y && center < sourceBounds.y2()) {
- // See if we should draw a margin line
- if (targetSegmentTypeX == RIGHT && targetMargins.right > 5) {
- int sharedX = targetX + targetMargins.right;
- if (sourceX > sharedX + 2) { // Skip when source falls on the margin line
- graphics.useStyle(GUIDELINE_DASHED);
- graphics.drawLine(sharedX, targetBounds.y, sharedX, targetBounds.y2());
- graphics.useStyle(GUIDELINE);
- graphics.drawArrow(sourceX, center, sharedX + 2, center, ARROW_SIZE);
- graphics.drawArrow(targetX, center, sharedX - 3, center, ARROW_SIZE);
- } else {
- graphics.useStyle(GUIDELINE);
- // Draw reverse arrow to make it clear the node is as close
- // at it can be
- graphics.drawArrow(targetX, center, sourceX, center, ARROW_SIZE);
- }
- return;
- } else if (targetSegmentTypeX == LEFT && targetMargins.left > 5) {
- int sharedX = targetX - targetMargins.left;
- if (sourceX < sharedX - 2) {
- graphics.useStyle(GUIDELINE_DASHED);
- graphics.drawLine(sharedX, targetBounds.y, sharedX, targetBounds.y2());
- graphics.useStyle(GUIDELINE);
- graphics.drawArrow(sourceX, center, sharedX - 3, center, ARROW_SIZE);
- graphics.drawArrow(targetX, center, sharedX + 3, center, ARROW_SIZE);
- } else {
- graphics.useStyle(GUIDELINE);
- graphics.drawArrow(targetX, center, sourceX, center, ARROW_SIZE);
- }
- return;
- }
-
- if (sourceX == targetX) {
- if (sourceSegmentTypeX == RIGHT) {
- sourceX -= 2 * ARROW_SIZE;
- } else if (sourceSegmentTypeX == LEFT ) {
- sourceX += 2 * ARROW_SIZE;
- } else {
- assert sourceSegmentTypeX == CENTER_VERTICAL : sourceSegmentTypeX;
- sourceX += sourceBounds.w / 2 - 2 * ARROW_SIZE;
- }
- }
-
- graphics.useStyle(GUIDELINE);
- graphics.drawArrow(sourceX, center, targetX, center, ARROW_SIZE);
- return;
- }
-
- // Segment line
-
- // Compute overlap region and pick the middle
- int sharedX = targetSegmentTypeX == UNKNOWN ?
- sourceX : targetSegmentTypeX.getX(targetNode, targetBounds);
- if (type.relativeToMargin) {
- if (targetSegmentTypeX == LEFT) {
- sharedX -= targetMargins.left;
- } else if (targetSegmentTypeX == RIGHT) {
- sharedX += targetMargins.right;
- }
- }
-
- int startY, endY;
- if (center <= sourceBounds.y) {
- startY = targetBounds.y + targetBounds.h / 4;
- endY = sourceBounds.y2();
- } else {
- assert (center >= sourceBounds.y2());
- startY = sourceBounds.y;
- endY = targetBounds.y + 3 * targetBounds.h / 2;
- }
-
- // Must draw segmented line instead
- // Place at 1/4 instead of 1/2 to avoid overlapping with selection handles
- int y = sourceBounds.y + sourceBounds.h / 4;
- graphics.useStyle(GUIDELINE_DASHED);
- graphics.drawLine(sharedX, startY, sharedX, endY);
-
- // Adjust position of source arrow such that it does not sit across edge; it
- // should point directly at the edge
- if (Math.abs(sharedX - sourceX) < 2 * ARROW_SIZE) {
- if (sourceSegmentTypeX == LEFT) {
- sharedX = sourceX;
- sourceX = sharedX + 2 * ARROW_SIZE;
- } else {
- sharedX = sourceX;
- sourceX = sharedX - 2 * ARROW_SIZE;
- }
- }
-
- graphics.useStyle(GUIDELINE);
-
- // Draw the line from the source anchor to the shared edge
- graphics.drawArrow(sourceX, y, sharedX, y, ARROW_SIZE);
-
- // Draw the line from the target to the horizontal shared edge
- int ty = targetBounds.centerY();
- if (targetSegmentTypeX == LEFT) {
- int tx = targetBounds.x;
- int margin = targetMargins.left;
- if (margin == 0 || !type.relativeToMargin) {
- graphics.drawArrow(tx + 2 * ARROW_SIZE, ty, tx, ty, ARROW_SIZE);
- } else {
- graphics.drawArrow(tx, ty, tx - margin, ty, ARROW_SIZE);
- }
- } else {
- assert targetSegmentTypeX == RIGHT;
- int tx = targetBounds.x2();
- int margin = targetMargins.right;
- if (margin == 0 || !type.relativeToMargin) {
- graphics.drawArrow(tx - 2 * ARROW_SIZE, ty, tx, ty, ARROW_SIZE);
- } else {
- graphics.drawArrow(tx, ty, tx + margin, ty, ARROW_SIZE);
- }
- }
-
- return;
- }
-
- /**
- * Paints a vertical center constraint. The constraint is shown as a dashed line
- * through the vertical view, and a solid line over the node bounds.
- */
- private static void paintVerticalCenterConstraint(IGraphics graphics, Rect sourceBounds,
- Rect targetBounds) {
- graphics.useStyle(GUIDELINE_DASHED);
- graphics.drawLine(targetBounds.x, targetBounds.centerY(),
- targetBounds.x2(), targetBounds.centerY());
- graphics.useStyle(GUIDELINE);
- graphics.drawLine(sourceBounds.x, sourceBounds.centerY(),
- sourceBounds.x2(), sourceBounds.centerY());
- }
-
- /**
- * Paints a horizontal center constraint. The constraint is shown as a dashed line
- * through the horizontal view, and a solid line over the node bounds.
- */
- private static void paintHorizontalCenterConstraint(IGraphics graphics, Rect sourceBounds,
- Rect targetBounds) {
- graphics.useStyle(GUIDELINE_DASHED);
- graphics.drawLine(targetBounds.centerX(), targetBounds.y,
- targetBounds.centerX(), targetBounds.y2());
- graphics.useStyle(GUIDELINE);
- graphics.drawLine(sourceBounds.centerX(), sourceBounds.y,
- sourceBounds.centerX(), sourceBounds.y2());
- }
-} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ConstraintType.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ConstraintType.java
deleted file mode 100644
index ed4ac1bf4..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ConstraintType.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2011 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.relative;
-
-import static com.android.ide.common.api.SegmentType.BASELINE;
-import static com.android.ide.common.api.SegmentType.BOTTOM;
-import static com.android.ide.common.api.SegmentType.CENTER_HORIZONTAL;
-import static com.android.ide.common.api.SegmentType.CENTER_VERTICAL;
-import static com.android.ide.common.api.SegmentType.LEFT;
-import static com.android.ide.common.api.SegmentType.RIGHT;
-import static com.android.ide.common.api.SegmentType.TOP;
-import static com.android.ide.common.api.SegmentType.UNKNOWN;
-import static com.android.SdkConstants.ATTR_LAYOUT_ABOVE;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BASELINE;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BOTTOM;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_LEFT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_BOTTOM;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_LEFT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_RIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_TOP;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_RIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_TOP;
-import static com.android.SdkConstants.ATTR_LAYOUT_BELOW;
-import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_HORIZONTAL;
-import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_IN_PARENT;
-import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_VERTICAL;
-import static com.android.SdkConstants.ATTR_LAYOUT_TO_LEFT_OF;
-import static com.android.SdkConstants.ATTR_LAYOUT_TO_RIGHT_OF;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.SegmentType;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Each constraint type corresponds to a type of constraint available for the
- * RelativeLayout; for example, {@link #LAYOUT_ABOVE} corresponds to the layout_above constraint.
- */
-enum ConstraintType {
- LAYOUT_ABOVE(ATTR_LAYOUT_ABOVE,
- null /* sourceX */, BOTTOM, null /* targetX */, TOP,
- false /* targetParent */, true /* horizontalEdge */, false /* verticalEdge */,
- true /* relativeToMargin */),
-
- LAYOUT_BELOW(ATTR_LAYOUT_BELOW, null, TOP, null, BOTTOM, false, true, false, true),
- ALIGN_TOP(ATTR_LAYOUT_ALIGN_TOP, null, TOP, null, TOP, false, true, false, false),
- ALIGN_BOTTOM(ATTR_LAYOUT_ALIGN_BOTTOM, null, BOTTOM, null, BOTTOM, false, true, false, false),
- ALIGN_LEFT(ATTR_LAYOUT_ALIGN_LEFT, LEFT, null, LEFT, null, false, false, true, false),
- ALIGN_RIGHT(ATTR_LAYOUT_ALIGN_RIGHT, RIGHT, null, RIGHT, null, false, false, true, false),
- LAYOUT_LEFT_OF(ATTR_LAYOUT_TO_LEFT_OF, RIGHT, null, LEFT, null, false, false, true, true),
- LAYOUT_RIGHT_OF(ATTR_LAYOUT_TO_RIGHT_OF, LEFT, null, RIGHT, null, false, false, true, true),
- ALIGN_PARENT_TOP(ATTR_LAYOUT_ALIGN_PARENT_TOP, null, TOP, null, TOP, true, true, false, false),
- ALIGN_BASELINE(ATTR_LAYOUT_ALIGN_BASELINE, null, BASELINE, null, BASELINE, false, true, false,
- false),
- ALIGN_PARENT_LEFT(ATTR_LAYOUT_ALIGN_PARENT_LEFT, LEFT, null, LEFT, null, true, false, true,
- false),
- ALIGN_PARENT_RIGHT(ATTR_LAYOUT_ALIGN_PARENT_RIGHT, RIGHT, null, RIGHT, null, true, false, true,
- false),
- ALIGN_PARENT_BOTTOM(ATTR_LAYOUT_ALIGN_PARENT_BOTTOM, null, BOTTOM, null, BOTTOM, true, true,
- false, false),
- LAYOUT_CENTER_HORIZONTAL(ATTR_LAYOUT_CENTER_HORIZONTAL, CENTER_VERTICAL, null, CENTER_VERTICAL,
- null, true, true, false, false),
- LAYOUT_CENTER_VERTICAL(ATTR_LAYOUT_CENTER_VERTICAL, null, CENTER_HORIZONTAL, null,
- CENTER_HORIZONTAL, true, false, true, false),
- LAYOUT_CENTER_IN_PARENT(ATTR_LAYOUT_CENTER_IN_PARENT, CENTER_VERTICAL, CENTER_HORIZONTAL,
- CENTER_VERTICAL, CENTER_HORIZONTAL, true, true, true, false);
-
- private ConstraintType(String name, SegmentType sourceSegmentTypeX,
- SegmentType sourceSegmentTypeY, SegmentType targetSegmentTypeX,
- SegmentType targetSegmentTypeY, boolean targetParent, boolean horizontalEdge,
- boolean verticalEdge, boolean relativeToMargin) {
- assert horizontalEdge || verticalEdge;
-
- this.name = name;
- this.sourceSegmentTypeX = sourceSegmentTypeX != null ? sourceSegmentTypeX : UNKNOWN;
- this.sourceSegmentTypeY = sourceSegmentTypeY != null ? sourceSegmentTypeY : UNKNOWN;
- this.targetSegmentTypeX = targetSegmentTypeX != null ? targetSegmentTypeX : UNKNOWN;
- this.targetSegmentTypeY = targetSegmentTypeY != null ? targetSegmentTypeY : UNKNOWN;
- this.targetParent = targetParent;
- this.horizontalEdge = horizontalEdge;
- this.verticalEdge = verticalEdge;
- this.relativeToMargin = relativeToMargin;
- }
-
- /** The attribute name of the constraint */
- public final String name;
-
- /** The horizontal position of the source of the constraint */
- public final SegmentType sourceSegmentTypeX;
-
- /** The vertical position of the source of the constraint */
- public final SegmentType sourceSegmentTypeY;
-
- /** The horizontal position of the target of the constraint */
- public final SegmentType targetSegmentTypeX;
-
- /** The vertical position of the target of the constraint */
- public final SegmentType targetSegmentTypeY;
-
- /**
- * If true, the constraint targets the parent layout, otherwise it targets another
- * view
- */
- public final boolean targetParent;
-
- /** If true, this constraint affects the horizontal dimension */
- public final boolean horizontalEdge;
-
- /** If true, this constraint affects the vertical dimension */
- public final boolean verticalEdge;
-
- /**
- * Whether this constraint is relative to the margin bounds of the node rather than
- * the node's actual bounds
- */
- public final boolean relativeToMargin;
-
- /** Map from attribute name to constraint type */
- private static Map<String, ConstraintType> sNameToType;
-
- /**
- * Returns the {@link ConstraintType} corresponding to the given attribute name, or
- * null if not found.
- *
- * @param attribute the name of the attribute to look up
- * @return the corresponding {@link ConstraintType}
- */
- @Nullable
- public static ConstraintType fromAttribute(@NonNull String attribute) {
- if (sNameToType == null) {
- ConstraintType[] types = ConstraintType.values();
- Map<String, ConstraintType> map = new HashMap<String, ConstraintType>(types.length);
- for (ConstraintType type : types) {
- map.put(type.name, type);
- }
- sNameToType = map;
- }
- return sNameToType.get(attribute);
- }
-
- /**
- * Returns true if this constraint type represents a constraint where the target edge
- * is one of the parent edges (actual edge, not center/baseline segments)
- *
- * @return true if the target segment is a parent edge
- */
- public boolean isRelativeToParentEdge() {
- return this == ALIGN_PARENT_LEFT || this == ALIGN_PARENT_RIGHT || this == ALIGN_PARENT_TOP
- || this == ALIGN_PARENT_BOTTOM;
- }
-
- /**
- * Returns a {@link ConstraintType} for a potential match of edges.
- *
- * @param withParent if true, the target is the parent
- * @param from the source edge
- * @param to the target edge
- * @return a {@link ConstraintType}, or null
- */
- @Nullable
- public static ConstraintType forMatch(boolean withParent, SegmentType from, SegmentType to) {
- // Attached to parent edge?
- if (withParent) {
- switch (from) {
- case TOP:
- return ALIGN_PARENT_TOP;
- case BOTTOM:
- return ALIGN_PARENT_BOTTOM;
- case LEFT:
- return ALIGN_PARENT_LEFT;
- case RIGHT:
- return ALIGN_PARENT_RIGHT;
- case CENTER_HORIZONTAL:
- return LAYOUT_CENTER_VERTICAL;
- case CENTER_VERTICAL:
- return LAYOUT_CENTER_HORIZONTAL;
- }
-
- return null;
- }
-
- // Attached to some other node.
- switch (from) {
- case TOP:
- switch (to) {
- case TOP:
- return ALIGN_TOP;
- case BOTTOM:
- return LAYOUT_BELOW;
- case BASELINE:
- return ALIGN_BASELINE;
- }
- break;
- case BOTTOM:
- switch (to) {
- case TOP:
- return LAYOUT_ABOVE;
- case BOTTOM:
- return ALIGN_BOTTOM;
- case BASELINE:
- return ALIGN_BASELINE;
- }
- break;
- case LEFT:
- switch (to) {
- case LEFT:
- return ALIGN_LEFT;
- case RIGHT:
- return LAYOUT_RIGHT_OF;
- }
- break;
- case RIGHT:
- switch (to) {
- case LEFT:
- return LAYOUT_LEFT_OF;
- case RIGHT:
- return ALIGN_RIGHT;
- }
- break;
- case BASELINE:
- return ALIGN_BASELINE;
- }
-
- return null;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/DeletionHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/DeletionHandler.java
deleted file mode 100644
index 3eac510df..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/DeletionHandler.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2012 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.relative;
-
-import static com.android.SdkConstants.ANDROID_URI;
-import static com.android.SdkConstants.ATTR_ID;
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN;
-import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX;
-import static com.android.SdkConstants.ID_PREFIX;
-import static com.android.SdkConstants.NEW_ID_PREFIX;
-import static com.android.ide.common.layout.BaseViewRule.stripIdPrefix;
-import static com.android.ide.common.layout.relative.ConstraintType.LAYOUT_CENTER_HORIZONTAL;
-import static com.android.ide.common.layout.relative.ConstraintType.LAYOUT_CENTER_VERTICAL;
-
-import com.android.SdkConstants;
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.INode.IAttribute;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Handles deletions in a relative layout, transferring constraints across
- * deleted nodes
- * <p>
- * TODO: Consider adding the
- * {@link SdkConstants#ATTR_LAYOUT_ALIGN_WITH_PARENT_MISSING} attribute to a
- * node if it's pointing to a node which is deleted and which has no transitive
- * reference to another node.
- */
-public class DeletionHandler {
- private final INode mLayout;
- private final INode[] mChildren;
- private final List<INode> mDeleted;
- private final Set<String> mDeletedIds;
- private final Map<String, INode> mNodeMap;
- private final List<INode> mMoved;
-
- /**
- * Creates a new {@link DeletionHandler}
- *
- * @param deleted the deleted nodes
- * @param moved nodes that were moved (e.g. deleted, but also inserted elsewhere)
- * @param layout the parent layout of the deleted nodes
- */
- public DeletionHandler(@NonNull List<INode> deleted, @NonNull List<INode> moved,
- @NonNull INode layout) {
- mDeleted = deleted;
- mMoved = moved;
- mLayout = layout;
-
- mChildren = mLayout.getChildren();
- mNodeMap = Maps.newHashMapWithExpectedSize(mChildren.length);
- for (INode child : mChildren) {
- String id = child.getStringAttr(ANDROID_URI, ATTR_ID);
- if (id != null) {
- mNodeMap.put(stripIdPrefix(id), child);
- }
- }
-
- mDeletedIds = Sets.newHashSetWithExpectedSize(mDeleted.size());
- for (INode node : mDeleted) {
- String id = node.getStringAttr(ANDROID_URI, ATTR_ID);
- if (id != null) {
- mDeletedIds.add(stripIdPrefix(id));
- }
- }
-
- // Any widgets that remain (e.g. typically because they were moved) should
- // keep their incoming dependencies
- for (INode node : mMoved) {
- String id = node.getStringAttr(ANDROID_URI, ATTR_ID);
- if (id != null) {
- mDeletedIds.remove(stripIdPrefix(id));
- }
- }
- }
-
- @Nullable
- private static String getId(@NonNull IAttribute attribute) {
- if (attribute.getName().startsWith(ATTR_LAYOUT_RESOURCE_PREFIX)
- && ANDROID_URI.equals(attribute.getUri())
- && !attribute.getName().startsWith(ATTR_LAYOUT_MARGIN)) {
- String id = attribute.getValue();
- // It might not be an id reference, so check manually rather than just
- // calling stripIdPrefix():
- 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 null;
- }
-
- /**
- * Updates the constraints in the layout to handle deletion of a set of
- * nodes. This ensures that any constraints pointing to one of the deleted
- * nodes are changed properly to point to a non-deleted node with similar
- * constraints.
- */
- public void updateConstraints() {
- if (mChildren.length == mDeleted.size()) {
- // Deleting everything: Nothing to be done
- return;
- }
-
- // Now remove incoming edges to any views that were deleted. If possible,
- // don't just delete them but replace them with a transitive constraint, e.g.
- // if we have "A <= B <= C" and "B" is removed, then we end up with "A <= C",
-
- for (INode child : mChildren) {
- if (mDeleted.contains(child)) {
- continue;
- }
-
- for (IAttribute attribute : child.getLiveAttributes()) {
- String id = getId(attribute);
- if (id != null) {
- if (mDeletedIds.contains(id)) {
- // Unset this reference to a deleted widget. It might be
- // replaced if the pointed to node points to some other node
- // on the same side, but it may use a different constraint name,
- // or have none at all (e.g. parent).
- String name = attribute.getName();
- child.setAttribute(ANDROID_URI, name, null);
-
- INode deleted = mNodeMap.get(id);
- if (deleted != null) {
- ConstraintType type = ConstraintType.fromAttribute(name);
- if (type != null) {
- transfer(deleted, child, type, 0);
- }
- }
- }
- }
- }
- }
- }
-
- private void transfer(INode deleted, INode target, ConstraintType targetType, int depth) {
- if (depth == 20) {
- // Prevent really deep flow or unbounded recursion in case there is a bug in
- // the cycle detection code
- return;
- }
-
- assert mDeleted.contains(deleted);
-
- for (IAttribute attribute : deleted.getLiveAttributes()) {
- String name = attribute.getName();
- ConstraintType type = ConstraintType.fromAttribute(name);
- if (type == null) {
- continue;
- }
-
- ConstraintType transfer = getCompatibleConstraint(type, targetType);
- if (transfer != null) {
- String id = getId(attribute);
- if (id != null) {
- if (mDeletedIds.contains(id)) {
- INode nextDeleted = mNodeMap.get(id);
- if (nextDeleted != null) {
- // Points to another deleted node: recurse
- transfer(nextDeleted, target, targetType, depth + 1);
- }
- } else {
- // Found an undeleted node destination: point to it directly.
- // Note that we're using the
- target.setAttribute(ANDROID_URI, transfer.name, attribute.getValue());
- }
- } else {
- // Pointing to parent or center etc (non-id ref): replicate this on the target
- target.setAttribute(ANDROID_URI, name, attribute.getValue());
- }
- }
- }
- }
-
- /**
- * Determines if two constraints are in the same direction and if so returns
- * the constraint in the same direction. Rather than returning boolean true
- * or false, this returns the constraint which is sometimes modified. For
- * example, if you have a node which points left to a node which is centered
- * in parent, then the constraint is turned into center horizontal.
- */
- @Nullable
- private static ConstraintType getCompatibleConstraint(
- @NonNull ConstraintType first, @NonNull ConstraintType second) {
- if (first == second) {
- return first;
- }
-
- switch (second) {
- case ALIGN_LEFT:
- case LAYOUT_RIGHT_OF:
- switch (first) {
- case LAYOUT_CENTER_HORIZONTAL:
- case LAYOUT_LEFT_OF:
- case ALIGN_LEFT:
- return first;
- case LAYOUT_CENTER_IN_PARENT:
- return LAYOUT_CENTER_HORIZONTAL;
- }
- return null;
-
- case ALIGN_RIGHT:
- case LAYOUT_LEFT_OF:
- switch (first) {
- case LAYOUT_CENTER_HORIZONTAL:
- case ALIGN_RIGHT:
- case LAYOUT_LEFT_OF:
- return first;
- case LAYOUT_CENTER_IN_PARENT:
- return LAYOUT_CENTER_HORIZONTAL;
- }
- return null;
-
- case ALIGN_TOP:
- case LAYOUT_BELOW:
- case ALIGN_BASELINE:
- switch (first) {
- case LAYOUT_CENTER_VERTICAL:
- case ALIGN_TOP:
- case LAYOUT_BELOW:
- case ALIGN_BASELINE:
- return first;
- case LAYOUT_CENTER_IN_PARENT:
- return LAYOUT_CENTER_VERTICAL;
- }
- return null;
- case ALIGN_BOTTOM:
- case LAYOUT_ABOVE:
- switch (first) {
- case LAYOUT_CENTER_VERTICAL:
- case ALIGN_BOTTOM:
- case LAYOUT_ABOVE:
- case ALIGN_BASELINE:
- return first;
- case LAYOUT_CENTER_IN_PARENT:
- return LAYOUT_CENTER_VERTICAL;
- }
- return null;
- }
-
- return null;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/DependencyGraph.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/DependencyGraph.java
deleted file mode 100644
index 43d52d137..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/DependencyGraph.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (C) 2011 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.relative;
-
-import static com.android.SdkConstants.ATTR_ID;
-import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX;
-import static com.android.SdkConstants.VALUE_TRUE;
-
-
-import com.android.SdkConstants;
-import static com.android.SdkConstants.ANDROID_URI;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IDragElement.IDragAttribute;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.INode.IAttribute;
-import com.android.ide.common.layout.BaseLayoutRule;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Data structure about relative layout relationships which makes it possible to:
- * <ul>
- * <li> Quickly determine not just the dependencies on other nodes, but which nodes
- * depend on this node such that they can be visualized for the selection
- * <li> Determine if there are cyclic dependencies, and whether a potential move
- * would result in a cycle
- * <li> Determine the "depth" of a given node (in terms of how many connections it
- * is away from a parent edge) such that we can prioritize connections which
- * minimizes the depth
- * </ul>
- */
-class DependencyGraph {
- /** Format to chain include cycles in: a=>b=>c=>d etc */
- static final String CHAIN_FORMAT = "%1$s=>%2$s"; //$NON-NLS-1$
-
- /** Format to chain constraint dependencies: button 1 above button2 etc */
- private static final String DEPENDENCY_FORMAT = "%1$s %2$s %3$s"; //$NON-NLS-1$
-
- private final Map<String, ViewData> mIdToView = new HashMap<String, ViewData>();
- private final Map<INode, ViewData> mNodeToView = new HashMap<INode, ViewData>();
-
- /** Constructs a new {@link DependencyGraph} for the given relative layout */
- DependencyGraph(INode layout) {
- INode[] nodes = layout.getChildren();
-
- // Parent view:
- String parentId = layout.getStringAttr(ANDROID_URI, ATTR_ID);
- if (parentId != null) {
- parentId = BaseLayoutRule.stripIdPrefix(parentId);
- } else {
- parentId = "RelativeLayout"; // For display purposes; we never reference
- // the parent id from a constraint, only via parent-relative params
- // like centerInParent
- }
- ViewData parentView = new ViewData(layout, parentId);
- mNodeToView.put(layout, parentView);
- if (parentId != null) {
- mIdToView.put(parentId, parentView);
- }
-
- for (INode child : nodes) {
- String id = child.getStringAttr(ANDROID_URI, ATTR_ID);
- if (id != null) {
- id = BaseLayoutRule.stripIdPrefix(id);
- }
- ViewData view = new ViewData(child, id);
- mNodeToView.put(child, view);
- if (id != null) {
- mIdToView.put(id, view);
- }
- }
-
- for (ViewData view : mNodeToView.values()) {
- for (IAttribute attribute : view.node.getLiveAttributes()) {
- String name = attribute.getName();
- ConstraintType type = ConstraintType.fromAttribute(name);
- if (type != null) {
- String value = attribute.getValue();
-
- if (type.targetParent) {
- if (value.equals(VALUE_TRUE)) {
- Constraint constraint = new Constraint(type, view, parentView);
- view.dependsOn.add(constraint);
- parentView.dependedOnBy.add(constraint);
- }
- } else {
- // id-based constraint.
- // NOTE: The id could refer to some widget that is NOT a sibling!
- String targetId = BaseLayoutRule.stripIdPrefix(value);
- ViewData target = mIdToView.get(targetId);
- if (target == view) {
- // Self-reference. RelativeLayout ignores these so it's
- // not an error like a deeper cycle (where RelativeLayout
- // will throw an exception), but we might as well warn
- // the user about it.
- // TODO: Where do we emit this error?
- } else if (target != null) {
- Constraint constraint = new Constraint(type, view, target);
- view.dependsOn.add(constraint);
- target.dependedOnBy.add(constraint);
- } else {
- // This is valid but we might want to warn...
- //System.out.println("Warning: no view data found for " + targetId);
- }
- }
- }
- }
- }
- }
-
- public ViewData getView(IDragElement element) {
- IDragAttribute attribute = element.getAttribute(ANDROID_URI, ATTR_ID);
- if (attribute != null) {
- String id = attribute.getValue();
- id = BaseLayoutRule.stripIdPrefix(id);
- return getView(id);
- }
-
- return null;
- }
-
- public ViewData getView(String id) {
- return mIdToView.get(id);
- }
-
- public ViewData getView(INode node) {
- return mNodeToView.get(node);
- }
-
- /**
- * Returns the set of views that depend on the given node in either the horizontal or
- * vertical direction
- *
- * @param nodes the set of nodes that we want to compute the transitive dependencies
- * for
- * @param vertical if true, look for vertical edge dependencies, otherwise look for
- * horizontal edge dependencies
- * @return the set of nodes that directly or indirectly depend on the given nodes in
- * the given direction
- */
- public Set<INode> dependsOn(Collection<? extends INode> nodes, boolean vertical) {
- List<ViewData> reachable = new ArrayList<ViewData>();
-
- // Traverse the graph of constraints and determine all nodes affected by
- // this node
- Set<ViewData> visiting = new HashSet<ViewData>();
- for (INode node : nodes) {
- ViewData view = mNodeToView.get(node);
- if (view != null) {
- findBackwards(view, visiting, reachable, vertical, view);
- }
- }
-
- Set<INode> dependents = new HashSet<INode>(reachable.size());
-
- for (ViewData v : reachable) {
- dependents.add(v.node);
- }
-
- return dependents;
- }
-
- private void findBackwards(ViewData view,
- Set<ViewData> visiting, List<ViewData> reachable,
- boolean vertical, ViewData start) {
- visiting.add(view);
- reachable.add(view);
-
- for (Constraint constraint : view.dependedOnBy) {
- if (vertical && !constraint.type.verticalEdge) {
- continue;
- } else if (!vertical && !constraint.type.horizontalEdge) {
- continue;
- }
-
- assert constraint.to == view;
- ViewData from = constraint.from;
- if (visiting.contains(from)) {
- // Cycle - what do we do to highlight this?
- List<Constraint> path = getPathTo(start.node, view.node, vertical);
- if (path != null) {
- // TODO: display to the user somehow. We need log access for the
- // view rules.
- System.out.println(Constraint.describePath(path, null, null));
- }
- } else {
- findBackwards(from, visiting, reachable, vertical, start);
- }
- }
-
- visiting.remove(view);
- }
-
- public List<Constraint> getPathTo(INode from, INode to, boolean vertical) {
- // Traverse the graph of constraints and determine all nodes affected by
- // this node
- Set<ViewData> visiting = new HashSet<ViewData>();
- List<Constraint> path = new ArrayList<Constraint>();
- ViewData view = mNodeToView.get(from);
- if (view != null) {
- return findForwards(view, visiting, path, vertical, to);
- }
-
- return null;
- }
-
- private List<Constraint> findForwards(ViewData view, Set<ViewData> visiting,
- List<Constraint> path, boolean vertical, INode target) {
- visiting.add(view);
-
- for (Constraint constraint : view.dependsOn) {
- if (vertical && !constraint.type.verticalEdge) {
- continue;
- } else if (!vertical && !constraint.type.horizontalEdge) {
- continue;
- }
-
- try {
- path.add(constraint);
-
- if (constraint.to.node == target) {
- return new ArrayList<Constraint>(path);
- }
-
- assert constraint.from == view;
- ViewData to = constraint.to;
- if (visiting.contains(to)) {
- // CYCLE!
- continue;
- }
-
- List<Constraint> chain = findForwards(to, visiting, path, vertical, target);
- if (chain != null) {
- return chain;
- }
- } finally {
- path.remove(constraint);
- }
- }
-
- visiting.remove(view);
-
- return null;
- }
-
- /**
- * Info about a specific widget child of a relative layout and its constraints. This
- * is a node in the dependency graph.
- */
- static class ViewData {
- public final INode node;
- public final String id;
- public final List<Constraint> dependsOn = new ArrayList<Constraint>(4);
- public final List<Constraint> dependedOnBy = new ArrayList<Constraint>(8);
-
- ViewData(INode node, String id) {
- this.node = node;
- this.id = id;
- }
- }
-
- /**
- * Info about a specific constraint between two widgets in a relative layout. This is
- * an edge in the dependency graph.
- */
- static class Constraint {
- public final ConstraintType type;
- public final ViewData from;
- public final ViewData to;
-
- // TODO: Initialize depth -- should be computed independently for top, left, etc.
- // We can use this in GuidelineHandler.MatchComparator to prefer matches that
- // are closer to a parent edge:
- //public int depth;
-
- Constraint(ConstraintType type, ViewData from, ViewData to) {
- this.type = type;
- this.from = from;
- this.to = to;
- }
-
- static String describePath(List<Constraint> path, String newName, String newId) {
- String s = "";
- for (int i = path.size() - 1; i >= 0; i--) {
- Constraint constraint = path.get(i);
- String suffix = (i == path.size() -1) ? constraint.to.id : s;
- s = String.format(DEPENDENCY_FORMAT, constraint.from.id,
- stripLayoutAttributePrefix(constraint.type.name), suffix);
- }
-
- if (newName != null) {
- s = String.format(DEPENDENCY_FORMAT, s, stripLayoutAttributePrefix(newName),
- BaseLayoutRule.stripIdPrefix(newId));
- }
-
- return s;
- }
-
- private static String stripLayoutAttributePrefix(String name) {
- if (name.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX)) {
- return name.substring(ATTR_LAYOUT_RESOURCE_PREFIX.length());
- }
-
- return name;
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/GuidelineHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/GuidelineHandler.java
deleted file mode 100644
index db08b1857..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/GuidelineHandler.java
+++ /dev/null
@@ -1,839 +0,0 @@
-/*
- * Copyright (C) 2011 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.relative;
-
-import static com.android.ide.common.api.MarginType.NO_MARGIN;
-import static com.android.ide.common.api.MarginType.WITHOUT_MARGIN;
-import static com.android.ide.common.api.MarginType.WITH_MARGIN;
-import static com.android.ide.common.api.SegmentType.BASELINE;
-import static com.android.ide.common.api.SegmentType.BOTTOM;
-import static com.android.ide.common.api.SegmentType.CENTER_HORIZONTAL;
-import static com.android.ide.common.api.SegmentType.CENTER_VERTICAL;
-import static com.android.ide.common.api.SegmentType.LEFT;
-import static com.android.ide.common.api.SegmentType.RIGHT;
-import static com.android.ide.common.api.SegmentType.TOP;
-import static com.android.ide.common.layout.BaseLayoutRule.getMaxMatchDistance;
-import static com.android.SdkConstants.ATTR_ID;
-import static com.android.SdkConstants.ATTR_LAYOUT_ABOVE;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BASELINE;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BOTTOM;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_LEFT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_BOTTOM;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_LEFT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_RIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_TOP;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_RIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_TOP;
-import static com.android.SdkConstants.ATTR_LAYOUT_BELOW;
-import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_HORIZONTAL;
-import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_IN_PARENT;
-import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_VERTICAL;
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_BOTTOM;
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_LEFT;
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_RIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_TOP;
-import static com.android.SdkConstants.ATTR_LAYOUT_TO_LEFT_OF;
-import static com.android.SdkConstants.ATTR_LAYOUT_TO_RIGHT_OF;
-import static com.android.SdkConstants.VALUE_N_DP;
-import static com.android.SdkConstants.VALUE_TRUE;
-import static com.android.ide.common.layout.relative.ConstraintType.ALIGN_BASELINE;
-
-import static java.lang.Math.abs;
-
-import com.android.SdkConstants;
-import static com.android.SdkConstants.ANDROID_URI;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IClientRulesEngine;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.Margins;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.Segment;
-import com.android.ide.common.api.SegmentType;
-import com.android.ide.common.layout.BaseLayoutRule;
-import com.android.ide.common.layout.relative.DependencyGraph.Constraint;
-import com.android.ide.common.layout.relative.DependencyGraph.ViewData;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Set;
-
-/**
- * The {@link GuidelineHandler} class keeps track of state related to a guideline operation
- * like move and resize, and performs various constraint computations.
- */
-public class GuidelineHandler {
- /**
- * A dependency graph for the relative layout recording constraint relationships
- */
- protected DependencyGraph mDependencyGraph;
-
- /** The RelativeLayout we are moving/resizing within */
- public INode layout;
-
- /** The set of nodes being dragged (may be null) */
- protected Collection<INode> mDraggedNodes;
-
- /** The bounds of the primary child node being dragged */
- protected Rect mBounds;
-
- /** Whether the left edge is being moved/resized */
- protected boolean mMoveLeft;
-
- /** Whether the right edge is being moved/resized */
- protected boolean mMoveRight;
-
- /** Whether the top edge is being moved/resized */
- protected boolean mMoveTop;
-
- /** Whether the bottom edge is being moved/resized */
- protected boolean mMoveBottom;
-
- /**
- * Whether the drop/move/resize position should be snapped (which can be turned off
- * with a modifier key during the operation)
- */
- protected boolean mSnap = true;
-
- /**
- * The set of nodes which depend on the currently selected nodes, including
- * transitively, through horizontal constraints (a "horizontal constraint"
- * is a constraint between two horizontal edges)
- */
- protected Set<INode> mHorizontalDeps;
-
- /**
- * The set of nodes which depend on the currently selected nodes, including
- * transitively, through vertical constraints (a "vertical constraint"
- * is a constraint between two vertical edges)
- */
- protected Set<INode> mVerticalDeps;
-
- /** The current list of constraints which result in a horizontal cycle (if applicable) */
- protected List<Constraint> mHorizontalCycle;
-
- /** The current list of constraints which result in a vertical cycle (if applicable) */
- protected List<Constraint> mVerticalCycle;
-
- /**
- * All horizontal segments in the relative layout - top and bottom edges, baseline
- * edges, and top and bottom edges offset by the applicable margins in each direction
- */
- protected List<Segment> mHorizontalEdges;
-
- /**
- * All vertical segments in the relative layout - left and right edges, and left and
- * right edges offset by the applicable margins in each direction
- */
- protected List<Segment> mVerticalEdges;
-
- /**
- * All center vertical segments in the relative layout. These are kept separate since
- * they only match other center edges.
- */
- protected List<Segment> mCenterVertEdges;
-
- /**
- * All center horizontal segments in the relative layout. These are kept separate
- * since they only match other center edges.
- */
- protected List<Segment> mCenterHorizEdges;
-
- /**
- * Suggestions for horizontal matches. There could be more than one, but all matches
- * will be equidistant from the current position (as well as in the same direction,
- * which means that you can't have one match 5 pixels to the left and one match 5
- * pixels to the right since it would be impossible to snap to fit with both; you can
- * however have multiple matches all 5 pixels to the left.)
- * <p
- * The best vertical match will be found in {@link #mCurrentTopMatch} or
- * {@link #mCurrentBottomMatch}.
- */
- protected List<Match> mHorizontalSuggestions;
-
- /**
- * Suggestions for vertical matches.
- * <p
- * The best vertical match will be found in {@link #mCurrentLeftMatch} or
- * {@link #mCurrentRightMatch}.
- */
- protected List<Match> mVerticalSuggestions;
-
- /**
- * The current match on the left edge, or null if no match or if the left edge is not
- * being moved or resized.
- */
- protected Match mCurrentLeftMatch;
-
- /**
- * The current match on the top edge, or null if no match or if the top edge is not
- * being moved or resized.
- */
- protected Match mCurrentTopMatch;
-
- /**
- * The current match on the right edge, or null if no match or if the right edge is
- * not being moved or resized.
- */
- protected Match mCurrentRightMatch;
-
- /**
- * The current match on the bottom edge, or null if no match or if the bottom edge is
- * not being moved or resized.
- */
- protected Match mCurrentBottomMatch;
-
- /**
- * The amount of margin to add to the top edge, or 0
- */
- protected int mTopMargin;
-
- /**
- * The amount of margin to add to the bottom edge, or 0
- */
- protected int mBottomMargin;
-
- /**
- * The amount of margin to add to the left edge, or 0
- */
- protected int mLeftMargin;
-
- /**
- * The amount of margin to add to the right edge, or 0
- */
- protected int mRightMargin;
-
- /**
- * The associated rules engine
- */
- protected IClientRulesEngine mRulesEngine;
-
- /**
- * Construct a new {@link GuidelineHandler} for the given relative layout.
- *
- * @param layout the RelativeLayout to handle
- */
- GuidelineHandler(INode layout, IClientRulesEngine rulesEngine) {
- this.layout = layout;
- mRulesEngine = rulesEngine;
-
- mHorizontalEdges = new ArrayList<Segment>();
- mVerticalEdges = new ArrayList<Segment>();
- mCenterVertEdges = new ArrayList<Segment>();
- mCenterHorizEdges = new ArrayList<Segment>();
- mDependencyGraph = new DependencyGraph(layout);
- }
-
- /**
- * Returns true if the handler has any suggestions to offer
- *
- * @return true if the handler has any suggestions to offer
- */
- public boolean haveSuggestions() {
- return mCurrentLeftMatch != null || mCurrentTopMatch != null
- || mCurrentRightMatch != null || mCurrentBottomMatch != null;
- }
-
- /**
- * Returns the closest match.
- *
- * @return the closest match, or null if nothing matched
- */
- protected Match pickBestMatch(List<Match> matches) {
- int alternatives = matches.size();
- if (alternatives == 0) {
- return null;
- } else if (alternatives == 1) {
- Match match = matches.get(0);
- return match;
- } else {
- assert alternatives > 1;
- Collections.sort(matches, new MatchComparator());
- return matches.get(0);
- }
- }
-
- private boolean checkCycle(DropFeedback feedback, Match match, boolean vertical) {
- if (match != null && match.cycle) {
- for (INode node : mDraggedNodes) {
- INode from = match.edge.node;
- assert match.with.node == null || match.with.node == node;
- INode to = node;
- List<Constraint> path = mDependencyGraph.getPathTo(from, to, vertical);
- if (path != null) {
- if (vertical) {
- mVerticalCycle = path;
- } else {
- mHorizontalCycle = path;
- }
- String desc = Constraint.describePath(path,
- match.type.name, match.edge.id);
-
- feedback.errorMessage = "Constraint creates a cycle: " + desc;
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Checks for any cycles in the dependencies
- *
- * @param feedback the drop feedback state
- */
- public void checkCycles(DropFeedback feedback) {
- // Deliberate short circuit evaluation -- only list the first cycle
- feedback.errorMessage = null;
- mHorizontalCycle = null;
- mVerticalCycle = null;
-
- if (checkCycle(feedback, mCurrentTopMatch, true /* vertical */)
- || checkCycle(feedback, mCurrentBottomMatch, true)) {
- }
-
- if (checkCycle(feedback, mCurrentLeftMatch, false)
- || checkCycle(feedback, mCurrentRightMatch, false)) {
- }
- }
-
- /** Records the matchable outside edges for the given node to the potential match list */
- protected void addBounds(INode node, String id,
- boolean addHorizontal, boolean addVertical) {
- Rect b = node.getBounds();
- Margins margins = node.getMargins();
- if (addHorizontal) {
- if (margins.top != 0) {
- mHorizontalEdges.add(new Segment(b.y, b.x, b.x2(), node, id, TOP, WITHOUT_MARGIN));
- mHorizontalEdges.add(new Segment(b.y - margins.top, b.x, b.x2(), node, id,
- TOP, WITH_MARGIN));
- } else {
- mHorizontalEdges.add(new Segment(b.y, b.x, b.x2(), node, id, TOP, NO_MARGIN));
- }
- if (margins.bottom != 0) {
- mHorizontalEdges.add(new Segment(b.y2(), b.x, b.x2(), node, id, BOTTOM,
- WITHOUT_MARGIN));
- mHorizontalEdges.add(new Segment(b.y2() + margins.bottom, b.x, b.x2(), node,
- id, BOTTOM, WITH_MARGIN));
- } else {
- mHorizontalEdges.add(new Segment(b.y2(), b.x, b.x2(), node, id,
- BOTTOM, NO_MARGIN));
- }
- }
- if (addVertical) {
- if (margins.left != 0) {
- mVerticalEdges.add(new Segment(b.x, b.y, b.y2(), node, id, LEFT, WITHOUT_MARGIN));
- mVerticalEdges.add(new Segment(b.x - margins.left, b.y, b.y2(), node, id, LEFT,
- WITH_MARGIN));
- } else {
- mVerticalEdges.add(new Segment(b.x, b.y, b.y2(), node, id, LEFT, NO_MARGIN));
- }
-
- if (margins.right != 0) {
- mVerticalEdges.add(new Segment(b.x2(), b.y, b.y2(), node, id,
- RIGHT, WITHOUT_MARGIN));
- mVerticalEdges.add(new Segment(b.x2() + margins.right, b.y, b.y2(), node, id,
- RIGHT, WITH_MARGIN));
- } else {
- mVerticalEdges.add(new Segment(b.x2(), b.y, b.y2(), node, id,
- RIGHT, NO_MARGIN));
- }
- }
- }
-
- /** Records the center edges for the given node to the potential match list */
- protected void addCenter(INode node, String id,
- boolean addHorizontal, boolean addVertical) {
- Rect b = node.getBounds();
-
- if (addHorizontal) {
- mCenterHorizEdges.add(new Segment(b.centerY(), b.x, b.x2(),
- node, id, CENTER_HORIZONTAL, NO_MARGIN));
- }
- if (addVertical) {
- mCenterVertEdges.add(new Segment(b.centerX(), b.y, b.y2(),
- node, id, CENTER_VERTICAL, NO_MARGIN));
- }
- }
-
- /** Records the baseline edge for the given node to the potential match list */
- protected int addBaseLine(INode node, String id) {
- int baselineY = node.getBaseline();
- if (baselineY != -1) {
- Rect b = node.getBounds();
- mHorizontalEdges.add(new Segment(b.y + baselineY, b.x, b.x2(), node, id, BASELINE,
- NO_MARGIN));
- }
-
- return baselineY;
- }
-
- protected void snapVertical(Segment vEdge, int x, Rect newBounds) {
- newBounds.x = x;
- }
-
- protected void snapHorizontal(Segment hEdge, int y, Rect newBounds) {
- newBounds.y = y;
- }
-
- /**
- * Returns whether two edge types are compatible. For example, we only match the
- * center of one object with the center of another.
- *
- * @param edge the first edge type to compare
- * @param dragged the second edge type to compare the first one with
- * @param delta the delta between the two edge locations
- * @return true if the two edge types can be compatibly matched
- */
- protected boolean isEdgeTypeCompatible(SegmentType edge, SegmentType dragged, int delta) {
-
- if (Math.abs(delta) > BaseLayoutRule.getMaxMatchDistance()) {
- if (dragged == LEFT || dragged == TOP) {
- if (delta > 0) {
- return false;
- }
- } else {
- if (delta < 0) {
- return false;
- }
- }
- }
-
- switch (edge) {
- case BOTTOM:
- case TOP:
- return dragged == TOP || dragged == BOTTOM;
- case LEFT:
- case RIGHT:
- return dragged == LEFT || dragged == RIGHT;
-
- // Center horizontal, center vertical and Baseline only matches the same
- // type, and only within the matching distance -- no margins!
- case BASELINE:
- case CENTER_HORIZONTAL:
- case CENTER_VERTICAL:
- return dragged == edge && Math.abs(delta) < getMaxMatchDistance();
- default: assert false : edge;
- }
- return false;
- }
-
- /**
- * Finds the closest matching segments among the given list of edges for the given
- * dragged edge, and returns these as a list of matches
- */
- protected List<Match> findClosest(Segment draggedEdge, List<Segment> edges) {
- List<Match> closest = new ArrayList<Match>();
- addClosest(draggedEdge, edges, closest);
- return closest;
- }
-
- protected void addClosest(Segment draggedEdge, List<Segment> edges,
- List<Match> closest) {
- int at = draggedEdge.at;
- int closestDelta = closest.size() > 0 ? closest.get(0).delta : Integer.MAX_VALUE;
- int closestDistance = abs(closestDelta);
- for (Segment edge : edges) {
- assert draggedEdge.edgeType.isHorizontal() == edge.edgeType.isHorizontal();
-
- int delta = edge.at - at;
- int distance = abs(delta);
- if (distance > closestDistance) {
- continue;
- }
-
- if (!isEdgeTypeCompatible(edge.edgeType, draggedEdge.edgeType, delta)) {
- continue;
- }
-
- boolean withParent = edge.node == layout;
- ConstraintType type = ConstraintType.forMatch(withParent,
- draggedEdge.edgeType, edge.edgeType);
- if (type == null) {
- continue;
- }
-
- // Ensure that the edge match is compatible; for example, a "below"
- // constraint can only apply to the margin bounds and a "bottom"
- // constraint can only apply to the non-margin bounds.
- if (type.relativeToMargin && edge.marginType == WITHOUT_MARGIN) {
- continue;
- } else if (!type.relativeToMargin && edge.marginType == WITH_MARGIN) {
- continue;
- }
-
- Match match = new Match(this, edge, draggedEdge, type, delta);
-
- if (distance < closestDistance) {
- closest.clear();
- closestDistance = distance;
- closestDelta = delta;
- } else if (delta * closestDelta < 0) {
- // They have different signs, e.g. the matches are equal but
- // on opposite sides; can't accept them both
- continue;
- }
- closest.add(match);
- }
- }
-
- protected void clearSuggestions() {
- mHorizontalSuggestions = mVerticalSuggestions = null;
- mCurrentLeftMatch = mCurrentRightMatch = null;
- mCurrentTopMatch = mCurrentBottomMatch = null;
- }
-
- /**
- * Given a node, apply the suggestions by expressing them as relative layout param
- * values
- *
- * @param n the node to apply constraints to
- */
- public void applyConstraints(INode n) {
- // Process each edge separately
- String centerBoth = n.getStringAttr(ANDROID_URI, ATTR_LAYOUT_CENTER_IN_PARENT);
- if (centerBoth != null && centerBoth.equals(VALUE_TRUE)) {
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_IN_PARENT, null);
-
- // If you had a center-in-both-directions attribute, and you're
- // only resizing in one dimension, then leave the other dimension
- // centered, e.g. if you have centerInParent and apply alignLeft,
- // then you should end up with alignLeft and centerVertically
- if (mCurrentTopMatch == null && mCurrentBottomMatch == null) {
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_VERTICAL, VALUE_TRUE);
- }
- if (mCurrentLeftMatch == null && mCurrentRightMatch == null) {
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_HORIZONTAL, VALUE_TRUE);
- }
- }
-
- if (mMoveTop) {
- // Remove top attachments
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_PARENT_TOP, null);
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_TOP, null);
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_BELOW, null);
-
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_VERTICAL, null);
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_BASELINE, null);
-
- }
-
- if (mMoveBottom) {
- // Remove bottom attachments
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_PARENT_BOTTOM, null);
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_BOTTOM, null);
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ABOVE, null);
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_VERTICAL, null);
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_BASELINE, null);
- }
-
- if (mMoveLeft) {
- // Remove left attachments
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_PARENT_LEFT, null);
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_LEFT, null);
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_TO_RIGHT_OF, null);
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_HORIZONTAL, null);
- }
-
- if (mMoveRight) {
- // Remove right attachments
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_PARENT_RIGHT, null);
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_ALIGN_RIGHT, null);
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_TO_LEFT_OF, null);
- n.setAttribute(ANDROID_URI, ATTR_LAYOUT_CENTER_HORIZONTAL, null);
- }
-
- if (mMoveTop && mCurrentTopMatch != null) {
- applyConstraint(n, mCurrentTopMatch.getConstraint(true /* generateId */));
- if (mCurrentTopMatch.type == ALIGN_BASELINE) {
- // HACK! WORKAROUND! Baseline doesn't provide a new bottom edge for attachments
- String c = mCurrentTopMatch.getConstraint(true);
- c = c.replace(ATTR_LAYOUT_ALIGN_BASELINE, ATTR_LAYOUT_ALIGN_BOTTOM);
- applyConstraint(n, c);
- }
- }
-
- if (mMoveBottom && mCurrentBottomMatch != null) {
- applyConstraint(n, mCurrentBottomMatch.getConstraint(true));
- }
-
- if (mMoveLeft && mCurrentLeftMatch != null) {
- applyConstraint(n, mCurrentLeftMatch.getConstraint(true));
- }
-
- if (mMoveRight && mCurrentRightMatch != null) {
- applyConstraint(n, mCurrentRightMatch.getConstraint(true));
- }
-
- if (mMoveLeft) {
- applyMargin(n, ATTR_LAYOUT_MARGIN_LEFT, mLeftMargin);
- }
- if (mMoveRight) {
- applyMargin(n, ATTR_LAYOUT_MARGIN_RIGHT, mRightMargin);
- }
- if (mMoveTop) {
- applyMargin(n, ATTR_LAYOUT_MARGIN_TOP, mTopMargin);
- }
- if (mMoveBottom) {
- applyMargin(n, ATTR_LAYOUT_MARGIN_BOTTOM, mBottomMargin);
- }
- }
-
- private void applyConstraint(INode n, String constraint) {
- assert constraint.contains("=") : constraint;
- String name = constraint.substring(0, constraint.indexOf('='));
- String value = constraint.substring(constraint.indexOf('=') + 1);
- n.setAttribute(ANDROID_URI, name, value);
- }
-
- private void applyMargin(INode n, String marginAttribute, int margin) {
- if (margin > 0) {
- int dp = mRulesEngine.pxToDp(margin);
- n.setAttribute(ANDROID_URI, marginAttribute, String.format(VALUE_N_DP, dp));
- } else if (n.getStringAttr(ANDROID_URI, marginAttribute) != null) {
- // Clear out existing margin
- n.setAttribute(ANDROID_URI, marginAttribute, null);
- }
- }
-
- private void removeRelativeParams(INode node) {
- for (ConstraintType type : ConstraintType.values()) {
- node.setAttribute(ANDROID_URI, type.name, null);
- }
- node.setAttribute(ANDROID_URI,ATTR_LAYOUT_MARGIN_LEFT, null);
- node.setAttribute(ANDROID_URI,ATTR_LAYOUT_MARGIN_RIGHT, null);
- node.setAttribute(ANDROID_URI,ATTR_LAYOUT_MARGIN_TOP, null);
- node.setAttribute(ANDROID_URI,ATTR_LAYOUT_MARGIN_BOTTOM, null);
- }
-
- /**
- * Attach the new child to the previous node
- * @param previous the previous child
- * @param node the new child to attach it to
- */
- public void attachPrevious(INode previous, INode node) {
- removeRelativeParams(node);
-
- String id = previous.getStringAttr(ANDROID_URI, ATTR_ID);
- if (id == null) {
- return;
- }
-
- if (mCurrentTopMatch != null || mCurrentBottomMatch != null) {
- // Attaching the top: arrange below, and for bottom arrange above
- node.setAttribute(ANDROID_URI,
- mCurrentTopMatch != null ? ATTR_LAYOUT_BELOW : ATTR_LAYOUT_ABOVE, id);
- // Apply same left/right constraints as the parent
- if (mCurrentLeftMatch != null) {
- applyConstraint(node, mCurrentLeftMatch.getConstraint(true));
- applyMargin(node, ATTR_LAYOUT_MARGIN_LEFT, mLeftMargin);
- } else if (mCurrentRightMatch != null) {
- applyConstraint(node, mCurrentRightMatch.getConstraint(true));
- applyMargin(node, ATTR_LAYOUT_MARGIN_RIGHT, mRightMargin);
- }
- } else if (mCurrentLeftMatch != null || mCurrentRightMatch != null) {
- node.setAttribute(ANDROID_URI,
- mCurrentLeftMatch != null ? ATTR_LAYOUT_TO_RIGHT_OF : ATTR_LAYOUT_TO_LEFT_OF,
- id);
- // Apply same top/bottom constraints as the parent
- if (mCurrentTopMatch != null) {
- applyConstraint(node, mCurrentTopMatch.getConstraint(true));
- applyMargin(node, ATTR_LAYOUT_MARGIN_TOP, mTopMargin);
- } else if (mCurrentBottomMatch != null) {
- applyConstraint(node, mCurrentBottomMatch.getConstraint(true));
- applyMargin(node, ATTR_LAYOUT_MARGIN_BOTTOM, mBottomMargin);
- }
- } else {
- return;
- }
- }
-
- /** Breaks any cycles detected by the handler */
- public void removeCycles() {
- if (mHorizontalCycle != null) {
- removeCycles(mHorizontalDeps);
- }
- if (mVerticalCycle != null) {
- removeCycles(mVerticalDeps);
- }
- }
-
- private void removeCycles(Set<INode> deps) {
- for (INode node : mDraggedNodes) {
- ViewData view = mDependencyGraph.getView(node);
- if (view != null) {
- for (Constraint constraint : view.dependedOnBy) {
- // For now, remove ALL constraints pointing to this node in this orientation.
- // Later refine this to be smarter. (We can't JUST remove the constraints
- // identified in the cycle since there could be multiple.)
- constraint.from.node.setAttribute(ANDROID_URI, constraint.type.name, null);
- }
- }
- }
- }
-
- /**
- * Comparator used to sort matches such that the first match is the most desirable
- * match (where we prefer attaching to parent bounds, we avoid matches that lead to a
- * cycle, we prefer constraints on closer widgets rather than ones further away, and
- * so on.)
- * <p>
- * There are a number of sorting criteria. One of them is the distance between the
- * matched edges. We may end up with multiple matches that are the same distance. In
- * that case we look at the orientation; on the left side, prefer left-oriented
- * attachments, and on the right-side prefer right-oriented attachments. For example,
- * consider the following scenario:
- *
- * <pre>
- * +--------------------+-------------------------+
- * | Attached on left | |
- * +--------------------+ |
- * | |
- * | +-----+ |
- * | | A | |
- * | +-----+ |
- * | |
- * | +-------------------------+
- * | | Attached on right |
- * +--------------------+-------------------------+
- * </pre>
- *
- * Here, dragging the left edge should attach to the top left attached view, whereas
- * in the following layout dragging the right edge would attach to the bottom view:
- *
- * <pre>
- * +--------------------------+-------------------+
- * | Attached on left | |
- * +--------------------------+ |
- * | |
- * | +-----+ |
- * | | A | |
- * | +-----+ |
- * | |
- * | +-------------------+
- * | | Attached on right |
- * +--------------------------+-------------------+
- *
- * </pre>
- *
- * </ul>
- */
- private final class MatchComparator implements Comparator<Match> {
- @Override
- public int compare(Match m1, Match m2) {
- // Always prefer matching parent bounds
- int parent1 = m1.edge.node == layout ? -1 : 1;
- int parent2 = m2.edge.node == layout ? -1 : 1;
- // unless it's a center bound -- those should always get lowest priority since
- // they overlap with other usually more interesting edges near the center of
- // the layout.
- if (m1.edge.edgeType == CENTER_HORIZONTAL
- || m1.edge.edgeType == CENTER_VERTICAL) {
- parent1 = 2;
- }
- if (m2.edge.edgeType == CENTER_HORIZONTAL
- || m2.edge.edgeType == CENTER_VERTICAL) {
- parent2 = 2;
- }
- if (parent1 != parent2) {
- return parent1 - parent2;
- }
-
- // Avoid matching edges that would lead to a cycle
- if (m1.edge.edgeType.isHorizontal()) {
- int cycle1 = mHorizontalDeps.contains(m1.edge.node) ? 1 : -1;
- int cycle2 = mHorizontalDeps.contains(m2.edge.node) ? 1 : -1;
- if (cycle1 != cycle2) {
- return cycle1 - cycle2;
- }
- } else {
- int cycle1 = mVerticalDeps.contains(m1.edge.node) ? 1 : -1;
- int cycle2 = mVerticalDeps.contains(m2.edge.node) ? 1 : -1;
- if (cycle1 != cycle2) {
- return cycle1 - cycle2;
- }
- }
-
- // TODO: Sort by minimum depth -- do we have the depth anywhere?
-
- // Prefer nodes that are closer
- int distance1, distance2;
- if (m1.edge.to <= m1.with.from) {
- distance1 = m1.with.from - m1.edge.to;
- } else if (m1.edge.from >= m1.with.to) {
- distance1 = m1.edge.from - m1.with.to;
- } else {
- // Some kind of overlap - not sure how to prioritize these yet...
- distance1 = 0;
- }
- if (m2.edge.to <= m2.with.from) {
- distance2 = m2.with.from - m2.edge.to;
- } else if (m2.edge.from >= m2.with.to) {
- distance2 = m2.edge.from - m2.with.to;
- } else {
- // Some kind of overlap - not sure how to prioritize these yet...
- distance2 = 0;
- }
-
- if (distance1 != distance2) {
- return distance1 - distance2;
- }
-
- // Prefer matching on baseline
- int baseline1 = (m1.edge.edgeType == BASELINE) ? -1 : 1;
- int baseline2 = (m2.edge.edgeType == BASELINE) ? -1 : 1;
- if (baseline1 != baseline2) {
- return baseline1 - baseline2;
- }
-
- // Prefer matching top/left edges before matching bottom/right edges
- int orientation1 = (m1.with.edgeType == LEFT ||
- m1.with.edgeType == TOP) ? -1 : 1;
- int orientation2 = (m2.with.edgeType == LEFT ||
- m2.with.edgeType == TOP) ? -1 : 1;
- if (orientation1 != orientation2) {
- return orientation1 - orientation2;
- }
-
- // Prefer opposite-matching over same-matching.
- // In other words, if we have the choice of matching
- // our left edge with another element's left edge,
- // or matching our left edge with another element's right
- // edge, prefer the right edge since that
- // The two matches have identical distance; try to sort by
- // orientation
- int edgeType1 = (m1.edge.edgeType != m1.with.edgeType) ? -1 : 1;
- int edgeType2 = (m2.edge.edgeType != m2.with.edgeType) ? -1 : 1;
- if (edgeType1 != edgeType2) {
- return edgeType1 - edgeType2;
- }
-
- return 0;
- }
- }
-
- /**
- * Returns the {@link IClientRulesEngine} IDE callback
- *
- * @return the {@link IClientRulesEngine} IDE callback, never null
- */
- public IClientRulesEngine getRulesEngine() {
- return mRulesEngine;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/GuidelinePainter.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/GuidelinePainter.java
deleted file mode 100644
index 2fe74768f..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/GuidelinePainter.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2011 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.relative;
-
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_BOTTOM;
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_LEFT;
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_RIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_TOP;
-import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX;
-import static com.android.SdkConstants.ID_PREFIX;
-import static com.android.SdkConstants.NEW_ID_PREFIX;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IFeedbackPainter;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.SegmentType;
-import com.android.ide.common.layout.relative.DependencyGraph.Constraint;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * The {@link GuidelinePainter} is responsible for painting guidelines during an operation
- * which uses a {@link GuidelineHandler} such as a resize operation.
- */
-public final class GuidelinePainter implements IFeedbackPainter {
- // ---- Implements IFeedbackPainter ----
- @Override
- public void paint(@NonNull IGraphics gc, @NonNull INode node, @NonNull DropFeedback feedback) {
- GuidelineHandler state = (GuidelineHandler) feedback.userData;
-
- for (INode dragged : state.mDraggedNodes) {
- gc.useStyle(DrawingStyle.DRAGGED);
- Rect bounds = dragged.getBounds();
- if (bounds.isValid()) {
- gc.fillRect(bounds);
- }
- }
-
- Set<INode> horizontalDeps = state.mHorizontalDeps;
- Set<INode> verticalDeps = state.mVerticalDeps;
- Set<INode> deps = new HashSet<INode>(horizontalDeps.size() + verticalDeps.size());
- deps.addAll(horizontalDeps);
- deps.addAll(verticalDeps);
- if (deps.size() > 0) {
- gc.useStyle(DrawingStyle.DEPENDENCY);
- for (INode n : deps) {
- // Don't highlight the selected nodes themselves
- if (state.mDraggedNodes.contains(n)) {
- continue;
- }
- Rect bounds = n.getBounds();
- gc.fillRect(bounds);
- }
- }
-
- if (state.mBounds != null) {
- if (state instanceof MoveHandler) {
- gc.useStyle(DrawingStyle.DROP_PREVIEW);
- } else {
- // Resizing
- if (state.haveSuggestions()) {
- gc.useStyle(DrawingStyle.RESIZE_PREVIEW);
- } else {
- gc.useStyle(DrawingStyle.RESIZE_FAIL);
- }
- }
- gc.drawRect(state.mBounds);
-
- // Draw baseline preview too
- if (feedback.dragBaseline != -1) {
- int y = state.mBounds.y + feedback.dragBaseline;
- gc.drawLine(state.mBounds.x, y, state.mBounds.x2(), y);
- }
- }
-
- List<String> strings = new ArrayList<String>();
-
- showMatch(gc, state.mCurrentLeftMatch, state, strings,
- state.mLeftMargin, ATTR_LAYOUT_MARGIN_LEFT);
- showMatch(gc, state.mCurrentRightMatch, state, strings,
- state.mRightMargin, ATTR_LAYOUT_MARGIN_RIGHT);
- showMatch(gc, state.mCurrentTopMatch, state, strings,
- state.mTopMargin, ATTR_LAYOUT_MARGIN_TOP);
- showMatch(gc, state.mCurrentBottomMatch, state, strings,
- state.mBottomMargin, ATTR_LAYOUT_MARGIN_BOTTOM);
-
- if (strings.size() > 0) {
- // Update the drag tooltip
- StringBuilder sb = new StringBuilder(200);
- for (String s : strings) {
- if (sb.length() > 0) {
- sb.append('\n');
- }
- sb.append(s);
- }
- feedback.tooltip = sb.toString();
-
- // Set the tooltip orientation to ensure that it does not interfere with
- // the constraint arrows
- if (state.mCurrentLeftMatch != null) {
- feedback.tooltipX = SegmentType.RIGHT;
- } else if (state.mCurrentRightMatch != null) {
- feedback.tooltipX = SegmentType.LEFT;
- }
- if (state.mCurrentTopMatch != null) {
- feedback.tooltipY = SegmentType.BOTTOM;
- } else if (state.mCurrentBottomMatch != null) {
- feedback.tooltipY = SegmentType.TOP;
- }
- } else {
- feedback.tooltip = null;
- }
-
- if (state.mHorizontalCycle != null) {
- paintCycle(gc, state, state.mHorizontalCycle);
- }
- if (state.mVerticalCycle != null) {
- paintCycle(gc, state, state.mVerticalCycle);
- }
- }
-
- /** Paints a particular match constraint */
- private void showMatch(IGraphics gc, Match m, GuidelineHandler state, List<String> strings,
- int margin, String marginAttribute) {
- if (m == null) {
- return;
- }
- ConstraintPainter.paintConstraint(gc, state.mBounds, m);
-
- // Display the constraint. Remove the @id/ and @+id/ prefixes to make the text
- // shorter and easier to read. This doesn't use stripPrefix() because the id is
- // usually not a prefix of the value (for example, 'layout_alignBottom=@+id/foo').
- String constraint = m.getConstraint(false /* generateId */);
- String description = constraint.replace(NEW_ID_PREFIX, "").replace(ID_PREFIX, "");
- if (description.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX)) {
- description = description.substring(ATTR_LAYOUT_RESOURCE_PREFIX.length());
- }
- if (margin > 0) {
- int dp = state.getRulesEngine().pxToDp(margin);
- description = String.format("%1$s, margin=%2$d dp", description, dp);
- }
- strings.add(description);
- }
-
- /** Paints a constraint cycle */
- void paintCycle(IGraphics gc, GuidelineHandler state, List<Constraint> cycle) {
- gc.useStyle(DrawingStyle.CYCLE);
- assert cycle.size() > 0;
-
- INode from = cycle.get(0).from.node;
- Rect fromBounds = from.getBounds();
- if (state.mDraggedNodes.contains(from)) {
- fromBounds = state.mBounds;
- }
- Point fromCenter = fromBounds.center();
- INode to = null;
-
- List<Point> points = new ArrayList<Point>();
- points.add(fromCenter);
-
- for (Constraint constraint : cycle) {
- assert constraint.from.node == from;
- to = constraint.to.node;
- assert from != null && to != null;
-
- Point toCenter = to.getBounds().center();
- points.add(toCenter);
-
- // Also go through the dragged node bounds
- boolean isDragged = state.mDraggedNodes.contains(to);
- if (isDragged) {
- toCenter = state.mBounds.center();
- points.add(toCenter);
- }
-
- from = to;
- fromCenter = toCenter;
- }
-
- points.add(fromCenter);
- points.add(points.get(0));
-
- for (int i = 1, n = points.size(); i < n; i++) {
- gc.drawLine(points.get(i-1), points.get(i));
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/Match.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/Match.java
deleted file mode 100644
index 6f3f0d0f7..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/Match.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2011 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.relative;
-
-import static com.android.SdkConstants.ATTR_ID;
-import static com.android.SdkConstants.VALUE_TRUE;
-
-
-import com.android.SdkConstants;
-import static com.android.SdkConstants.ANDROID_URI;
-import com.android.ide.common.api.Segment;
-
-/** A match is a potential pairing of two segments with a given {@link ConstraintType}. */
-class Match {
- /** the edge of the dragged node that is matched */
- public final Segment with;
-
- /** the "other" edge that the dragged edge is matched with */
- public final Segment edge;
-
- /** the signed distance between the matched edges */
- public final int delta;
-
- /** the type of constraint this is a match for */
- public final ConstraintType type;
-
- /** whether this {@link Match} results in a cycle */
- public boolean cycle;
-
- /** The associated {@link GuidelineHander} which performed the match */
- private final GuidelineHandler mHandler;
-
- /**
- * Create a new match.
- *
- * @param handler the handler which performed the match
- * @param edge the "other" edge that the dragged edge is matched with
- * @param with the edge of the dragged node that is matched
- * @param type the type of constraint this is a match for
- * @param delta the signed distance between the matched edges
- */
- public Match(GuidelineHandler handler, Segment edge, Segment with,
- ConstraintType type, int delta) {
- mHandler = handler;
-
- this.edge = edge;
- this.with = with;
- this.type = type;
- this.delta = delta;
- }
-
- /**
- * Returns the XML constraint attribute value for this match
- *
- * @param generateId whether an id should be generated if one is missing
- * @return the XML constraint attribute value for this match
- */
- public String getConstraint(boolean generateId) {
- if (type.targetParent) {
- return type.name + '=' + VALUE_TRUE;
- } else {
- String id = edge.id;
- if (id == null || id.length() == -1) {
- if (!generateId) {
- // Placeholder to display for the user during dragging
- id = "<generated>";
- } else {
- // Must generate an id on the fly!
- // See if it's been set by a different constraint we've already applied
- // to this same node
- id = edge.node.getStringAttr(ANDROID_URI, ATTR_ID);
- if (id == null || id.length() == 0) {
- id = mHandler.getRulesEngine().getUniqueId(edge.node.getFqcn());
- edge.node.setAttribute(ANDROID_URI, ATTR_ID, id);
- }
- }
- }
- return type.name + '=' + id;
- }
- }
-
- @Override
- public String toString() {
- return "Match [type=" + type + ", delta=" + delta + ", edge=" + edge
- + "]";
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/MoveHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/MoveHandler.java
deleted file mode 100644
index 0fa915d81..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/MoveHandler.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2011 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.relative;
-
-import static com.android.ide.common.api.MarginType.NO_MARGIN;
-import static com.android.ide.common.api.SegmentType.BASELINE;
-import static com.android.ide.common.api.SegmentType.BOTTOM;
-import static com.android.ide.common.api.SegmentType.CENTER_HORIZONTAL;
-import static com.android.ide.common.api.SegmentType.CENTER_VERTICAL;
-import static com.android.ide.common.api.SegmentType.LEFT;
-import static com.android.ide.common.api.SegmentType.RIGHT;
-import static com.android.ide.common.api.SegmentType.TOP;
-import static com.android.SdkConstants.ATTR_ID;
-
-import static java.lang.Math.abs;
-
-import com.android.SdkConstants;
-import static com.android.SdkConstants.ANDROID_URI;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IClientRulesEngine;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.Segment;
-import com.android.ide.common.layout.BaseLayoutRule;
-import com.android.ide.common.layout.relative.DependencyGraph.ViewData;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A {@link MoveHandler} is a {@link GuidelineHandler} which handles move and drop
- * gestures, and offers guideline suggestions and snapping.
- * <p>
- * Unlike the {@link ResizeHandler}, the {@link MoveHandler} looks for matches for all
- * different segment types -- the left edge, the right edge, the baseline, the center
- * edges, and so on -- and picks the best among these.
- */
-public class MoveHandler extends GuidelineHandler {
- private int mDraggedBaseline;
-
- /**
- * Creates a new {@link MoveHandler}.
- *
- * @param layout the layout element the handler is operating on
- * @param elements the elements being dragged in the move operation
- * @param rulesEngine the corresponding {@link IClientRulesEngine}
- */
- public MoveHandler(INode layout, IDragElement[] elements, IClientRulesEngine rulesEngine) {
- super(layout, rulesEngine);
-
- // Compute list of nodes being dragged within the layout, if any
- List<INode> nodes = new ArrayList<INode>();
- for (IDragElement element : elements) {
- ViewData view = mDependencyGraph.getView(element);
- if (view != null) {
- nodes.add(view.node);
- }
- }
- mDraggedNodes = nodes;
-
- mHorizontalDeps = mDependencyGraph.dependsOn(nodes, false /* verticalEdge */);
- mVerticalDeps = mDependencyGraph.dependsOn(nodes, true /* verticalEdge */);
-
- for (INode child : layout.getChildren()) {
- Rect bc = child.getBounds();
- if (bc.isValid()) {
- // First see if this node looks like it's the same as one of the
- // *dragged* bounds
- boolean isDragged = false;
- for (IDragElement element : elements) {
- // This tries to determine if an INode corresponds to an
- // IDragElement, by comparing their bounds.
- if (bc.equals(element.getBounds())) {
- isDragged = true;
- }
- }
-
- if (!isDragged) {
- String id = child.getStringAttr(ANDROID_URI, ATTR_ID);
- // It's okay for id to be null; if you apply a constraint
- // to a node with a missing id we will generate the id
-
- boolean addHorizontal = !mHorizontalDeps.contains(child);
- boolean addVertical = !mVerticalDeps.contains(child);
-
- addBounds(child, id, addHorizontal, addVertical);
- if (addHorizontal) {
- addBaseLine(child, id);
- }
- }
- }
- }
-
- String id = layout.getStringAttr(ANDROID_URI, ATTR_ID);
- addBounds(layout, id, true, true);
- addCenter(layout, id, true, true);
- }
-
- @Override
- protected void snapVertical(Segment vEdge, int x, Rect newBounds) {
- int maxDistance = BaseLayoutRule.getMaxMatchDistance();
- if (vEdge.edgeType == LEFT) {
- int margin = !mSnap ? 0 : abs(newBounds.x - x);
- if (margin > maxDistance) {
- mLeftMargin = margin;
- } else {
- newBounds.x = x;
- }
- } else if (vEdge.edgeType == RIGHT) {
- int margin = !mSnap ? 0 : abs(newBounds.x - (x - newBounds.w));
- if (margin > maxDistance) {
- mRightMargin = margin;
- } else {
- newBounds.x = x - newBounds.w;
- }
- } else if (vEdge.edgeType == CENTER_VERTICAL) {
- newBounds.x = x - newBounds.w / 2;
- } else {
- assert false : vEdge;
- }
- }
-
- // TODO: Consider unifying this with the snapping logic in ResizeHandler
- @Override
- protected void snapHorizontal(Segment hEdge, int y, Rect newBounds) {
- int maxDistance = BaseLayoutRule.getMaxMatchDistance();
- if (hEdge.edgeType == TOP) {
- int margin = !mSnap ? 0 : abs(newBounds.y - y);
- if (margin > maxDistance) {
- mTopMargin = margin;
- } else {
- newBounds.y = y;
- }
- } else if (hEdge.edgeType == BOTTOM) {
- int margin = !mSnap ? 0 : abs(newBounds.y - (y - newBounds.h));
- if (margin > maxDistance) {
- mBottomMargin = margin;
- } else {
- newBounds.y = y - newBounds.h;
- }
- } else if (hEdge.edgeType == CENTER_HORIZONTAL) {
- int margin = !mSnap ? 0 : abs(newBounds.y - (y - newBounds.h / 2));
- if (margin > maxDistance) {
- mTopMargin = margin;
- // or bottomMargin?
- } else {
- newBounds.y = y - newBounds.h / 2;
- }
- } else if (hEdge.edgeType == BASELINE) {
- newBounds.y = y - mDraggedBaseline;
- } else {
- assert false : hEdge;
- }
- }
-
- /**
- * Updates the handler for the given mouse move
- *
- * @param feedback the feedback handler
- * @param elements the elements being dragged
- * @param offsetX the new mouse X coordinate
- * @param offsetY the new mouse Y coordinate
- * @param modifierMask the keyboard modifiers pressed during the drag
- */
- public void updateMove(DropFeedback feedback, IDragElement[] elements,
- int offsetX, int offsetY, int modifierMask) {
- mSnap = (modifierMask & DropFeedback.MODIFIER2) == 0;
-
- Rect firstBounds = elements[0].getBounds();
- INode firstNode = null;
- if (mDraggedNodes != null && mDraggedNodes.size() > 0) {
- // TODO - this isn't quite right; this could be a different node than we have
- // bounds for!
- firstNode = mDraggedNodes.iterator().next();
- firstBounds = firstNode.getBounds();
- }
-
- mBounds = new Rect(offsetX, offsetY, firstBounds.w, firstBounds.h);
- Rect layoutBounds = layout.getBounds();
- if (mBounds.x2() > layoutBounds.x2()) {
- mBounds.x -= mBounds.x2() - layoutBounds.x2();
- }
- if (mBounds.y2() > layoutBounds.y2()) {
- mBounds.y -= mBounds.y2() - layoutBounds.y2();
- }
- if (mBounds.x < layoutBounds.x) {
- mBounds.x = layoutBounds.x;
- }
- if (mBounds.y < layoutBounds.y) {
- mBounds.y = layoutBounds.y;
- }
-
- clearSuggestions();
-
- Rect b = mBounds;
- Segment edge = new Segment(b.y, b.x, b.x2(), null, null, TOP, NO_MARGIN);
- List<Match> horizontalMatches = findClosest(edge, mHorizontalEdges);
- edge = new Segment(b.y2(), b.x, b.x2(), null, null, BOTTOM, NO_MARGIN);
- addClosest(edge, mHorizontalEdges, horizontalMatches);
-
- edge = new Segment(b.x, b.y, b.y2(), null, null, LEFT, NO_MARGIN);
- List<Match> verticalMatches = findClosest(edge, mVerticalEdges);
- edge = new Segment(b.x2(), b.y, b.y2(), null, null, RIGHT, NO_MARGIN);
- addClosest(edge, mVerticalEdges, verticalMatches);
-
- // Match center
- edge = new Segment(b.centerX(), b.y, b.y2(), null, null, CENTER_VERTICAL, NO_MARGIN);
- addClosest(edge, mCenterVertEdges, verticalMatches);
- edge = new Segment(b.centerY(), b.x, b.x2(), null, null, CENTER_HORIZONTAL, NO_MARGIN);
- addClosest(edge, mCenterHorizEdges, horizontalMatches);
-
- // Match baseline
- if (firstNode != null) {
- int baseline = firstNode.getBaseline();
- if (baseline != -1) {
- mDraggedBaseline = baseline;
- edge = new Segment(b.y + baseline, b.x, b.x2(), firstNode, null, BASELINE,
- NO_MARGIN);
- addClosest(edge, mHorizontalEdges, horizontalMatches);
- }
- } else {
- int baseline = feedback.dragBaseline;
- if (baseline != -1) {
- mDraggedBaseline = baseline;
- edge = new Segment(offsetY + baseline, b.x, b.x2(), null, null, BASELINE,
- NO_MARGIN);
- addClosest(edge, mHorizontalEdges, horizontalMatches);
- }
- }
-
- mHorizontalSuggestions = horizontalMatches;
- mVerticalSuggestions = verticalMatches;
- mTopMargin = mBottomMargin = mLeftMargin = mRightMargin = 0;
-
- Match match = pickBestMatch(mHorizontalSuggestions);
- if (match != null) {
- if (mHorizontalDeps.contains(match.edge.node)) {
- match.cycle = true;
- }
-
- // Reset top AND bottom bounds regardless of whether both are bound
- mMoveTop = true;
- mMoveBottom = true;
-
- // TODO: Consider doing the snap logic on all the possible matches
- // BEFORE sorting, in case this affects the best-pick algorithm (since some
- // edges snap and others don't).
- snapHorizontal(match.with, match.edge.at, mBounds);
-
- if (match.with.edgeType == TOP) {
- mCurrentTopMatch = match;
- } else if (match.with.edgeType == BOTTOM) {
- mCurrentBottomMatch = match;
- } else {
- assert match.with.edgeType == CENTER_HORIZONTAL
- || match.with.edgeType == BASELINE : match.with.edgeType;
- mCurrentTopMatch = match;
- }
- }
-
- match = pickBestMatch(mVerticalSuggestions);
- if (match != null) {
- if (mVerticalDeps.contains(match.edge.node)) {
- match.cycle = true;
- }
-
- // Reset left AND right bounds regardless of whether both are bound
- mMoveLeft = true;
- mMoveRight = true;
-
- snapVertical(match.with, match.edge.at, mBounds);
-
- if (match.with.edgeType == LEFT) {
- mCurrentLeftMatch = match;
- } else if (match.with.edgeType == RIGHT) {
- mCurrentRightMatch = match;
- } else {
- assert match.with.edgeType == CENTER_VERTICAL;
- mCurrentLeftMatch = match;
- }
- }
-
- checkCycles(feedback);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ResizeHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ResizeHandler.java
deleted file mode 100644
index a5e071d74..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ResizeHandler.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2011 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.relative;
-
-import static com.android.ide.common.api.MarginType.NO_MARGIN;
-import static com.android.ide.common.api.SegmentType.BASELINE;
-import static com.android.ide.common.api.SegmentType.BOTTOM;
-import static com.android.ide.common.api.SegmentType.CENTER_HORIZONTAL;
-import static com.android.ide.common.api.SegmentType.CENTER_VERTICAL;
-import static com.android.ide.common.api.SegmentType.LEFT;
-import static com.android.ide.common.api.SegmentType.RIGHT;
-import static com.android.ide.common.api.SegmentType.TOP;
-import static com.android.SdkConstants.ATTR_ID;
-
-import static java.lang.Math.abs;
-
-import com.android.SdkConstants;
-import static com.android.SdkConstants.ANDROID_URI;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IClientRulesEngine;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.Segment;
-import com.android.ide.common.api.SegmentType;
-import com.android.ide.common.layout.BaseLayoutRule;
-
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * A {@link ResizeHandler} is a {@link GuidelineHandler} which handles resizing of individual
- * edges in a RelativeLayout.
- */
-public class ResizeHandler extends GuidelineHandler {
- private final SegmentType mHorizontalEdgeType;
- private final SegmentType mVerticalEdgeType;
-
- /**
- * Creates a new {@link ResizeHandler}
- *
- * @param layout the layout containing the resized node
- * @param resized the node being resized
- * @param rulesEngine the applicable {@link IClientRulesEngine}
- * @param horizontalEdgeType the type of horizontal edge being resized, or null
- * @param verticalEdgeType the type of vertical edge being resized, or null
- */
- public ResizeHandler(INode layout, INode resized,
- IClientRulesEngine rulesEngine,
- SegmentType horizontalEdgeType, SegmentType verticalEdgeType) {
- super(layout, rulesEngine);
-
- assert horizontalEdgeType != null || verticalEdgeType != null;
- assert horizontalEdgeType != BASELINE && verticalEdgeType != BASELINE;
- assert horizontalEdgeType != CENTER_HORIZONTAL && verticalEdgeType != CENTER_HORIZONTAL;
- assert horizontalEdgeType != CENTER_VERTICAL && verticalEdgeType != CENTER_VERTICAL;
-
- mHorizontalEdgeType = horizontalEdgeType;
- mVerticalEdgeType = verticalEdgeType;
-
- Set<INode> nodes = Collections.singleton(resized);
- mDraggedNodes = nodes;
-
- mHorizontalDeps = mDependencyGraph.dependsOn(nodes, false /* vertical */);
- mVerticalDeps = mDependencyGraph.dependsOn(nodes, true /* vertical */);
-
- if (horizontalEdgeType != null) {
- if (horizontalEdgeType == TOP) {
- mMoveTop = true;
- } else if (horizontalEdgeType == BOTTOM) {
- mMoveBottom = true;
- }
- }
- if (verticalEdgeType != null) {
- if (verticalEdgeType == LEFT) {
- mMoveLeft = true;
- } else if (verticalEdgeType == RIGHT) {
- mMoveRight = true;
- }
- }
-
- for (INode child : layout.getChildren()) {
- if (child != resized) {
- String id = child.getStringAttr(ANDROID_URI, ATTR_ID);
- addBounds(child, id,
- !mHorizontalDeps.contains(child),
- !mVerticalDeps.contains(child));
- }
- }
-
- addBounds(layout, layout.getStringAttr(ANDROID_URI, ATTR_ID), true, true);
- }
-
- @Override
- protected void snapVertical(Segment vEdge, int x, Rect newBounds) {
- int maxDistance = BaseLayoutRule.getMaxMatchDistance();
- if (vEdge.edgeType == LEFT) {
- int margin = mSnap ? 0 : abs(newBounds.x - x);
- if (margin > maxDistance) {
- mLeftMargin = margin;
- } else {
- newBounds.w += newBounds.x - x;
- newBounds.x = x;
- }
- } else if (vEdge.edgeType == RIGHT) {
- int margin = mSnap ? 0 : abs(newBounds.x - (x - newBounds.w));
- if (margin > maxDistance) {
- mRightMargin = margin;
- } else {
- newBounds.w = x - newBounds.x;
- }
- } else {
- assert false : vEdge;
- }
- }
-
- @Override
- protected void snapHorizontal(Segment hEdge, int y, Rect newBounds) {
- int maxDistance = BaseLayoutRule.getMaxMatchDistance();
- if (hEdge.edgeType == TOP) {
- int margin = mSnap ? 0 : abs(newBounds.y - y);
- if (margin > maxDistance) {
- mTopMargin = margin;
- } else {
- newBounds.h += newBounds.y - y;
- newBounds.y = y;
- }
- } else if (hEdge.edgeType == BOTTOM) {
- int margin = mSnap ? 0 : abs(newBounds.y - (y - newBounds.h));
- if (margin > maxDistance) {
- mBottomMargin = margin;
- } else {
- newBounds.h = y - newBounds.y;
- }
- } else {
- assert false : hEdge;
- }
- }
-
- @Override
- protected boolean isEdgeTypeCompatible(SegmentType edge, SegmentType dragged, int delta) {
- boolean compatible = super.isEdgeTypeCompatible(edge, dragged, delta);
-
- // When resizing and not snapping (e.g. using margins to pick a specific pixel
- // width) we cannot use -negative- margins to jump back to a closer edge; we
- // must always use positive margins, so mark closer edges that result in a negative
- // margin as not compatible.
- if (compatible && !mSnap) {
- switch (dragged) {
- case LEFT:
- case TOP:
- return delta <= 0;
- default:
- return delta >= 0;
- }
- }
-
- return compatible;
- }
-
- /**
- * Updates the handler for the given mouse resize
- *
- * @param feedback the feedback handler
- * @param child the node being resized
- * @param newBounds the new bounds of the resize rectangle
- * @param modifierMask the keyboard modifiers pressed during the drag
- */
- public void updateResize(DropFeedback feedback, INode child, Rect newBounds,
- int modifierMask) {
- mSnap = (modifierMask & DropFeedback.MODIFIER2) == 0;
- mBounds = newBounds;
- clearSuggestions();
-
- Rect b = newBounds;
- Segment hEdge = null;
- Segment vEdge = null;
- String childId = child.getStringAttr(ANDROID_URI, ATTR_ID);
-
- // TODO: MarginType=NO_MARGIN may not be right. Consider resizing a widget
- // that has margins and how that should be handled.
-
- if (mHorizontalEdgeType == TOP) {
- hEdge = new Segment(b.y, b.x, b.x2(), child, childId, mHorizontalEdgeType, NO_MARGIN);
- } else if (mHorizontalEdgeType == BOTTOM) {
- hEdge = new Segment(b.y2(), b.x, b.x2(), child, childId, mHorizontalEdgeType,
- NO_MARGIN);
- } else {
- assert mHorizontalEdgeType == null;
- }
-
- if (mVerticalEdgeType == LEFT) {
- vEdge = new Segment(b.x, b.y, b.y2(), child, childId, mVerticalEdgeType, NO_MARGIN);
- } else if (mVerticalEdgeType == RIGHT) {
- vEdge = new Segment(b.x2(), b.y, b.y2(), child, childId, mVerticalEdgeType, NO_MARGIN);
- } else {
- assert mVerticalEdgeType == null;
- }
-
- mTopMargin = mBottomMargin = mLeftMargin = mRightMargin = 0;
-
- if (hEdge != null && mHorizontalEdges.size() > 0) {
- // Compute horizontal matches
- mHorizontalSuggestions = findClosest(hEdge, mHorizontalEdges);
-
- Match match = pickBestMatch(mHorizontalSuggestions);
- if (match != null
- && (!mSnap || Math.abs(match.delta) < BaseLayoutRule.getMaxMatchDistance())) {
- if (mHorizontalDeps.contains(match.edge.node)) {
- match.cycle = true;
- }
-
- snapHorizontal(hEdge, match.edge.at, newBounds);
-
- if (hEdge.edgeType == TOP) {
- mCurrentTopMatch = match;
- } else if (hEdge.edgeType == BOTTOM) {
- mCurrentBottomMatch = match;
- } else {
- assert hEdge.edgeType == CENTER_HORIZONTAL
- || hEdge.edgeType == BASELINE : hEdge;
- mCurrentTopMatch = match;
- }
- }
- }
-
- if (vEdge != null && mVerticalEdges.size() > 0) {
- mVerticalSuggestions = findClosest(vEdge, mVerticalEdges);
-
- Match match = pickBestMatch(mVerticalSuggestions);
- if (match != null
- && (!mSnap || Math.abs(match.delta) < BaseLayoutRule.getMaxMatchDistance())) {
- if (mVerticalDeps.contains(match.edge.node)) {
- match.cycle = true;
- }
-
- // Snap
- snapVertical(vEdge, match.edge.at, newBounds);
-
- if (vEdge.edgeType == LEFT) {
- mCurrentLeftMatch = match;
- } else if (vEdge.edgeType == RIGHT) {
- mCurrentRightMatch = match;
- } else {
- assert vEdge.edgeType == CENTER_VERTICAL;
- mCurrentLeftMatch = match;
- }
- }
- }
-
- checkCycles(feedback);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/removecol.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/removecol.png
deleted file mode 100644
index c41261afa..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/removecol.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/removerow.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/removerow.png
deleted file mode 100644
index db695a714..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/removerow.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/showgrid.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/showgrid.png
deleted file mode 100644
index 6f7bf9160..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/showgrid.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/snap.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/snap.png
deleted file mode 100644
index b50a16ed1..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/snap.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/structure.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/structure.png
deleted file mode 100644
index e5d753885..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/structure.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/vlinear.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/vlinear.png
deleted file mode 100644
index e03c16e00..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/vlinear.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/weights.png b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/weights.png
deleted file mode 100644
index cb654a140..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/weights.png
+++ /dev/null
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttributeInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttributeInfo.java
deleted file mode 100755
index e246975bb..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttributeInfo.java
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.common.resources.platform;
-
-import static com.android.SdkConstants.ANDROID_PREFIX;
-import static com.android.SdkConstants.ANDROID_THEME_PREFIX;
-import static com.android.SdkConstants.ID_PREFIX;
-import static com.android.SdkConstants.NEW_ID_PREFIX;
-import static com.android.SdkConstants.PREFIX_THEME_REF;
-import static com.android.SdkConstants.VALUE_FALSE;
-import static com.android.SdkConstants.VALUE_TRUE;
-import static com.android.ide.common.api.IAttributeInfo.Format.BOOLEAN;
-import static com.android.ide.common.api.IAttributeInfo.Format.COLOR;
-import static com.android.ide.common.api.IAttributeInfo.Format.DIMENSION;
-import static com.android.ide.common.api.IAttributeInfo.Format.ENUM;
-import static com.android.ide.common.api.IAttributeInfo.Format.FLAG;
-import static com.android.ide.common.api.IAttributeInfo.Format.FLOAT;
-import static com.android.ide.common.api.IAttributeInfo.Format.FRACTION;
-import static com.android.ide.common.api.IAttributeInfo.Format.INTEGER;
-import static com.android.ide.common.api.IAttributeInfo.Format.STRING;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.IAttributeInfo;
-import com.android.ide.common.resources.ResourceRepository;
-import com.android.resources.ResourceType;
-import com.google.common.base.Splitter;
-
-import java.util.EnumSet;
-import java.util.regex.Pattern;
-
-
-/**
- * Information about an attribute as gathered from the attrs.xml file where
- * the attribute was declared. This must include a format (string, reference, float, etc.),
- * possible flag or enum values, whether it's deprecated and its javadoc.
- */
-public class AttributeInfo implements IAttributeInfo {
- /** XML Name of the attribute */
- private String mName;
-
- /** Formats of the attribute. Cannot be null. Should have at least one format. */
- private EnumSet<Format> mFormats;
- /** Values for enum. null for other types. */
- private String[] mEnumValues;
- /** Values for flag. null for other types. */
- private String[] mFlagValues;
- /** Short javadoc (i.e. the first sentence). */
- private String mJavaDoc;
- /** Documentation for deprecated attributes. Null if not deprecated. */
- private String mDeprecatedDoc;
- /** The source class defining this attribute */
- private String mDefinedBy;
-
- /**
- * @param name The XML Name of the attribute
- * @param formats The formats of the attribute. Cannot be null.
- * Should have at least one format.
- */
- public AttributeInfo(@NonNull String name, @NonNull EnumSet<Format> formats) {
- mName = name;
- mFormats = formats;
- }
-
- /**
- * @param name The XML Name of the attribute
- * @param formats The formats of the attribute. Cannot be null.
- * Should have at least one format.
- * @param javadoc Short javadoc (i.e. the first sentence).
- */
- public AttributeInfo(@NonNull String name, @NonNull EnumSet<Format> formats, String javadoc) {
- mName = name;
- mFormats = formats;
- mJavaDoc = javadoc;
- }
-
- public AttributeInfo(AttributeInfo info) {
- mName = info.mName;
- mFormats = info.mFormats;
- mEnumValues = info.mEnumValues;
- mFlagValues = info.mFlagValues;
- mJavaDoc = info.mJavaDoc;
- mDeprecatedDoc = info.mDeprecatedDoc;
- }
-
- /**
- * Sets the XML Name of the attribute
- *
- * @param name the new name to assign
- */
- public void setName(String name) {
- mName = name;
- }
-
- /** Returns the XML Name of the attribute */
- @Override
- public @NonNull String getName() {
- return mName;
- }
- /** Returns the formats of the attribute. Cannot be null.
- * Should have at least one format. */
- @Override
- public @NonNull EnumSet<Format> getFormats() {
- return mFormats;
- }
- /** Returns the values for enums. null for other types. */
- @Override
- public String[] getEnumValues() {
- return mEnumValues;
- }
- /** Returns the values for flags. null for other types. */
- @Override
- public String[] getFlagValues() {
- return mFlagValues;
- }
- /** Returns a short javadoc, .i.e. the first sentence. */
- @Override
- public @NonNull String getJavaDoc() {
- return mJavaDoc;
- }
- /** Returns the documentation for deprecated attributes. Null if not deprecated. */
- @Override
- public String getDeprecatedDoc() {
- return mDeprecatedDoc;
- }
-
- /** Sets the values for enums. null for other types. */
- public AttributeInfo setEnumValues(String[] values) {
- mEnumValues = values;
- return this;
- }
-
- /** Sets the values for flags. null for other types. */
- public AttributeInfo setFlagValues(String[] values) {
- mFlagValues = values;
- return this;
- }
-
- /** Sets a short javadoc, .i.e. the first sentence. */
- public void setJavaDoc(String javaDoc) {
- mJavaDoc = javaDoc;
- }
-
- /** Sets the documentation for deprecated attributes. Null if not deprecated. */
- public void setDeprecatedDoc(String deprecatedDoc) {
- mDeprecatedDoc = deprecatedDoc;
- }
-
- /**
- * Sets the name of the class (fully qualified class name) which defined
- * this attribute
- *
- * @param definedBy the name of the class (fully qualified class name) which
- * defined this attribute
- */
- public void setDefinedBy(String definedBy) {
- mDefinedBy = definedBy;
- }
-
- /**
- * Returns the name of the class (fully qualified class name) which defined
- * this attribute
- *
- * @return the name of the class (fully qualified class name) which defined
- * this attribute
- */
- @Override
- public @NonNull String getDefinedBy() {
- return mDefinedBy;
- }
-
- private final static Pattern INTEGER_PATTERN = Pattern.compile("-?[0-9]+"); //$NON-NLS-1$
- private final static Pattern FLOAT_PATTERN =
- Pattern.compile("-?[0-9]?(\\.[0-9]+)?"); //$NON-NLS-1$
- private final static Pattern DIMENSION_PATTERN =
- Pattern.compile("-?[0-9]+(\\.[0-9]+)?(dp|dip|sp|px|pt|in|mm)"); //$NON-NLS-1$
-
- /**
- * Checks the given value and returns true only if it is a valid XML value
- * for this attribute.
- *
- * @param value the XML value to check
- * @param projectResources project resources to validate resource URLs with,
- * if any
- * @param frameworkResources framework resources to validate resource URLs
- * with, if any
- * @return true if the value is valid, false otherwise
- */
- public boolean isValid(
- @NonNull String value,
- @Nullable ResourceRepository projectResources,
- @Nullable ResourceRepository frameworkResources) {
-
- if (mFormats.contains(STRING) || mFormats.isEmpty()) {
- // Anything is allowed
- return true;
- }
-
- // All other formats require a nonempty string
- if (value.isEmpty()) {
- // Except for flags
- if (mFormats.contains(FLAG)) {
- return true;
- }
-
- return false;
- }
- char first = value.charAt(0);
-
- // There are many attributes which are incorrectly marked in the attrs.xml
- // file, such as "duration", "minHeight", etc. These are marked as only
- // accepting "integer", but also appear to accept "reference". Therefore,
- // in these cases, be more lenient. (This happens for theme references too,
- // such as ?android:attr/listPreferredItemHeight)
- if ((first == '@' || first == '?') /* && mFormats.contains(REFERENCE)*/) {
- if (value.equals("@null")) {
- return true;
- }
-
- if (value.startsWith(NEW_ID_PREFIX) || value.startsWith(ID_PREFIX)) {
- // These are handled in the IdGeneratingResourceFile; we shouldn't
- // complain about not finding ids in the repository yet since they may
- // not yet have been defined (@+id's can be defined in the same layout,
- // later on.)
- return true;
- }
-
- if (value.startsWith(ANDROID_PREFIX) || value.startsWith(ANDROID_THEME_PREFIX)) {
- if (frameworkResources != null) {
- return frameworkResources.hasResourceItem(value);
- }
- } else if (projectResources != null) {
- return projectResources.hasResourceItem(value);
- }
-
- // Validate resource string
- String url = value;
- int typeEnd = url.indexOf('/', 1);
- if (typeEnd != -1) {
- int typeBegin = url.startsWith("@+") ? 2 : 1; //$NON-NLS-1$
- int colon = url.lastIndexOf(':', typeEnd);
- if (colon != -1) {
- typeBegin = colon + 1;
- }
- String typeName = url.substring(typeBegin, typeEnd);
- ResourceType type = ResourceType.getEnum(typeName);
- if (type != null) {
- // TODO: Validate that the name portion conforms to the rules
- // (is an identifier but not a keyword, etc.)
- // Also validate that the prefix before the colon is either
- // not there or is "android"
-
- //int nameBegin = typeEnd + 1;
- //String name = url.substring(nameBegin);
- return true;
- }
- } else if (value.startsWith(PREFIX_THEME_REF)) {
- if (projectResources != null) {
- return projectResources.hasResourceItem(ResourceType.ATTR,
- value.substring(PREFIX_THEME_REF.length()));
- } else {
- // Until proven otherwise
- return true;
- }
- }
- }
-
- if (mFormats.contains(ENUM) && mEnumValues != null) {
- for (String e : mEnumValues) {
- if (value.equals(e)) {
- return true;
- }
- }
- }
-
- if (mFormats.contains(FLAG) && mFlagValues != null) {
- for (String v : Splitter.on('|').split(value)) {
- for (String e : mFlagValues) {
- if (v.equals(e)) {
- return true;
- }
- }
- }
- }
-
- if (mFormats.contains(DIMENSION)) {
- if (DIMENSION_PATTERN.matcher(value).matches()) {
- return true;
- }
- }
-
- if (mFormats.contains(BOOLEAN)) {
- if (value.equalsIgnoreCase(VALUE_TRUE) || value.equalsIgnoreCase(VALUE_FALSE)) {
- return true;
- }
- }
-
- if (mFormats.contains(FLOAT)) {
- if (Character.isDigit(first) || first == '-' || first == '.') {
- if (FLOAT_PATTERN.matcher(value).matches()) {
- return true;
- }
- // AAPT accepts more general floats, such as ".1",
- try {
- Float.parseFloat(value);
- return true;
- } catch (NumberFormatException nufe) {
- // Not a float
- }
- }
- }
-
- if (mFormats.contains(INTEGER)) {
- if (Character.isDigit(first) || first == '-') {
- if (INTEGER_PATTERN.matcher(value).matches()) {
- return true;
- }
- }
- }
-
- if (mFormats.contains(COLOR)) {
- if (first == '#' && value.length() <= 9) { // Only allowed 32 bit ARGB
- try {
- // Use Long.parseLong rather than Integer.parseInt to not overflow on
- // 32 big hex values like "ff191919"
- Long.parseLong(value.substring(1), 16);
- return true;
- } catch (NumberFormatException nufe) {
- // Not a valid color number
- }
- }
- }
-
- if (mFormats.contains(FRACTION)) {
- // should end with % or %p
- return true;
- }
-
- return false;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttrsXmlParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttrsXmlParser.java
deleted file mode 100644
index 1330c50f3..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttrsXmlParser.java
+++ /dev/null
@@ -1,698 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.common.resources.platform;
-
-import static com.android.SdkConstants.DOT_LAYOUT_PARAMS;
-import static com.android.ide.eclipse.adt.AdtConstants.DOC_HIDE;
-
-import com.android.ide.common.api.IAttributeInfo.Format;
-import com.android.ide.common.resources.platform.ViewClassInfo.LayoutParamsInfo;
-import com.android.ide.eclipse.adt.AdtUtils;
-import com.android.utils.ILogger;
-import com.google.common.collect.Maps;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.xml.sax.SAXException;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-/**
- * Parser for attributes description files.
- */
-public final class AttrsXmlParser {
-
- public static final String ANDROID_MANIFEST_STYLEABLE = "AndroidManifest"; //$NON-NLS-1$
-
- private Document mDocument;
- private String mOsAttrsXmlPath;
-
- // all attributes that have the same name are supposed to have the same
- // parameters so we'll keep a cache of them to avoid processing them twice.
- private Map<String, AttributeInfo> mAttributeMap;
-
- /** Map of all attribute names for a given element */
- private final Map<String, DeclareStyleableInfo> mStyleMap =
- new HashMap<String, DeclareStyleableInfo>();
-
- /** Map from format name (lower case) to the uppercase version */
- private Map<String, Format> mFormatNames = new HashMap<String, Format>(10);
-
- /**
- * Map of all (constant, value) pairs for attributes of format enum or flag.
- * E.g. for attribute name=gravity, this tells us there's an enum/flag called "center"
- * with value 0x11.
- */
- private Map<String, Map<String, Integer>> mEnumFlagValues;
-
- /**
- * A logger object. Must not be null.
- */
- private final ILogger mLog;
-
- /**
- * Creates a new {@link AttrsXmlParser}, set to load things from the given
- * XML file. Nothing has been parsed yet. Callers should call {@link #preload()}
- * next.
- *
- * @param osAttrsXmlPath The path of the <code>attrs.xml</code> file to parse.
- * Must not be null. Should point to an existing valid XML document.
- * @param log A logger object. Must not be null.
- * @param expectedAttributeCount expected number of attributes in the file
- */
- public AttrsXmlParser(String osAttrsXmlPath, ILogger log, int expectedAttributeCount) {
- this(osAttrsXmlPath, null /* inheritableAttributes */, log, expectedAttributeCount);
- }
-
- /**
- * Returns the parsed map of attribute infos
- *
- * @return a map from string name to {@link AttributeInfo}
- */
- public Map<String, AttributeInfo> getAttributeMap() {
- return mAttributeMap;
- }
-
- /**
- * Creates a new {@link AttrsXmlParser} set to load things from the given
- * XML file.
- * <p/>
- * If inheritableAttributes is non-null, it must point to a preloaded
- * {@link AttrsXmlParser} which attributes will be used for this one. Since
- * already defined attributes are not modifiable, they are thus "inherited".
- *
- * @param osAttrsXmlPath The path of the <code>attrs.xml</code> file to parse.
- * Must not be null. Should point to an existing valid XML document.
- * @param inheritableAttributes An optional parser with attributes to inherit. Can be null.
- * If not null, the parser must have had its {@link #preload()} method
- * invoked prior to being used here.
- * @param log A logger object. Must not be null.
- * @param expectedAttributeCount expected number of attributes in the file
- */
- public AttrsXmlParser(
- String osAttrsXmlPath,
- AttrsXmlParser inheritableAttributes,
- ILogger log,
- int expectedAttributeCount) {
- mOsAttrsXmlPath = osAttrsXmlPath;
- mLog = log;
-
- assert osAttrsXmlPath != null;
- assert log != null;
-
- mAttributeMap = Maps.newHashMapWithExpectedSize(expectedAttributeCount);
- if (inheritableAttributes == null) {
- mEnumFlagValues = new HashMap<String, Map<String,Integer>>();
- } else {
- mAttributeMap.putAll(inheritableAttributes.mAttributeMap);
- mEnumFlagValues = new HashMap<String, Map<String,Integer>>(
- inheritableAttributes.mEnumFlagValues);
- }
-
- // Pre-compute the set of format names such that we don't have to compute the uppercase
- // version of the same format string names again and again
- for (Format f : Format.values()) {
- mFormatNames.put(f.name().toLowerCase(Locale.US), f);
- }
- }
-
- /**
- * Returns the OS path of the attrs.xml file parsed.
- */
- public String getOsAttrsXmlPath() {
- return mOsAttrsXmlPath;
- }
-
- /**
- * Preloads the document, parsing all attributes and declared styles.
- *
- * @return Self, for command chaining.
- */
- public AttrsXmlParser preload() {
- Document doc = getDocument();
-
- if (doc == null) {
- mLog.warning("Failed to find %1$s", //$NON-NLS-1$
- mOsAttrsXmlPath);
- return this;
- }
-
- Node res = doc.getFirstChild();
- while (res != null &&
- res.getNodeType() != Node.ELEMENT_NODE &&
- !res.getNodeName().equals("resources")) { //$NON-NLS-1$
- res = res.getNextSibling();
- }
-
- if (res == null) {
- mLog.warning("Failed to find a <resources> node in %1$s", //$NON-NLS-1$
- mOsAttrsXmlPath);
- return this;
- }
-
- parseResources(res);
- return this;
- }
-
- /**
- * Loads all attributes & javadoc for the view class info based on the class name.
- */
- public void loadViewAttributes(ViewClassInfo info) {
- if (getDocument() != null) {
- String xmlName = info.getShortClassName();
- DeclareStyleableInfo style = mStyleMap.get(xmlName);
- if (style != null) {
- String definedBy = info.getFullClassName();
- AttributeInfo[] attributes = style.getAttributes();
- for (AttributeInfo attribute : attributes) {
- if (attribute.getDefinedBy() == null) {
- attribute.setDefinedBy(definedBy);
- }
- }
- info.setAttributes(attributes);
- info.setJavaDoc(style.getJavaDoc());
- }
- }
- }
-
- /**
- * Loads all attributes for the layout data info based on the class name.
- */
- public void loadLayoutParamsAttributes(LayoutParamsInfo info) {
- if (getDocument() != null) {
- // Transforms "LinearLayout" and "LayoutParams" into "LinearLayout_Layout".
- ViewClassInfo viewLayoutClass = info.getViewLayoutClass();
- String xmlName = String.format("%1$s_%2$s", //$NON-NLS-1$
- viewLayoutClass.getShortClassName(),
- info.getShortClassName());
- xmlName = AdtUtils.stripSuffix(xmlName, "Params"); //$NON-NLS-1$
-
- DeclareStyleableInfo style = mStyleMap.get(xmlName);
- if (style != null) {
- // For defined by, use the actual class name, e.g.
- // android.widget.LinearLayout.LayoutParams
- String definedBy = viewLayoutClass.getFullClassName() + DOT_LAYOUT_PARAMS;
- AttributeInfo[] attributes = style.getAttributes();
- for (AttributeInfo attribute : attributes) {
- if (attribute.getDefinedBy() == null) {
- attribute.setDefinedBy(definedBy);
- }
- }
- info.setAttributes(attributes);
- }
- }
- }
-
- /**
- * Returns a list of all <code>declare-styleable</code> found in the XML file.
- */
- public Map<String, DeclareStyleableInfo> getDeclareStyleableList() {
- return Collections.unmodifiableMap(mStyleMap);
- }
-
- /**
- * Returns a map of all enum and flag constants sorted by parent attribute name.
- * The map is attribute_name => (constant_name => integer_value).
- */
- public Map<String, Map<String, Integer>> getEnumFlagValues() {
- return mEnumFlagValues;
- }
-
- //-------------------------
-
- /**
- * Creates an XML document from the attrs.xml OS path.
- * May return null if the file doesn't exist or cannot be parsed.
- */
- private Document getDocument() {
- if (mDocument == null) {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- factory.setIgnoringComments(false);
- try {
- DocumentBuilder builder = factory.newDocumentBuilder();
- mDocument = builder.parse(new File(mOsAttrsXmlPath));
- } catch (ParserConfigurationException e) {
- mLog.error(e, "Failed to create XML document builder for %1$s", //$NON-NLS-1$
- mOsAttrsXmlPath);
- } catch (SAXException e) {
- mLog.error(e, "Failed to parse XML document %1$s", //$NON-NLS-1$
- mOsAttrsXmlPath);
- } catch (IOException e) {
- mLog.error(e, "Failed to read XML document %1$s", //$NON-NLS-1$
- mOsAttrsXmlPath);
- }
- }
- return mDocument;
- }
-
- /**
- * Finds all the &lt;declare-styleable&gt; and &lt;attr&gt; nodes
- * in the top &lt;resources&gt; node.
- */
- private void parseResources(Node res) {
-
- Map<String, String> unknownParents = new HashMap<String, String>();
-
- Node lastComment = null;
- for (Node node = res.getFirstChild(); node != null; node = node.getNextSibling()) {
- switch (node.getNodeType()) {
- case Node.COMMENT_NODE:
- lastComment = node;
- break;
- case Node.ELEMENT_NODE:
- if (node.getNodeName().equals("declare-styleable")) { //$NON-NLS-1$
- Node nameNode = node.getAttributes().getNamedItem("name"); //$NON-NLS-1$
- if (nameNode != null) {
- String name = nameNode.getNodeValue();
-
- Node parentNode = node.getAttributes().getNamedItem("parent"); //$NON-NLS-1$
- String parents = parentNode == null ? null : parentNode.getNodeValue();
-
- if (name != null && !mStyleMap.containsKey(name)) {
- DeclareStyleableInfo style = parseDeclaredStyleable(name, node);
- if (parents != null) {
- String[] parentsArray =
- parseStyleableParents(parents, mStyleMap, unknownParents);
- style.setParents(parentsArray);
- }
- mStyleMap.put(name, style);
- unknownParents.remove(name);
- if (lastComment != null) {
- String nodeValue = lastComment.getNodeValue();
- if (nodeValue.contains(DOC_HIDE)) {
- mStyleMap.remove(name);
- } else {
- style.setJavaDoc(parseJavadoc(nodeValue));
- }
- }
- }
- }
- } else if (node.getNodeName().equals("attr")) { //$NON-NLS-1$
- parseAttr(node, lastComment);
- }
- lastComment = null;
- break;
- }
- }
-
- // If we have any unknown parent, re-create synthetic styleable for them.
- for (Entry<String, String> entry : unknownParents.entrySet()) {
- String name = entry.getKey();
- String parent = entry.getValue();
-
- DeclareStyleableInfo style = new DeclareStyleableInfo(name, (AttributeInfo[])null);
- if (parent != null) {
- style.setParents(new String[] { parent });
- }
- mStyleMap.put(name, style);
-
- // Simplify parents names. See SDK Bug 3125910.
- // Implementation detail: that since we want to delete and add to the map,
- // we can't just use an iterator.
- for (String key : new ArrayList<String>(mStyleMap.keySet())) {
- if (key.startsWith(name) && !key.equals(name)) {
- // We found a child which name starts with the full name of the
- // parent. Simplify the children name.
- String newName = ANDROID_MANIFEST_STYLEABLE + key.substring(name.length());
-
- DeclareStyleableInfo newStyle =
- new DeclareStyleableInfo(newName, mStyleMap.get(key));
- mStyleMap.remove(key);
- mStyleMap.put(newName, newStyle);
- }
- }
- }
- }
-
- /**
- * Parses the "parents" attribute from a &lt;declare-styleable&gt;.
- * <p/>
- * The syntax is the following:
- * <pre>
- * parent[.parent]* [[space|,] parent[.parent]* ]
- * </pre>
- * <p/>
- * In English: </br>
- * - There can be one or more parents, separated by whitespace or commas. </br>
- * - Whitespace is ignored and trimmed. </br>
- * - A parent name is actually composed of one or more identifiers joined by a dot.
- * <p/>
- * Styleables do not usually need to declare their parent chain (e.g. the grand-parents
- * of a parent.) Parent names are unique, so in most cases a styleable will only declare
- * its immediate parent.
- * <p/>
- * However it is possible for a styleable's parent to not exist, e.g. if you have a
- * styleable "A" that is the root and then styleable "C" declares its parent to be "A.B".
- * In this case we record "B" as the parent, even though it is unknown and will never be
- * known. Any parent that is currently not in the knownParent map is thus added to the
- * unknownParent set. The caller will remove the name from the unknownParent set when it
- * sees a declaration for it.
- *
- * @param parents The parents string to parse. Must not be null or empty.
- * @param knownParents The map of all declared styles known so far.
- * @param unknownParents A map of all unknown parents collected here.
- * @return The array of terminal parent names parsed from the parents string.
- */
- private String[] parseStyleableParents(String parents,
- Map<String, DeclareStyleableInfo> knownParents,
- Map<String, String> unknownParents) {
-
- ArrayList<String> result = new ArrayList<String>();
-
- for (String parent : parents.split("[ \t\n\r\f,|]")) { //$NON-NLS-1$
- parent = parent.trim();
- if (parent.length() == 0) {
- continue;
- }
- if (parent.indexOf('.') >= 0) {
- // This is a grand-parent/parent chain. Make sure we know about the
- // parents and only record the terminal one.
- String last = null;
- for (String name : parent.split("\\.")) { //$NON-NLS-1$
- if (name.length() > 0) {
- if (!knownParents.containsKey(name)) {
- // Record this unknown parent and its grand parent.
- unknownParents.put(name, last);
- }
- last = name;
- }
- }
- parent = last;
- }
-
- result.add(parent);
- }
-
- return result.toArray(new String[result.size()]);
- }
-
- /**
- * Parses an &lt;attr&gt; node and convert it into an {@link AttributeInfo} if it is valid.
- */
- private AttributeInfo parseAttr(Node attrNode, Node lastComment) {
- AttributeInfo info = null;
- Node nameNode = attrNode.getAttributes().getNamedItem("name"); //$NON-NLS-1$
- if (nameNode != null) {
- String name = nameNode.getNodeValue();
- if (name != null) {
- info = mAttributeMap.get(name);
- // If the attribute is unknown yet, parse it.
- // If the attribute is know but its format is unknown, parse it too.
- if (info == null || info.getFormats().size() == 0) {
- info = parseAttributeTypes(attrNode, name);
- if (info != null) {
- mAttributeMap.put(name, info);
- }
- } else if (lastComment != null) {
- info = new AttributeInfo(info);
- }
- if (info != null) {
- if (lastComment != null) {
- String nodeValue = lastComment.getNodeValue();
- if (nodeValue.contains(DOC_HIDE)) {
- return null;
- }
- info.setJavaDoc(parseJavadoc(nodeValue));
- info.setDeprecatedDoc(parseDeprecatedDoc(nodeValue));
- }
- }
- }
- }
- return info;
- }
-
- /**
- * Finds all the attributes for a particular style node,
- * e.g. a declare-styleable of name "TextView" or "LinearLayout_Layout".
- *
- * @param styleName The name of the declare-styleable node
- * @param declareStyleableNode The declare-styleable node itself
- */
- private DeclareStyleableInfo parseDeclaredStyleable(String styleName,
- Node declareStyleableNode) {
- ArrayList<AttributeInfo> attrs = new ArrayList<AttributeInfo>();
- Node lastComment = null;
- for (Node node = declareStyleableNode.getFirstChild();
- node != null;
- node = node.getNextSibling()) {
-
- switch (node.getNodeType()) {
- case Node.COMMENT_NODE:
- lastComment = node;
- break;
- case Node.ELEMENT_NODE:
- if (node.getNodeName().equals("attr")) { //$NON-NLS-1$
- AttributeInfo info = parseAttr(node, lastComment);
- if (info != null) {
- attrs.add(info);
- }
- }
- lastComment = null;
- break;
- }
-
- }
-
- return new DeclareStyleableInfo(styleName, attrs.toArray(new AttributeInfo[attrs.size()]));
- }
-
- /**
- * Returns the {@link AttributeInfo} for a specific <attr> XML node.
- * This gets the javadoc, the type, the name and the enum/flag values if any.
- * <p/>
- * The XML node is expected to have the following attributes:
- * <ul>
- * <li>"name", which is mandatory. The node is skipped if this is missing.</li>
- * <li>"format".</li>
- * </ul>
- * The format may be one type or two types (e.g. "reference|color").
- * An extra format can be implied: "enum" or "flag" are not specified in the "format" attribute,
- * they are implicitly stated by the presence of sub-nodes <enum> or <flag>.
- * <p/>
- * By design, attr nodes of the same name MUST have the same type.
- * Attribute nodes are thus cached by name and reused as much as possible.
- * When reusing a node, it is duplicated and its javadoc reassigned.
- */
- private AttributeInfo parseAttributeTypes(Node attrNode, String name) {
- EnumSet<Format> formats = null;
- String[] enumValues = null;
- String[] flagValues = null;
-
- Node attrFormat = attrNode.getAttributes().getNamedItem("format"); //$NON-NLS-1$
- if (attrFormat != null) {
- for (String f : attrFormat.getNodeValue().split("\\|")) { //$NON-NLS-1$
- Format format = mFormatNames.get(f);
- if (format == null) {
- mLog.info(
- "Unknown format name '%s' in <attr name=\"%s\">, file '%s'.", //$NON-NLS-1$
- f, name, getOsAttrsXmlPath());
- } else if (format != AttributeInfo.Format.ENUM &&
- format != AttributeInfo.Format.FLAG) {
- if (formats == null) {
- formats = format.asSet();
- } else {
- if (formats.size() == 1) {
- formats = EnumSet.copyOf(formats);
- }
- formats.add(format);
- }
- }
- }
- }
-
- // does this <attr> have <enum> children?
- enumValues = parseEnumFlagValues(attrNode, "enum", name); //$NON-NLS-1$
- if (enumValues != null) {
- if (formats == null) {
- formats = Format.ENUM_SET;
- } else {
- if (formats.size() == 1) {
- formats = EnumSet.copyOf(formats);
- }
- formats.add(Format.ENUM);
- }
- }
-
- // does this <attr> have <flag> children?
- flagValues = parseEnumFlagValues(attrNode, "flag", name); //$NON-NLS-1$
- if (flagValues != null) {
- if (formats == null) {
- formats = Format.FLAG_SET;
- } else {
- if (formats.size() == 1) {
- formats = EnumSet.copyOf(formats);
- }
- formats.add(Format.FLAG);
- }
- }
-
- if (formats == null) {
- formats = Format.NONE;
- }
-
- AttributeInfo info = new AttributeInfo(name, formats);
- info.setEnumValues(enumValues);
- info.setFlagValues(flagValues);
- return info;
- }
-
- /**
- * Given an XML node that represents an <attr> node, this method searches
- * if the node has any children nodes named "target" (e.g. "enum" or "flag").
- * Such nodes must have a "name" attribute.
- * <p/>
- * If "attrNode" is null, look for any <attr> that has the given attrNode
- * and the requested children nodes.
- * <p/>
- * This method collects all the possible names of these children nodes and
- * return them.
- *
- * @param attrNode The <attr> XML node
- * @param filter The child node to look for, either "enum" or "flag".
- * @param attrName The value of the name attribute of <attr>
- *
- * @return Null if there are no such children nodes, otherwise an array of length >= 1
- * of all the names of these children nodes.
- */
- private String[] parseEnumFlagValues(Node attrNode, String filter, String attrName) {
- ArrayList<String> names = null;
- for (Node child = attrNode.getFirstChild(); child != null; child = child.getNextSibling()) {
- if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals(filter)) {
- Node nameNode = child.getAttributes().getNamedItem("name"); //$NON-NLS-1$
- if (nameNode == null) {
- mLog.warning(
- "Missing name attribute in <attr name=\"%s\"><%s></attr>", //$NON-NLS-1$
- attrName, filter);
- } else {
- if (names == null) {
- names = new ArrayList<String>();
- }
- String name = nameNode.getNodeValue();
- names.add(name);
-
- Node valueNode = child.getAttributes().getNamedItem("value"); //$NON-NLS-1$
- if (valueNode == null) {
- mLog.warning(
- "Missing value attribute in <attr name=\"%s\"><%s name=\"%s\"></attr>", //$NON-NLS-1$
- attrName, filter, name);
- } else {
- String value = valueNode.getNodeValue();
- try {
- // Integer.decode cannot handle "ffffffff", see JDK issue 6624867
- int i = (int) (long) Long.decode(value);
-
- Map<String, Integer> map = mEnumFlagValues.get(attrName);
- if (map == null) {
- map = new HashMap<String, Integer>();
- mEnumFlagValues.put(attrName, map);
- }
- map.put(name, Integer.valueOf(i));
-
- } catch(NumberFormatException e) {
- mLog.error(e,
- "Value in <attr name=\"%s\"><%s name=\"%s\" value=\"%s\"></attr> is not a valid decimal or hexadecimal", //$NON-NLS-1$
- attrName, filter, name, value);
- }
- }
- }
- }
- }
- return names == null ? null : names.toArray(new String[names.size()]);
- }
-
- /**
- * Parses the javadoc comment.
- * Only keeps the first sentence.
- * <p/>
- * This does not remove nor simplify links and references.
- */
- private String parseJavadoc(String comment) {
- if (comment == null) {
- return null;
- }
-
- // sanitize & collapse whitespace
- comment = comment.replaceAll("\\s+", " "); //$NON-NLS-1$ //$NON-NLS-2$
-
- // Explicitly remove any @deprecated tags since they are handled separately.
- comment = comment.replaceAll("(?:\\{@deprecated[^}]*\\}|@deprecated[^@}]*)", "");
-
- // take everything up to the first dot that is followed by a space or the end of the line.
- // I love regexps :-). For the curious, the regexp is:
- // - start of line
- // - ignore whitespace
- // - group:
- // - everything, not greedy
- // - non-capturing group (?: )
- // - end of string
- // or
- // - not preceded by a letter, a dot and another letter (for "i.e" and "e.g" )
- // (<! non-capturing zero-width negative look-behind)
- // - a dot
- // - followed by a space (?= non-capturing zero-width positive look-ahead)
- // - anything else is ignored
- comment = comment.replaceFirst("^\\s*(.*?(?:$|(?<![a-zA-Z]\\.[a-zA-Z])\\.(?=\\s))).*", "$1"); //$NON-NLS-1$ //$NON-NLS-2$
-
- return comment;
- }
-
-
- /**
- * Parses the javadoc and extract the first @deprecated tag, if any.
- * Returns null if there's no @deprecated tag.
- * The deprecated tag can be of two forms:
- * - {+@deprecated ...text till the next bracket }
- * Note: there should be no space or + between { and @. I need one in this comment otherwise
- * this method will be tagged as deprecated ;-)
- * - @deprecated ...text till the next @tag or end of the comment.
- * In both cases the comment can be multi-line.
- */
- private String parseDeprecatedDoc(String comment) {
- // Skip if we can't even find the tag in the comment.
- if (comment == null) {
- return null;
- }
-
- // sanitize & collapse whitespace
- comment = comment.replaceAll("\\s+", " "); //$NON-NLS-1$ //$NON-NLS-2$
-
- int pos = comment.indexOf("{@deprecated");
- if (pos >= 0) {
- comment = comment.substring(pos + 12 /* len of {@deprecated */);
- comment = comment.replaceFirst("^([^}]*).*", "$1");
- } else if ((pos = comment.indexOf("@deprecated")) >= 0) {
- comment = comment.substring(pos + 11 /* len of @deprecated */);
- comment = comment.replaceFirst("^(.*?)(?:@.*|$)", "$1");
- } else {
- return null;
- }
-
- return comment.trim();
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/DeclareStyleableInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/DeclareStyleableInfo.java
deleted file mode 100644
index 40111e24e..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/DeclareStyleableInfo.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.common.resources.platform;
-
-
-/**
- * Information needed to represent a View or ViewGroup (aka Layout) item
- * in the layout hierarchy, as extracted from the main android.jar and the
- * associated attrs.xml.
- */
-public class DeclareStyleableInfo {
- /** The style name, never null. */
- private final String mStyleName;
- /** Attributes for this view or view group. Can be empty but never null. */
- private final AttributeInfo[] mAttributes;
- /** Short javadoc. Can be null. */
- private String mJavaDoc;
- /** Optional name of the parents styleable. Can be null. */
- private String[] mParents;
-
- /**
- * Creates a new {@link DeclareStyleableInfo}.
- *
- * @param styleName The name of the style. Should not be empty nor null.
- * @param attributes The initial list of attributes. Can be null.
- */
- public DeclareStyleableInfo(String styleName, AttributeInfo[] attributes) {
- mStyleName = styleName;
- mAttributes = attributes == null ? new AttributeInfo[0] : attributes;
- }
-
- /**
- * Creates a new {@link DeclareStyleableInfo} that has the same attributes
- * as an existing one and only differs by name.
- *
- * @param styleName The name of the style. Should not be empty nor null.
- * @param existing The existing {@link DeclareStyleableInfo} to mirror.
- */
- public DeclareStyleableInfo(String styleName, DeclareStyleableInfo existing) {
- mStyleName = styleName;
-
- mJavaDoc = existing.getJavaDoc();
-
- String[] parents = existing.getParents();
- if (parents != null) {
- mParents = new String[parents.length];
- System.arraycopy(parents, 0, mParents, 0, parents.length);
- }
-
- AttributeInfo[] attrs = existing.getAttributes();
- if (attrs == null || attrs.length == 0) {
- mAttributes = new AttributeInfo[0];
- } else {
- mAttributes = new AttributeInfo[attrs.length];
- System.arraycopy(attrs, 0, mAttributes, 0, attrs.length);
- }
- }
-
- /** Returns style name */
- public String getStyleName() {
- return mStyleName;
- }
-
- /** Returns the attributes for this view or view group. Maybe empty but not null. */
- public AttributeInfo[] getAttributes() {
- return mAttributes;
- }
-
- /** Returns a short javadoc */
- public String getJavaDoc() {
- return mJavaDoc;
- }
-
- /** Sets the javadoc. */
- public void setJavaDoc(String javaDoc) {
- mJavaDoc = javaDoc;
- }
-
- /** Sets the name of the parents styleable. Can be null. */
- public void setParents(String[] parents) {
- mParents = parents;
- }
-
- /** Returns the name of the parents styleable. Can be null. */
- public String[] getParents() {
- return mParents;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/ViewClassInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/ViewClassInfo.java
deleted file mode 100644
index 214eb9cb4..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/ViewClassInfo.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.common.resources.platform;
-
-
-/**
- * Information needed to represent a View or ViewGroup (aka Layout) item
- * in the layout hierarchy, as extracted from the main android.jar and the
- * associated attrs.xml.
- */
-public class ViewClassInfo {
- /** Is this a layout class (i.e. ViewGroup) or just a view? */
- private boolean mIsLayout;
- /** FQCN e.g. android.view.View, never null. */
- private String mFullClassName;
- /** Short class name, e.g. View, never null. */
- private String mShortClassName;
- /** Super class. Can be null. */
- private ViewClassInfo mSuperClass;
- /** Short javadoc. Can be null. */
- private String mJavaDoc;
- /** Attributes for this view or view group. Can be empty but never null. */
- private AttributeInfo[] mAttributes;
-
- public static class LayoutParamsInfo {
- /** Short class name, e.g. LayoutData, never null. */
- private String mShortClassName;
- /** ViewLayout class info owning this layout data */
- private ViewClassInfo mViewLayoutClass;
- /** Super class. Can be null. */
- private LayoutParamsInfo mSuperClass;
- /** Layout Data Attributes for layout classes. Can be empty but not null. */
- private AttributeInfo[] mAttributes;
-
- public LayoutParamsInfo(ViewClassInfo enclosingViewClassInfo,
- String shortClassName, LayoutParamsInfo superClassInfo) {
- mShortClassName = shortClassName;
- mViewLayoutClass = enclosingViewClassInfo;
- mSuperClass = superClassInfo;
- mAttributes = new AttributeInfo[0];
- }
-
- /** Returns short class name, e.g. "LayoutData" */
- public String getShortClassName() {
- return mShortClassName;
- }
- /** Returns the ViewLayout class info enclosing this layout data. Cannot null. */
- public ViewClassInfo getViewLayoutClass() {
- return mViewLayoutClass;
- }
- /** Returns the super class info. Can be null. */
- public LayoutParamsInfo getSuperClass() {
- return mSuperClass;
- }
- /** Returns the LayoutData attributes. Can be empty but not null. */
- public AttributeInfo[] getAttributes() {
- return mAttributes;
- }
- /** Sets the LayoutData attributes. Can be empty but not null. */
- public void setAttributes(AttributeInfo[] attributes) {
- mAttributes = attributes;
- }
- }
-
- /** Layout data info for a layout class. Null for all non-layout classes and always
- * non-null for a layout class. */
- public LayoutParamsInfo mLayoutData;
-
- // --------
-
- public ViewClassInfo(boolean isLayout, String fullClassName, String shortClassName) {
- mIsLayout = isLayout;
- mFullClassName = fullClassName;
- mShortClassName = shortClassName;
- mAttributes = new AttributeInfo[0];
- }
-
- /** Returns whether this is a layout class (i.e. ViewGroup) or just a View */
- public boolean isLayout() {
- return mIsLayout;
- }
-
- /** Returns FQCN e.g. "android.view.View" */
- public String getFullClassName() {
- return mFullClassName;
- }
-
- /** Returns short class name, e.g. "View" */
- public String getShortClassName() {
- return mShortClassName;
- }
-
- /** Returns the super class. Can be null. */
- public ViewClassInfo getSuperClass() {
- return mSuperClass;
- }
-
- /** Returns a short javadoc */
- public String getJavaDoc() {
- return mJavaDoc;
- }
-
- /** Returns the attributes for this view or view group. Maybe empty but not null. */
- public AttributeInfo[] getAttributes() {
- return mAttributes;
- }
-
- /** Returns the LayoutData info for layout classes. Null for non-layout view classes. */
- public LayoutParamsInfo getLayoutData() {
- return mLayoutData;
- }
-
- /**
- * Sets a link on the info of the super class of this View or ViewGroup.
- * <p/>
- * The super class info must be of the same kind (i.e. group to group or view to view)
- * except for the top ViewGroup which links to the View info.
- * <p/>
- * The super class cannot be null except for the top View info.
- */
- public void setSuperClass(ViewClassInfo superClass) {
- mSuperClass = superClass;
- }
-
- /** Sets the javadoc for this View or ViewGroup. */
- public void setJavaDoc(String javaDoc) {
- mJavaDoc = javaDoc;
- }
-
- /** Sets the list of attributes for this View or ViewGroup. */
- public void setAttributes(AttributeInfo[] attributes) {
- mAttributes = attributes;
- }
-
- /**
- * Sets the {@link LayoutParamsInfo} for layout classes.
- * Does nothing for non-layout view classes.
- */
- public void setLayoutParams(LayoutParamsInfo layoutData) {
- if (mIsLayout) {
- mLayoutData = layoutData;
- }
- }
-}