aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--eclipse/dictionary.txt20
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IClientRulesEngine.java10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewMetadata.java109
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewRule.java63
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/InsertType.java35
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/MenuAction.java4
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/Rect.java27
-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/BaseView.java63
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DialerFilterRule.java53
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java50
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageButtonRule.java37
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageViewRule.java37
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java121
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ListViewRule.java22
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MapViewRule.java42
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java49
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SeekBarRule.java37
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabHostRule.java32
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java68
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableRowRule.java43
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java2
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/IGraphicalLayoutEditor.java13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java16
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java4
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java69
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java15
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java12
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java115
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java37
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactory.java3
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java67
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java107
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadata.java131
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/RectTest.java28
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java24
38 files changed, 1394 insertions, 211 deletions
diff --git a/eclipse/dictionary.txt b/eclipse/dictionary.txt
index fe06849..b9356c0 100644
--- a/eclipse/dictionary.txt
+++ b/eclipse/dictionary.txt
@@ -1,5 +1,6 @@
aapt
adb
+addon
adt
aidl
alt
@@ -16,9 +17,11 @@ avds
basename
bitmask
breadcrumb
+builtin
callback
callbacks
checkbox
+classloader
classpath
clipboard
clipboards
@@ -37,13 +40,18 @@ dedent
deprecated
deselect
deselects
+designtime
dev
dex
dexified
diff
+diffs
dirs
dpi
editable
+enum
+enums
+env
exec
fallback
foo
@@ -65,6 +73,7 @@ int
javadoc
layoutlib
leaky
+lib
lifecycle
linestyle
linux
@@ -74,6 +83,7 @@ luminance
mac
macs
marquee
+metadata
min
multi
namespace
@@ -89,12 +99,16 @@ plugin
pre
precompiler
pref
+preload
+preloads
+primordial
printf
programmatically
proguard
proxies
proxy
recompilation
+rect
redo
regexp
registry
@@ -107,14 +121,18 @@ scrollbar
scrollbars
sdk
se
+searchable
semi
+serializer
settable
spec
standalone
stateless
stderr
stdout
+stretchiness
subclassing
+submenu
supertype
syncs
themed
@@ -122,10 +140,12 @@ tmp
tooltip
traceview
ui
+uncomment
undescribed
uninstall
uninstallation
uninstalling
+upcoming
uri
url
validator
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IClientRulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IClientRulesEngine.java
index 647c6a0..a17af01 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IClientRulesEngine.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IClientRulesEngine.java
@@ -28,8 +28,6 @@ import com.android.sdklib.annotations.Nullable;
* <b>NOTE: This is not a public or final API; if you rely on this be prepared
* to adjust your code for the next tools release.</b>
* </p>
- *
- * @see IViewRule#RULES_ENGINE
*/
public interface IClientRulesEngine {
@@ -57,6 +55,14 @@ public interface IClientRulesEngine {
IViewRule loadRule(String fqcn);
/**
+ * Returns the metadata associated with the given fully qualified class name.
+ *
+ * @param fqcn a fully qualified class name for an Android view class
+ * @return the metadata associated with the given fully qualified class name.
+ */
+ IViewMetadata getMetadata(String fqcn);
+
+ /**
* Displays the given message string in an alert dialog with an "OK" button.
*/
void displayAlert(String message);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewMetadata.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewMetadata.java
new file mode 100644
index 0000000..9397ef8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewMetadata.java
@@ -0,0 +1,109 @@
+/*
+ * 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.api;
+
+/**
+ * Metadata about a particular view. The metadata for a View can be found by asking the
+ * {@link IClientRulesEngine} for the metadata for a given class via
+ * {@link IClientRulesEngine#getMetadata}.
+ */
+public interface IViewMetadata {
+ /**
+ * Returns true if this view is a potential parent (e.g. it <b>can</b> have children).
+ *
+ * @return true if this view can have children
+ */
+ public boolean isParent();
+
+ /**
+ * Returns the display name views of this type (a name suitable to display to the
+ * user, normally capitalized and usually but not necessarily tied to the
+ * implementation class). To be clear, a specific view may have an id attribute and a
+ * text attribute, <b>neither</b> of these is the display name. Instead, the class
+ * android.widget.ZoomControls may have the display name "Zoom Controls", and an
+ * individual view created into a layout can for example have the id "ZoomControl01".
+ *
+ * @return the user visible name of views of this type (never null)
+ */
+ public String getDisplayName();
+
+ /**
+ * Returns the tooltip for this view, if any
+ *
+ * @return a tooltip, or null
+ */
+ public String getTooltip();
+
+ /**
+ * Returns the {@link FillPreference} of this view
+ *
+ * @return the {@link FillPreference} of this view
+ */
+ public FillPreference getFillPreference();
+
+ /**
+ * Types of fill behavior that views can prefer.
+ * <p>
+ * TODO: Consider better names. FillPolicy? Stretchiness?
+ */
+ public enum FillPreference {
+ /** This view does not want to fill */
+ NONE,
+ /** This view wants to always fill both horizontal and vertical */
+ BOTH,
+ /** This view wants to fill horizontally but not vertically */
+ WIDTH,
+ /** This view wants to fill vertically but not horizontally */
+ HEIGHT,
+ /**
+ * This view wants to fill in the opposite dimension of the context, e.g. in a
+ * vertical context it wants to fill horizontally, and vice versa
+ */
+ OPPOSITE,
+ /** This view wants to fill horizontally, but only in a vertical context */
+ WIDTH_IN_VERTICAL,
+ /** This view wants to fill vertically, but only in a horizontal context */
+ HEIGHT_IN_HORIZONTAL;
+
+ /**
+ * Returns true if this view wants to fill horizontally, if the context is
+ * vertical or horizontal as indicated by the parameter.
+ *
+ * @param verticalContext If true, the context is vertical, otherwise it is
+ * horizontal.
+ * @return true if this view wants to fill horizontally
+ */
+ public boolean fillHorizontally(boolean verticalContext) {
+ return (this == BOTH || this == WIDTH ||
+ (verticalContext && (this == OPPOSITE || this == WIDTH_IN_VERTICAL)));
+ }
+
+ /**
+ * Returns true if this view wants to fill vertically, if the context is
+ * vertical or horizontal as indicated by the parameter.
+ *
+ * @param verticalContext If true, the context is vertical, otherwise it is
+ * horizontal.
+ * @return true if this view wants to fill vertically
+ */
+ public boolean fillVertically(boolean verticalContext) {
+ return (this == BOTH || this == HEIGHT ||
+ (!verticalContext && (this == OPPOSITE || this == HEIGHT_IN_HORIZONTAL)));
+ }
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewRule.java
index 13699b4..0ec8eb3 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewRule.java
@@ -17,7 +17,6 @@
package com.android.ide.common.api;
import java.util.List;
-import java.util.Map;
/**
@@ -45,13 +44,6 @@ import java.util.Map;
public interface IViewRule {
/**
- * The name of the property that returns a {@link IClientRulesEngine} created for this
- * rules. The instance lets rules use some methods from the rules engine, for example
- * for accessing other rules.
- */
- final static String RULES_ENGINE = "_rules_engine";
-
- /**
* This method is called by the rule engine when the script is first loaded.
* It gives the rule a chance to initialize itself.
*
@@ -63,7 +55,7 @@ public interface IViewRule {
* the engine during initialization and then use it later to invoke some of the
* {@link IClientRulesEngine} methods for example to request user input.
* @return True if this rule can handle the given FQCN. False if the rule can't handle the
- * given FQCN, in which case the rule engine will find another rule matching a parent clas.
+ * given FQCN, in which case the rule engine will find another rule matching a parent class.
*/
boolean onInitialize(String fqcn, IClientRulesEngine engine);
@@ -140,23 +132,6 @@ public interface IViewRule {
INode childNode);
- // ==== XML Creation ====
-
- /**
- * Returns the default attributes that a new XML element of this type should have
- * when added to an XML layout file. Note that these defaults can be overridden by the
- * specific code performing the insertion.
- *
- * TODO:
- * - added=>created
- * - list tuple(uri, local name, str: value)
- * - gen id
- *
- * @return A map of attribute:values for a new element of this type. Can be null or empty.
- */
- Map<?, ?> getDefaultAttributes();
-
-
// ==== Drag'n'drop support ====
/**
@@ -215,4 +190,40 @@ public interface IViewRule {
* @param pastedElements The elements being pasted.
*/
void onPaste(INode targetNode, IDragElement[] pastedElements);
+
+ // ==== XML Creation ====
+
+ /**
+ * Called when a view for this rule is being created. This allows for the rule to
+ * customize the newly created object. Note that this method is called not just when a
+ * view is created from a palette drag, but when views are constructed via a drag-move
+ * (where views are created in the destination and then deleted from the source), and
+ * even when views are constructed programmatically from other view rules. The
+ * {@link InsertType} parameter can be used to distinguish the context for the
+ * insertion. For example, the <code>DialerFilterRule</code> will insert EditText children
+ * when a DialerFilter is first created, but not during a copy/paste or a move.
+ *
+ * @param node the newly created node (which will always be a View that applies to
+ * this {@link IViewRule})
+ * @param parent the parent of the node (which may not yet contain the newly created
+ * node in its child list)
+ * @param insertType whether this node was created as part of a newly created view, or
+ * as a copy, or as a move, etc.
+ */
+ void onCreate(INode node, INode parent, InsertType insertType);
+
+ /**
+ * Called when a child for this view has been created and is being inserted into the
+ * view parent for which this {@link IViewRule} applies. Allows the parent to perform
+ * customizations of the object. As with {@link #onCreate}, the {@link InsertType}
+ * parameter can be used to handle new creation versus moves versus copy/paste
+ * operations differently.
+ *
+ * @param child the newly created node
+ * @param parent the parent of the newly created node (which may not yet contain the
+ * newly created node in its child list)
+ * @param insertType whether this node was created as part of a newly created view, or
+ * as a copy, or as a move, etc.
+ */
+ void onChildInserted(INode child, INode parent, InsertType insertType);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/InsertType.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/InsertType.java
new file mode 100644
index 0000000..15d3a98
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/InsertType.java
@@ -0,0 +1,35 @@
+/*
+ * 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.api;
+
+/**
+ * An enumerated type of different insertion events, such as an insertion from a
+ * copy/paste operation or as the first half of a move operation.
+ */
+public enum InsertType {
+ /** The view is newly created (by for example a palette drag) */
+ CREATE,
+
+ /** The view is being inserted here because it was moved from somewhere else */
+ MOVE,
+
+ /**
+ * The view is being inserted here as a result of a copy/paste from elsewhere
+ * (including drags, but not from the palette)
+ */
+ PASTE;
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/MenuAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/MenuAction.java
index 210e565..d82049d 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/MenuAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/MenuAction.java
@@ -152,7 +152,7 @@ public abstract class MenuAction {
/**
* The base class for {@link Toggle} and {@link Choices}.
*/
- public static abstract class Action extends MenuAction {
+ public static class Action extends MenuAction {
/**
* A callback executed when the action is selected in the context menu.
@@ -176,7 +176,7 @@ public abstract class MenuAction {
* @param callback The callback executed when the action is selected.
* Must not be null.
*/
- private Action(String id, String title, String groupId, IMenuCallback callback) {
+ public Action(String id, String title, String groupId, IMenuCallback callback) {
super(id, title);
mGroupId = groupId;
mCallback = callback;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/Rect.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/Rect.java
index 32caf62..b34d729 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/Rect.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/Rect.java
@@ -16,7 +16,6 @@
package com.android.ide.common.api;
-import org.eclipse.swt.graphics.Rectangle;
/**
@@ -49,11 +48,6 @@ public class Rect {
}
/** Initialize rectangle to the given values. They can be invalid. */
- public Rect(Rectangle swtRect) {
- set(swtRect);
- }
-
- /** Initialize rectangle to the given values. They can be invalid. */
public Rect set(int x, int y, int w, int h) {
this.x = x;
this.y = y;
@@ -68,12 +62,6 @@ public class Rect {
return this;
}
- /** Initialize rectangle to match the given one. */
- public Rect set(Rectangle swtRect) {
- set(swtRect.x, swtRect.y, swtRect.width, swtRect.height);
- return this;
- }
-
/** Returns a new instance of a rectangle with the same values. */
public Rect copy() {
return new Rect(x, y, w, h);
@@ -161,11 +149,6 @@ public class Rect {
}
return this.x == rhs.x && this.y == rhs.y && this.w == rhs.w && this.h == rhs.h;
-
- } else if (obj instanceof Rectangle) {
- Rectangle rhs = (Rectangle) obj;
- return this.x == rhs.x && this.y == rhs.y &&
- this.w == rhs.width && this.h == rhs.height;
}
return false;
@@ -173,10 +156,10 @@ public class Rect {
@Override
public int hashCode() {
- int h = x;
- h ^= ((y >> 8) & 0x0FFFFFF) | ((y & 0x00000FF) << 24);
- h ^= ((w >> 16) & 0x000FFFF) | ((w & 0x000FFFF) << 16);
- h ^= ((h >> 24) & 0x00000FF) | ((h & 0x0FFFFFF) << 8);
- return h;
+ int hc = x;
+ hc ^= ((y >> 8) & 0x0FFFFFF) | ((y & 0x00000FF) << 24);
+ hc ^= ((w >> 16) & 0x000FFFF) | ((w & 0x000FFFF) << 16);
+ hc ^= ((h >> 24) & 0x00000FF) | ((h & 0x0FFFFFF) << 8);
+ return hc;
}
}
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
new file mode 100644
index 0000000..cd1b0fc
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsListViewRule.java
@@ -0,0 +1,28 @@
+/*
+ * 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/BaseView.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseView.java
index 15871d7..045f36d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseView.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseView.java
@@ -26,6 +26,7 @@ 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.MenuAction;
import com.android.ide.common.api.Point;
import com.android.ide.common.api.Rect;
@@ -45,7 +46,7 @@ import java.util.Set;
* Common IViewRule processing to all view and layout classes.
*/
public class BaseView implements IViewRule {
- private IClientRulesEngine mRulesEngine;
+ protected IClientRulesEngine mRulesEngine;
/**
* Namespace for the Android resource XML, i.e.
@@ -53,23 +54,42 @@ public class BaseView implements IViewRule {
*/
public static String ANDROID_URI = "http://schemas.android.com/apk/res/android"; //$NON-NLS-1$
+ /** The fully qualified class name of an EditText view */
+ public static final String FQCN_EDIT_TEXT = "android.widget.EditText"; //$NON-NLS-1$
+
+ /** The fully qualified class name of a LinearLayout view */
+ public static final String FQCN_LINEAR_LAYOUT = "android.widget.LinearLayout"; //$NON-NLS-1$
+
+ /** The fully qualified class name of a FrameLayout view */
+ public static final String FQCN_FRAME_LAYOUT = "android.widget.FrameLayout"; //$NON-NLS-1$
+
+ /** The fully qualified class name of a TableRow view */
+ public static final String FQCN_TABLE_ROW = "android.widget.TableRow"; //$NON-NLS-1$
+
+ /** The fully qualified class name of a TabWidget view */
+ public static final String FQCN_TAB_WIDGET = "android.widget.TabWidget"; //$NON-NLS-1$
+
// Some common Android layout attribute names used by the view rules.
// All these belong to the attribute namespace ANDROID_URI.
- public static String ATTR_ID = "id"; //$NON-NLS-1$
+ public static final String ATTR_ID = "id"; //$NON-NLS-1$
- public static String ATTR_TEXT = "text"; //$NON-NLS-1$
+ public static final String ATTR_TEXT = "text"; //$NON-NLS-1$
- public static String ATTR_LAYOUT_WIDTH = "layout_width"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_WIDTH = "layout_width"; //$NON-NLS-1$
- public static String ATTR_LAYOUT_HEIGHT = "layout_height"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_HEIGHT = "layout_height"; //$NON-NLS-1$
+
+ public static final String ATTR_SRC = "src"; //$NON-NLS-1$
+
+ public static final String ATTR_LAYOUT_BELOW = "layout_below"; //$NON-NLS-1$
// Some common Android layout attribute values used by the view rules.
- public static String VALUE_FILL_PARENT = "fill_parent"; //$NON-NLS-1$
+ public static final String VALUE_FILL_PARENT = "fill_parent"; //$NON-NLS-1$
// like fill_parent for API 8
- public static String VALUE_MATCH_PARENT = "match_parent"; //$NON-NLS-1$
+ public static final String VALUE_MATCH_PARENT = "match_parent"; //$NON-NLS-1$
- public static String VALUE_WRAP_CONTENT = "wrap_content"; //$NON-NLS-1$
+ public static final String VALUE_WRAP_CONTENT = "wrap_content"; //$NON-NLS-1$
// Cache of attributes. Key is FQCN of a node mixed with its view hierarchy
// parent. Values are a custom map as needed by getContextMenu.
@@ -98,11 +118,6 @@ public class BaseView implements IViewRule {
return null;
}
- public Map<?, ?> getDefaultAttributes() {
- // The base rule does not have any custom default attributes.
- return null;
- }
-
// === Context Menu ===
/**
@@ -531,4 +546,26 @@ public class BaseView implements IViewRule {
return mChoices;
}
}
+
+ /**
+ * 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 String getSampleImageSrc() {
+ // For now, we point to the sample icon which is written into new Android projects
+ // created in ADT. We could alternatively look into the project resources folder
+ // and try to pick something else, or even return some builtin image resource
+ // in the @android namespace.
+
+ return "@drawable/icon"; //$NON-NLS-1$
+ }
+
+ public void onCreate(INode node, INode parent, InsertType insertType) {
+ }
+
+ public void onChildInserted(INode node, INode parent, InsertType insertType) {
+ }
}
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
new file mode 100644
index 0000000..54b511e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DialerFilterRule.java
@@ -0,0 +1,53 @@
+/*
+ * 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.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 BaseView {
+
+ @Override
+ public void onCreate(INode node, INode parent, InsertType insertType) {
+ super.onCreate(node, parent, insertType);
+
+ // A DialerFilter requires a couple of nested EditTexts with fixed ids:
+ if (insertType == InsertType.CREATE) {
+ INode hint = node.appendChild(FQCN_EDIT_TEXT);
+ hint.setAttribute(ANDROID_URI, BaseView.ATTR_TEXT, "Hint");
+ hint.setAttribute(ANDROID_URI, ATTR_ID, "@android:id/hint"); //$NON-NLS-1$
+ hint.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT);
+
+ INode primary = node.appendChild(FQCN_EDIT_TEXT);
+ primary.setAttribute(ANDROID_URI, BaseView.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, VALUE_FILL_PARENT);
+
+
+ // 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/HorizontalScrollViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java
new file mode 100644
index 0000000..547dc04
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java
@@ -0,0 +1,50 @@
+/*
+ * 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.INode;
+import com.android.ide.common.api.IViewRule;
+import com.android.ide.common.api.InsertType;
+
+/**
+ * An {@link IViewRule} for android.widget.HorizontalScrollView.
+ */
+public class HorizontalScrollViewRule extends BaseView {
+
+ @Override
+ public void onChildInserted(INode child, INode parent, InsertType insertType) {
+ super.onChildInserted(child, parent, insertType);
+
+ // The child of the ScrollView should fill in both directions
+ child.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT);
+ child.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_FILL_PARENT);
+ }
+
+ @Override
+ public void onCreate(INode node, INode parent, InsertType insertType) {
+ super.onCreate(node, parent, insertType);
+
+ if (insertType == InsertType.CREATE) {
+ // 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, LinearLayoutRule.ATTR_ORIENTATION,
+ LinearLayoutRule.VALUE_HORIZONTAL);
+ }
+ }
+
+}
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
new file mode 100644
index 0000000..7a23f17
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageButtonRule.java
@@ -0,0 +1,37 @@
+/*
+ * 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.INode;
+import com.android.ide.common.api.IViewRule;
+import com.android.ide.common.api.InsertType;
+
+/**
+ * An {@link IViewRule} for android.widget.ImageButtonRule.
+ */
+public class ImageButtonRule extends BaseView {
+
+ @Override
+ public void onCreate(INode node, INode parent, InsertType insertType) {
+ super.onCreate(node, parent, insertType);
+
+ if (insertType == InsertType.CREATE) {
+ 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
new file mode 100644
index 0000000..67cd422
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageViewRule.java
@@ -0,0 +1,37 @@
+/*
+ * 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.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 BaseView {
+
+ @Override
+ public void onCreate(INode node, INode parent, InsertType insertType) {
+ super.onCreate(node, parent, insertType);
+
+ if (insertType == InsertType.CREATE) {
+ node.setAttribute(ANDROID_URI, ATTR_SRC, getSampleImageSrc());
+ }
+ }
+
+}
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
index e5334f8..e31424c 100644
--- 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
@@ -24,10 +24,13 @@ 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.IViewRule;
+import com.android.ide.common.api.InsertType;
import com.android.ide.common.api.MenuAction;
import com.android.ide.common.api.Point;
import com.android.ide.common.api.Rect;
+import com.android.ide.common.api.IViewMetadata.FillPreference;
import java.util.ArrayList;
import java.util.List;
@@ -47,34 +50,57 @@ public class LinearLayoutRule extends BaseLayout {
*/
@Override
public List<MenuAction> getContextMenu(final INode selectedNode) {
+ if (supportsOrientation()) {
+ String curr_orient = selectedNode.getStringAttr(ANDROID_URI, ATTR_ORIENTATION);
+ if (curr_orient == null || curr_orient.length() == 0) {
+ curr_orient = VALUE_HORIZONTAL;
+ }
- String curr_orient = selectedNode.getStringAttr(ANDROID_URI, ATTR_ORIENTATION);
- if (curr_orient == null || curr_orient.length() == 0) {
- curr_orient = VALUE_HORIZONTAL;
+ IMenuCallback onChange = new IMenuCallback() {
+ public void action(MenuAction action, final String valueId, Boolean newValue) {
+ String actionId = action.getId();
+ final INode node = selectedNode;
+
+ if (actionId.equals("_orientation")) { //$NON-NLS-1$
+ node.editXml("Change LinearLayout " + ATTR_ORIENTATION, new INodeHandler() {
+ public void handle(INode n) {
+ node.setAttribute(ANDROID_URI, ATTR_ORIENTATION, valueId);
+ }
+ });
+ }
+ }
+ };
+
+ return concatenate(super.getContextMenu(selectedNode),
+ new MenuAction.Choices("_orientation", "Orientation", //$NON-NLS-1$
+ mapify(
+ "horizontal", "Horizontal", //$NON-NLS-1$
+ "vertical", "Vertical" //$NON-NLS-1$
+ ),
+ curr_orient, onChange));
+ } else {
+ return super.getContextMenu(selectedNode);
}
+ }
- IMenuCallback onChange = new IMenuCallback() {
- public void action(MenuAction action, final String valueId, Boolean newValue) {
- String actionId = action.getId();
- final INode node = selectedNode;
+ /**
+ * 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));
+ }
- if (actionId.equals("_orientation")) { //$NON-NLS-1$
- node.editXml("Change LinearLayout " + ATTR_ORIENTATION, new INodeHandler() {
- public void handle(INode n) {
- node.setAttribute(ANDROID_URI, ATTR_ORIENTATION, valueId);
- }
- });
- }
- }
- };
-
- return concatenate(super.getContextMenu(selectedNode),
- new MenuAction.Choices("_orientation", "Orientation", //$NON-NLS-1$
- mapify(
- "horizontal", "Horizontal", //$NON-NLS-1$
- "vertical", "Vertical" //$NON-NLS-1$
- ),
- curr_orient, onChange));
+ /**
+ * Returns true if this LinearLayout supports switching orientation.
+ *
+ * @return true if this layout supports orientations
+ */
+ protected boolean supportsOrientation() {
+ return true;
}
// ==== Drag'n'drop support ====
@@ -91,8 +117,7 @@ public class LinearLayoutRule extends BaseLayout {
return null;
}
- boolean isVertical = VALUE_VERTICAL.equals(targetNode.getStringAttr(ANDROID_URI,
- ATTR_ORIENTATION));
+ boolean isVertical = isVertical(targetNode);
// Prepare a list of insertion points: X coords for horizontal, Y for
// vertical.
@@ -247,11 +272,28 @@ public class LinearLayoutRule extends BaseLayout {
gc.useStyle(DrawingStyle.DROP_PREVIEW);
for (IDragElement element : elements) {
Rect bounds = element.getBounds();
- if (bounds.isValid() && (bounds.w > b.w || bounds.h > b.h)) {
+ 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.
- Rect within = new Rect(b.x, b.y,
- Math.min(bounds.w, b.w), Math.min(bounds.h, b.h));
+ // 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);
@@ -369,6 +411,25 @@ public class LinearLayoutRule extends BaseLayout {
});
}
+ @Override
+ public void onChildInserted(INode node, INode parent, InsertType insertType) {
+ // 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();
+ if (fill.fillHorizontally(vertical)) {
+ node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT);
+ }
+ if (fill.fillVertically(vertical)) {
+ node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_FILL_PARENT);
+ }
+ }
+ }
+
/** A possible match position */
private class MatchPos {
/** The pixel distance */
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
index 67a3cf3..347057a 100755
--- 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
@@ -16,10 +16,9 @@
package com.android.ide.common.layout;
+import com.android.ide.common.api.INode;
import com.android.ide.common.api.IViewRule;
-
-import java.util.HashMap;
-import java.util.Map;
+import com.android.ide.common.api.InsertType;
/**
* An {@link IViewRule} for android.widget.ListView and all its derived classes.
@@ -28,19 +27,10 @@ import java.util.Map;
*/
public class ListViewRule extends BaseView {
- // ==== XML Creation ====
-
- /**
- * The default for new views is to be wrap_content on both width/height.
- * However ListView is special in that ideally we want fill_parent width by
- * default.
- */
@Override
- public Map<?, ?> getDefaultAttributes() {
- // TODO: find a way to plug in the new value VALUE_MATCH_PARENT.
- Map<String, String> result = new HashMap<String, String>();
- result.put(ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT);
- return result;
- }
+ public void onCreate(INode node, INode parent, InsertType insertType) {
+ super.onCreate(node, parent, insertType);
+ node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT);
+ }
}
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
new file mode 100644
index 0000000..2eff369
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MapViewRule.java
@@ -0,0 +1,42 @@
+/*
+ * 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.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 BaseView {
+
+ @Override
+ public void onCreate(INode node, INode parent, InsertType insertType) {
+ super.onCreate(node, parent, insertType);
+
+ if (insertType == InsertType.CREATE) {
+ 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/ScrollViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java
new file mode 100644
index 0000000..5ce8bad
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java
@@ -0,0 +1,49 @@
+/*
+ * 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.INode;
+import com.android.ide.common.api.IViewRule;
+import com.android.ide.common.api.InsertType;
+
+/**
+ * An {@link IViewRule} for android.widget.ScrollView.
+ */
+public class ScrollViewRule extends BaseView {
+
+ @Override
+ public void onChildInserted(INode child, INode parent, InsertType insertType) {
+ super.onChildInserted(child, parent, insertType);
+
+ // The child of the ScrollView should fill in both directions
+ child.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT);
+ child.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_FILL_PARENT);
+ }
+
+ @Override
+ public void onCreate(INode node, INode parent, InsertType insertType) {
+ super.onCreate(node, parent, insertType);
+
+ if (insertType == InsertType.CREATE) {
+ // 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.
+ node.appendChild(FQCN_LINEAR_LAYOUT);
+ }
+ }
+
+}
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
new file mode 100644
index 0000000..b6d184f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SeekBarRule.java
@@ -0,0 +1,37 @@
+/*
+ * 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.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 BaseView {
+
+ @Override
+ public void onCreate(INode node, INode parent, 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, VALUE_FILL_PARENT);
+ }
+
+}
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
index 4885bb9..f47da68 100755
--- 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
@@ -16,7 +16,9 @@
package com.android.ide.common.layout;
+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.
@@ -26,6 +28,32 @@ public class TabHostRule extends IgnoredLayoutRule {
// manipulate its children via the TabHost rather than directly manipulating
// the child elements yourself, e.g. via addTab() etc.
- // TODO: We should add a context menu here to add tabs. We should also add
- // creation code to pre-populate a tab
+ @Override
+ public void onCreate(INode node, INode parent, InsertType insertType) {
+ super.onCreate(node, parent, insertType);
+
+ if (insertType == InsertType.CREATE) {
+ // 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, VALUE_FILL_PARENT);
+ node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_FILL_PARENT);
+
+ INode linear = node.appendChild(FQCN_LINEAR_LAYOUT);
+ linear.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT);
+ linear.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_FILL_PARENT);
+ linear.setAttribute(ANDROID_URI, LinearLayoutRule.ATTR_ORIENTATION,
+ LinearLayoutRule.VALUE_VERTICAL);
+
+ INode tab = linear.appendChild(FQCN_TAB_WIDGET);
+ tab.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT);
+ 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, VALUE_FILL_PARENT);
+ frame.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_FILL_PARENT);
+ frame.setAttribute(ANDROID_URI, ATTR_ID, "@android:id/tabcontent"); //$NON-NLS-1$
+ }
+ }
+
}
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
new file mode 100644
index 0000000..a4d528c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java
@@ -0,0 +1,68 @@
+/*
+ * 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.IMenuCallback;
+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.MenuAction;
+
+import java.util.List;
+
+/**
+ * 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
+
+ @Override
+ protected boolean isVertical(INode node) {
+ // Tables are always vertical
+ return true;
+ }
+
+ @Override
+ protected boolean supportsOrientation() {
+ return false;
+ }
+
+ @Override
+ public void onChildInserted(INode node, INode parent, 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 List<MenuAction> getContextMenu(final INode selectedNode) {
+ IMenuCallback addTab = new IMenuCallback() {
+ public void action(MenuAction action, final String valueId, Boolean newValue) {
+ final INode node = selectedNode;
+ node.appendChild(FQCN_TABLE_ROW);
+
+ }
+ };
+ return concatenate(super.getContextMenu(selectedNode),
+ new MenuAction.Action("_addrow", "Add Row", //$NON-NLS-1$
+ null, addTab));
+ }
+
+}
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
new file mode 100644
index 0000000..e734f4a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableRowRule.java
@@ -0,0 +1,43 @@
+/*
+ * 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.INode;
+import com.android.ide.common.api.IViewRule;
+import com.android.ide.common.api.InsertType;
+
+/**
+ * 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(INode child, INode parent, 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.
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java
index 04abe34..9c31b11 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java
@@ -127,10 +127,10 @@ public class AndroidConstants {
/** Absolute path of the workspace root, i.e. "/" */
public final static String WS_ROOT = WS_SEP;
- /** Absolute path of the resource folder, eg "/res".<br> This is a workspace path. */
+ /** Absolute path of the resource folder, e.g. "/res".<br> This is a workspace path. */
public final static String WS_RESOURCES = WS_SEP + SdkConstants.FD_RESOURCES;
- /** Absolute path of the resource folder, eg "/assets".<br> This is a workspace path. */
+ /** Absolute path of the resource folder, e.g. "/assets".<br> This is a workspace path. */
public final static String WS_ASSETS = WS_SEP + SdkConstants.FD_ASSETS;
/** Leaf of the javaDoc folder. Does not start with a separator. */
@@ -212,7 +212,7 @@ public class AndroidConstants {
public final static String MARKER_ATTR_TYPE_PROVIDER = "provider"; //$NON-NLS-1$
/**
- * Prefered compiler level, i.e. "1.5".
+ * Preferred compiler level, i.e. "1.5".
*/
public final static String COMPILER_COMPLIANCE_PREFERRED = "1.5"; //$NON-NLS-1$
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java
index a899bba..0c10363 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java
@@ -198,7 +198,7 @@ public class IconFactory {
gc.setAntialias(SWT.ON);
gc.setTextAntialias(SWT.ON);
- // image.setBackground() does not appear to have any affect; we must explicitly
+ // image.setBackground() does not appear to have any effect; we must explicitly
// paint into the image the background color we want masked out later.
// HOWEVER, alpha transparency does not work; we only get to mark a single color
// as transparent. You might think we could pick a system color (to avoid having
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/IGraphicalLayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/IGraphicalLayoutEditor.java
index 3a22b64..c200e47 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/IGraphicalLayoutEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/IGraphicalLayoutEditor.java
@@ -22,6 +22,7 @@ import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
import com.android.layoutlib.api.LayoutScene;
import org.eclipse.core.resources.IFile;
+import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.ui.IEditorPart;
import java.util.Set;
@@ -96,8 +97,10 @@ public interface IGraphicalLayoutEditor extends IEditorPart {
*
* @param model the model to be rendered, which can be different than the editor's own
* {@link #getModel()}.
- * @param width the width to use for the layout
- * @param height the height to use for the layout
+ * @param width the width to use for the layout, or -1 to use the width of the screen
+ * associated with this editor
+ * @param height the height to use for the layout, or -1 to use the height of the screen
+ * associated with this editor
* @param explodeNodes a set of nodes to explode, or null for none
* @param transparentBackground If true, the rendering will <b>not</b> paint the
* normal background requested by the theme, and it will instead paint the
@@ -107,4 +110,10 @@ public interface IGraphicalLayoutEditor extends IEditorPart {
abstract LayoutScene render(UiDocumentNode model,
int width, int height, Set<UiElementNode> explodeNodes, boolean transparentBackground);
+ /**
+ * Returns the current bounds of the Android device screen, in canvas control pixels
+ *
+ * @return the bounds of the screen, never null
+ */
+ abstract Rectangle getScreenBounds();
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java
index ed8a205..41bf099 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java
@@ -28,6 +28,7 @@ import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewEleme
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.OutlinePage2;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.PropertySheetPage2;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
@@ -202,7 +203,7 @@ public class LayoutEditor extends AndroidXmlEditor implements IShowEditorInput,
// Optional: set the default page. Eventually a default page might be
// restored by selectDefaultPage() later based on the last page used by the user.
// For example, to make the last page the default one (rather than the first page),
- // un-comment this line:
+ // uncomment this line:
// setActivePage(getPageCount() - 1);
}
@@ -604,4 +605,17 @@ public class LayoutEditor extends AndroidXmlEditor implements IShowEditorInput,
return null;
}
+
+ /**
+ * Returns the {@link RulesEngine} associated with this editor
+ *
+ * @return the {@link RulesEngine} associated with this editor, or null
+ */
+ public RulesEngine getRulesEngine() {
+ if (mGraphicalEditor instanceof GraphicalEditorPart) {
+ return ((GraphicalEditorPart) mGraphicalEditor).getRulesEngine();
+ }
+
+ return null;
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java
index 2da2467..303c58d 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java
@@ -335,7 +335,7 @@ public class CanvasViewInfo implements IPropertySource {
String fqcn = SimpleXmlTransfer.getFqcn(uiNode.getDescriptor());
String parentFqcn = null;
- Rect bounds = new Rect(getAbsRect());
+ Rect bounds = SwtUtils.toRect(getAbsRect());
Rect parentBounds = null;
UiElementNode uiParent = uiNode.getUiParent();
@@ -343,7 +343,7 @@ public class CanvasViewInfo implements IPropertySource {
parentFqcn = SimpleXmlTransfer.getFqcn(uiParent.getDescriptor());
}
if (getParent() != null) {
- parentBounds = new Rect(getParent().getAbsRect());
+ parentBounds = SwtUtils.toRect(getParent().getAbsRect());
}
SimpleElement e = new SimpleElement(fqcn, parentFqcn, bounds, parentBounds);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java
index 66f2327..a514447 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java
@@ -179,6 +179,9 @@ import java.util.regex.Pattern;
contrib = createDynamicChoices(
(MenuAction.Choices)firstAction, choiceMap, actionsMap);
}
+ } else {
+ // Must be a plain action
+ contrib = createDynamicAction(firstAction, actionsMap);
}
if (contrib != null) {
@@ -328,7 +331,7 @@ import java.util.regex.Pattern;
mEditor.wrapUndoEditXmlModel(label, new Runnable() {
public void run() {
- // Invoke the closures of all the actions using the same action-id
+ // Invoke the callbacks of all the actions using the same action-id
for (MenuAction a2 : actions) {
if (a2 instanceof MenuAction.Action) {
IMenuCallback c = ((MenuAction.Action) a2).getCallback();
@@ -354,6 +357,66 @@ import java.util.regex.Pattern;
/**
* Invoked by {@link #populateDynamicContextMenu()} to create a new menu item
+ * for a plain action. This is nearly identical to {@link #createDynamicMenuToggle},
+ * except for the {@link IAction} type and the removal of setChecked, isChecked, etc.
+ *
+ * @param firstAction The action to convert to a menu item. In the case of a
+ * multiple selection, this is the first of many similar actions.
+ * @param actionsMap Map of all contributed actions.
+ * @return a new {@link IContributionItem} to add to the context menu
+ */
+ private IContributionItem createDynamicAction(
+ final MenuAction.Action firstAction,
+ final TreeMap<String, ArrayList<MenuAction>> actionsMap) {
+
+ Action a = new Action(firstAction.getTitle(), IAction.AS_PUSH_BUTTON) {
+ @Override
+ public void run() {
+ final List<MenuAction> actions = actionsMap.get(firstAction.getId());
+ if (actions == null || actions.isEmpty()) {
+ return;
+ }
+
+ String label = actions.get(0).getTitle();
+ if (actions.size() > 1) {
+ label += String.format(" (%d elements)", actions.size());
+ }
+
+ if (mEditor.isEditXmlModelPending()) {
+ // This should not be happening.
+ logError("Action '%s' failed: XML changes pending, document might be corrupt.", //$NON-NLS-1$
+ label);
+ return;
+ }
+
+ mEditor.wrapUndoEditXmlModel(label, new Runnable() {
+ public void run() {
+ // Invoke the callbacks of all the actions using the same action-id
+ for (MenuAction a2 : actions) {
+ if (a2 instanceof MenuAction.Action) {
+ IMenuCallback c = ((MenuAction.Action) a2).getCallback();
+ if (c != null) {
+ try {
+ // Values do not apply for plain actions
+ c.action(a2, null /* valueId */, null /* newValue */);
+ } catch (Exception e) {
+ RulesEngine gre = mCanvas.getRulesEngine();
+ gre.logError("XML edit operation failed: %s", e.toString());
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+ };
+ a.setId(firstAction.getId());
+
+ return new ActionContributionItem(a);
+ }
+
+ /**
+ * Invoked by {@link #populateDynamicContextMenu()} to create a new menu item
* for a {@link MenuAction.Choices}.
* <p/>
* Multiple-choices are represented by a sub-menu containing checked items.
@@ -429,7 +492,7 @@ import java.util.regex.Pattern;
}
// We consider the item to be checked if all actions are all checked.
- // This means a mixed item will be first toggled from off to on by all the closures.
+ // This means a mixed item will be first toggled from off to on by all the callbacks.
final boolean isChecked = numOff == 0 && numOn > 0;
boolean isMixed = numOff > 0 && numOn > 0;
@@ -456,7 +519,7 @@ import java.util.regex.Pattern;
mEditor.wrapUndoEditXmlModel(label, new Runnable() {
public void run() {
- // Invoke the closures of all the actions using the same action-id
+ // Invoke the callbacks of all the actions using the same action-id
for (MenuAction a2 : actions) {
if (a2 instanceof MenuAction.Action) {
try {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
index 73711aa..728c089 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
@@ -899,7 +899,7 @@ public class GraphicalEditorPart extends EditorPart
}
if (mRulesEngine == null) {
- mRulesEngine = new RulesEngine(mEditedFile.getProject());
+ mRulesEngine = new RulesEngine(this, mEditedFile.getProject());
if (mCanvasViewer != null) {
mCanvasViewer.getCanvas().setRulesEngine(mRulesEngine);
}
@@ -949,6 +949,15 @@ public class GraphicalEditorPart extends EditorPart
return mLayoutEditor;
}
+ /**
+ * Returns the {@link RulesEngine} associated with this editor
+ *
+ * @return the {@link RulesEngine} associated with this editor, never null
+ */
+ public RulesEngine getRulesEngine() {
+ return mRulesEngine;
+ }
+
/* package */ LayoutCanvas getCanvasControl() {
if (mCanvasViewer != null) {
return mCanvasViewer.getCanvas();
@@ -1174,7 +1183,7 @@ public class GraphicalEditorPart extends EditorPart
Set<UiElementNode> explodeNodes = canvas.getNodesToExplode();
// Compute the layout
- Rectangle rect = getBounds();
+ Rectangle rect = getScreenBounds();
int width = rect.width;
int height = rect.height;
@@ -1355,7 +1364,7 @@ public class GraphicalEditorPart extends EditorPart
// FIXME: get rid of the current LayoutScene if any.
}
- public Rectangle getBounds() {
+ public Rectangle getScreenBounds() {
return mConfigComposite.getScreenBounds();
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java
index c11d00b..d36b96c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java
@@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
import com.android.ide.common.api.DropFeedback;
import com.android.ide.common.api.INode;
+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.eclipse.adt.AdtPlugin;
@@ -316,10 +317,19 @@ public class MoveGesture extends DropGesture {
String label = computeUndoLabel(mTargetNode, elements, event.detail);
mCanvas.getLayoutEditor().wrapUndoEditXmlModel(label, new Runnable() {
public void run() {
+ InsertType insertType;
+ if (event.detail == DND.DROP_MOVE) {
+ insertType = InsertType.MOVE;
+ } else if (GlobalCanvasDragInfo.getInstance().getSourceCanvas() != null) {
+ insertType = InsertType.PASTE;
+ } else {
+ insertType = InsertType.CREATE;
+ }
mCanvas.getRulesEngine().callOnDropped(mTargetNode,
elementsFinal,
mFeedback,
- new Point(canvasPoint.x, canvasPoint.y));
+ new Point(canvasPoint.x, canvasPoint.y),
+ insertType);
// Clean up drag if applicable
if (event.detail == DND.DROP_MOVE) {
GlobalCanvasDragInfo.getInstance().removeSource();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java
index c249981..6b46688 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java
@@ -16,15 +16,21 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
+import com.android.ide.common.api.InsertType;
import com.android.ide.common.api.Rect;
+import com.android.ide.common.layout.BaseView;
import com.android.ide.common.layoutlib.LayoutLibrary;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.IGraphicalLayoutEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutConstants;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeFactory;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
+import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
@@ -60,10 +66,13 @@ import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;
+import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
@@ -548,11 +557,18 @@ public class PaletteComposite extends Composite {
// not to make it much larger than necessary since to crop this we rely on
// actually scanning pixels.
- /** Width of the rendered preview image (before it is cropped) */
- private static final int RENDER_HEIGHT = 400;
+ /**
+ * Width of the rendered preview image (before it is cropped), although the actual
+ * width may be smaller (since we also take the device screen's size into account)
+ */
+ private static final int MAX_RENDER_HEIGHT = 400;
- /** Height of the rendered preview image (before it is cropped) */
- private static final int RENDER_WIDTH = 500;
+ /**
+ * Height of the rendered preview image (before it is cropped), although the
+ * actual width may be smaller (since we also take the device screen's size into
+ * account)
+ */
+ private static final int MAX_RENDER_WIDTH = 500;
/** Amount of alpha to multiply into the image (divided by 256) */
private static final int IMG_ALPHA = 216;
@@ -655,6 +671,8 @@ public class PaletteComposite extends Composite {
Document document = null;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
+ factory.setNamespaceAware(true);
+ factory.setValidating(false);
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.newDocument();
} catch (ParserConfigurationException e) {
@@ -668,6 +686,13 @@ public class PaletteComposite extends Composite {
String viewName = desc.getXmlLocalName();
Element element = document.createElement(viewName);
+
+ // Set up a proper name space
+ Attr attr = document.createAttributeNS(XmlnsAttributeDescriptor.XMLNS_URI,
+ "xmlns:android"); //$NON-NLS-1$
+ attr.setValue(BaseView.ANDROID_URI);
+ element.getAttributes().setNamedItemNS(attr);
+
element.setAttributeNS(SdkConstants.NS_RESOURCES,
LayoutConstants.ATTR_LAYOUT_WIDTH, LayoutConstants.VALUE_WRAP_CONTENT);
element.setAttributeNS(SdkConstants.NS_RESOURCES,
@@ -679,15 +704,37 @@ public class PaletteComposite extends Composite {
String text = DescriptorsUtils.getFreeWidgetId(uiRoot, viewName);
element.setAttributeNS(SdkConstants.NS_RESOURCES, LayoutConstants.ATTR_TEXT, text);
- document.insertBefore(element, null);
+ document.appendChild(element);
// Construct UI model from XML
- DocumentDescriptor documentDescriptor =
- new DocumentDescriptor("layout_doc", null); //$NON-NLS-1$
- UiDocumentNode model = new UiDocumentNode(documentDescriptor);
+ AndroidTargetData data = layoutEditor.getTargetData();
+ DocumentDescriptor documentDescriptor;
+ if (data == null) {
+ documentDescriptor = new DocumentDescriptor("temp", null /*children*/); //$NON-NLS-1$
+ } else {
+ documentDescriptor = data.getLayoutDescriptors().getDescriptor();
+ }
+ UiDocumentNode model = (UiDocumentNode) documentDescriptor.createUiNode();
model.setEditor(layoutEditor);
+ model.setUnknownDescriptorProvider(editor.getModel().getUnknownDescriptorProvider());
model.loadFromXmlNode(document);
+ // Call the create-hooks such that we for example insert mandatory
+ // children into views like the DialerFilter, apply image source attributes
+ // to ImageButtons, etc.
+ if (editor instanceof GraphicalEditorPart) {
+ LayoutCanvas canvas = ((GraphicalEditorPart) editor).getCanvasControl();
+ NodeFactory nodeFactory = canvas.getNodeFactory();
+ UiElementNode parent = model.getUiRoot();
+ UiElementNode child = parent.getUiChildren().get(0);
+ if (child instanceof UiViewElementNode) {
+ UiViewElementNode childUiNode = (UiViewElementNode) child;
+ NodeProxy childNode = nodeFactory.create(childUiNode);
+ canvas.getRulesEngine().callCreateHooks(layoutEditor,
+ null, childNode, InsertType.CREATE);
+ }
+ }
+
boolean hasTransparency = false;
LayoutLibrary layoutLibrary = editor.getLayoutLibrary();
if (layoutLibrary != null) {
@@ -697,8 +744,23 @@ public class PaletteComposite extends Composite {
}
}
- LayoutScene scene = editor.render(model, RENDER_WIDTH, RENDER_HEIGHT,
+ LayoutScene scene = null;
+ try {
+ // Use at most the size of the screen for the preview render.
+ // This is important since when we fill the size of certain views (like
+ // a SeekBar), we want it to at most be the width of the screen, and for small
+ // screens the RENDER_WIDTH was wider.
+ org.eclipse.draw2d.geometry.Rectangle screenBounds = editor.getScreenBounds();
+ int renderWidth = Math.min(screenBounds.width, MAX_RENDER_WIDTH);
+ int renderHeight = Math.min(screenBounds.height, MAX_RENDER_HEIGHT);
+
+ scene = editor.render(model, renderWidth, renderHeight,
null /* explodeNodes */, hasTransparency);
+ } catch (Throwable t) {
+ // Previews can fail for a variety of reasons -- let's not bug
+ // the user with it
+ return null;
+ }
if (scene != null) {
if (scene.getResult() == SceneResult.SUCCESS) {
@@ -761,6 +823,41 @@ public class PaletteComposite extends Composite {
}
/**
+ * Utility method to print out the contents of the given XML document. This is
+ * really useful when working on the preview code above. I'm including all the
+ * code inside a constant false, which means the compiler will omit all the code,
+ * but I'd like to leave it in the code base and by doing it this way rather than
+ * as commented out code the code won't be accidentally broken.
+ */
+ @SuppressWarnings("all")
+ private static void dumpDocument(Document document) {
+ // Diagnostics: print out the XML that we're about to render
+ if (false) { // Will be omitted by the compiler
+ org.apache.xml.serialize.OutputFormat outputFormat =
+ new org.apache.xml.serialize.OutputFormat(
+ "XML", "ISO-8859-1", true); //$NON-NLS-1$ //$NON-NLS-2$
+ outputFormat.setIndent(2);
+ outputFormat.setLineWidth(100);
+ outputFormat.setIndenting(true);
+ outputFormat.setOmitXMLDeclaration(true);
+ outputFormat.setOmitDocumentType(true);
+ StringWriter stringWriter = new StringWriter();
+ // Using FQN here to avoid having an import above, which will result
+ // in a deprecation warning, and there isn't a way to annotate a single
+ // import element with a SuppressWarnings.
+ org.apache.xml.serialize.XMLSerializer serializer =
+ new org.apache.xml.serialize.XMLSerializer(stringWriter, outputFormat);
+ serializer.setNamespaces(true);
+ try {
+ serializer.serialize(document.getDocumentElement());
+ System.out.println(stringWriter.toString());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
* Returns the image shown as the drag source effect. The image may be a preview
* of the palette item, or just a placeholder image; {@link #isPlaceholder()} can
* tell the difference.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java
index 6c55dd3..c2c46f1 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java
@@ -15,9 +15,12 @@
*/
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
+import com.android.ide.common.api.Rect;
+
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import java.awt.image.BufferedImage;
@@ -109,4 +112,38 @@ public class SwtUtils {
return awtImage;
}
+
+ /**
+ * Converts the given SWT {@link Rectangle} into an ADT {@link Rect}
+ *
+ * @param swtRect the SWT {@link Rectangle}
+ * @return an equivalent {@link Rect}
+ */
+ public static Rect toRect(Rectangle swtRect) {
+ return new Rect(swtRect.x, swtRect.y, swtRect.width, swtRect.height);
+ }
+
+ /**
+ * Sets the values of the given ADT {@link Rect} to the values of the given SWT
+ * {@link Rectangle}
+ *
+ * @param target the ADT {@link Rect} to modify
+ * @param source the SWT {@link Rectangle} to read values from
+ */
+ public static void set(Rect target, Rectangle source) {
+ target.set(source.x, source.y, source.width, source.height);
+ }
+
+ /**
+ * Compares an ADT {@link Rect} with an SWT {@link Rectangle} and returns true if they
+ * are equivalent
+ *
+ * @param r1 the ADT {@link Rect}
+ * @param r2 the SWT {@link Rectangle}
+ * @return true if the two rectangles are equivalent
+ */
+ public static boolean equals(Rect r1, Rectangle r2) {
+ return r1.x == r2.x && r1.y == r2.y && r1.w == r2.width && r1.h == r2.height;
+
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactory.java
index 825a0e4..e608377 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactory.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactory.java
@@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gre;
import com.android.ide.common.api.INode;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SwtUtils;
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
import org.eclipse.swt.graphics.Rectangle;
@@ -67,7 +68,7 @@ public class NodeFactory {
proxy = new NodeProxy(uiNode, bounds, this);
mNodeMap.put(uiNode, proxy);
- } else if (bounds != null && !proxy.getBounds().equals(bounds)) {
+ } else if (bounds != null && !SwtUtils.equals(proxy.getBounds(), bounds)) {
// Update the bounds if necessary
proxy.setBounds(bounds);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
index 3916eaa..59c6e15 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
@@ -29,6 +29,7 @@ import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescripto
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SimpleAttribute;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SwtUtils;
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
@@ -71,7 +72,7 @@ public class NodeProxy implements INode {
if (bounds == null) {
mBounds = new Rect();
} else {
- mBounds = new Rect(bounds);
+ mBounds = SwtUtils.toRect(bounds);
}
}
@@ -85,7 +86,7 @@ public class NodeProxy implements INode {
* This is a package-protected method, only the {@link NodeFactory} uses this method.
*/
/*package*/ void setBounds(Rectangle bounds) {
- mBounds.set(bounds);
+ SwtUtils.set(mBounds, bounds);
}
/**
@@ -195,38 +196,14 @@ public class NodeProxy implements INode {
}
public INode appendChild(String viewFqcn) {
- checkEditOK();
-
- // Find the descriptor for this FQCN
- ViewElementDescriptor vd = getFqcnViewDescriptor(viewFqcn);
- if (vd == null) {
- warnPrintf("Can't create a new %s element", viewFqcn);
- return null;
- }
-
- // Append at the end.
- UiElementNode uiNew = mNode.appendNewUiChild(vd);
-
- // TODO we probably want to defer that to the GRE to use IViewRule#getDefaultAttributes()
- DescriptorsUtils.setDefaultLayoutAttributes(uiNew, false /*updateLayout*/);
-
- Node xmlNode = uiNew.createXmlNode();
-
- if (!(uiNew instanceof UiViewElementNode) || xmlNode == null) {
- // Both things are not supposed to happen. When they do, we're in big trouble.
- // We don't really know how to revert the state at this point and the UI model is
- // now out of sync with the XML model.
- // Panic ensues.
- // The best bet is to abort now. The edit wrapper will release the edit and the
- // XML/UI should get reloaded properly (with a likely invalid XML.)
- warnPrintf("Failed to create a new %s element", viewFqcn);
- throw new RuntimeException("XML node creation failed."); //$NON-NLS-1$
- }
-
- return mFactory.create((UiViewElementNode) uiNew);
+ return insertOrAppend(viewFqcn, -1);
}
public INode insertChildAt(String viewFqcn, int index) {
+ return insertOrAppend(viewFqcn, index);
+ }
+
+ private INode insertOrAppend(String viewFqcn, int index) {
checkEditOK();
// Find the descriptor for this FQCN
@@ -236,13 +213,18 @@ public class NodeProxy implements INode {
return null;
}
- // Insert at the requested position or at the end.
- int n = mNode.getUiChildren().size();
- UiElementNode uiNew = null;
- if (index < 0 || index >= n) {
+ final UiElementNode uiNew;
+ if (index == -1) {
+ // Append at the end.
uiNew = mNode.appendNewUiChild(vd);
} else {
- uiNew = mNode.insertNewUiChild(index, vd);
+ // Insert at the requested position or at the end.
+ int n = mNode.getUiChildren().size();
+ if (index < 0 || index >= n) {
+ uiNew = mNode.appendNewUiChild(vd);
+ } else {
+ uiNew = mNode.insertNewUiChild(index, vd);
+ }
}
// TODO we probably want to defer that to the GRE to use IViewRule#getDefaultAttributes()
@@ -261,7 +243,18 @@ public class NodeProxy implements INode {
throw new RuntimeException("XML node creation failed."); //$NON-NLS-1$
}
- return mFactory.create((UiViewElementNode) uiNew);
+ UiViewElementNode uiNewView = (UiViewElementNode) uiNew;
+ NodeProxy newNode = mFactory.create(uiNewView);
+
+ AndroidXmlEditor editor = mNode.getEditor();
+ if (editor instanceof LayoutEditor) {
+ RulesEngine engine = ((LayoutEditor)editor).getRulesEngine();
+ if (engine != null) {
+ engine.callCreateHooks(editor, this, newNode, null);
+ }
+ }
+
+ return newNode;
}
public boolean setAttribute(String uri, String name, String value) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
index 7856cd9..a14d812 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
@@ -22,12 +22,16 @@ 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.IValidator;
+import com.android.ide.common.api.IViewMetadata;
import com.android.ide.common.api.IViewRule;
+import com.android.ide.common.api.InsertType;
import com.android.ide.common.api.MenuAction;
import com.android.ide.common.api.Point;
import com.android.ide.common.layout.ViewRule;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.IGraphicalLayoutEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SimpleElement;
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
@@ -63,6 +67,15 @@ import java.util.Map;
public class RulesEngine {
private final IProject mProject;
private final Map<Object, IViewRule> mRulesCache = new HashMap<Object, IViewRule>();
+ /**
+ * The type of any upcoming node manipulations performed by the {@link IViewRule}s.
+ * When actions are performed in the tool (like a paste action, or a drag from palette,
+ * or a drag move within the canvas, etc), these are different types of inserts,
+ * and we don't want to have the rules track them closely (and pass them back to us
+ * in the {@link INode#insertChildAt} methods etc), so instead we track the state
+ * here on behalf of the currently executing rule.
+ */
+ private InsertType mInsertType = InsertType.CREATE;
/**
* Class loader (or null) used to load user/project-specific IViewRule
@@ -77,14 +90,21 @@ public class RulesEngine {
private boolean mUserClassLoaderInited;
/**
+ * The editor which owns this {@link RulesEngine}
+ */
+ private IGraphicalLayoutEditor mEditor;
+
+ /**
* Creates a new {@link RulesEngine} associated with the selected project.
* <p/>
* The rules engine will look in the project for a tools jar to load custom view rules.
*
+ * @param editor the editor which owns this {@link RulesEngine}
* @param project A non-null open project.
*/
- public RulesEngine(IProject project) {
+ public RulesEngine(IGraphicalLayoutEditor editor, IProject project) {
mProject = project;
+ mEditor = editor;
}
/**
@@ -194,6 +214,7 @@ public class RulesEngine {
if (rule != null) {
try {
+ mInsertType = InsertType.CREATE;
return rule.getContextMenu(selectedNode);
} catch (Exception e) {
@@ -335,12 +356,14 @@ public class RulesEngine {
public void callOnDropped(NodeProxy targetNode,
IDragElement[] elements,
DropFeedback feedback,
- Point where) {
+ Point where,
+ InsertType insertType) {
// try to find a rule for this element's FQCN
IViewRule rule = loadRule(targetNode.getNode());
if (rule != null) {
try {
+ mInsertType = insertType;
rule.onDropped(targetNode, elements, feedback, where);
} catch (Exception e) {
@@ -379,6 +402,7 @@ public class RulesEngine {
if (rule != null) {
try {
+ mInsertType = InsertType.PASTE;
rule.onPaste(targetNode, pastedElements);
} catch (Exception e) {
@@ -389,6 +413,74 @@ public class RulesEngine {
}
}
+ // ---- Creation customizations ----
+
+ /**
+ * Invokes the create hooks ({@link IViewRule#onCreate},
+ * {@link IViewRule#onChildInserted} when a new child has been created/pasted/moved, and
+ * is inserted into a given parent. The parent may be null (for example when rendering
+ * top level items for preview).
+ *
+ * @param editor the XML editor to apply edits to the model for (performed by view
+ * rules)
+ * @param parentNode the parent XML node, or null if unknown
+ * @param childNode the XML node of the new node, never null
+ * @param overrideInsertType If not null, specifies an explicit insert type to use for
+ * edits made during the customization
+ */
+ public void callCreateHooks(
+ AndroidXmlEditor editor,
+ NodeProxy parentNode, NodeProxy childNode,
+ InsertType overrideInsertType) {
+ IViewRule parentRule = null;
+
+ if (parentNode != null) {
+ UiViewElementNode parentUiNode = parentNode.getNode();
+ parentRule = loadRule(parentUiNode);
+ }
+
+ if (overrideInsertType != null) {
+ mInsertType = overrideInsertType;
+ }
+
+ UiViewElementNode newUiNode = childNode.getNode();
+ IViewRule childRule = loadRule(newUiNode);
+ if (childRule != null || parentRule != null) {
+ callCreateHooks(editor, mInsertType, parentRule, parentNode,
+ childRule, childNode);
+ }
+ }
+
+ private static void callCreateHooks(
+ final AndroidXmlEditor editor, final InsertType insertType,
+ final IViewRule parentRule, final INode parentNode,
+ final IViewRule childRule, final INode newNode) {
+ // Notify the parent about the new child in case it wants to customize it
+ // (For example, a ScrollView parent can go and set all its children's layout params to
+ // fill the parent.)
+ if (!editor.isEditXmlModelPending()) {
+ editor.wrapUndoEditXmlModel("Customize creation", new Runnable() {
+ public void run() {
+ callCreateHooks(editor, insertType,
+ parentRule, parentNode, childRule, newNode);
+ }
+ });
+ return;
+ }
+
+ if (parentRule != null) {
+ parentRule.onChildInserted(newNode, parentNode, insertType);
+ }
+
+ // Look up corresponding IViewRule, and notify the rule about
+ // this create action in case it wants to customize the new object.
+ // (For example, a rule for TabHosts can go and create a default child tab
+ // when you create it.)
+ if (childRule != null) {
+ childRule.onCreate(newNode, parentNode, insertType);
+ }
+ }
+
// ---- private ---
/**
@@ -531,7 +623,11 @@ public class RulesEngine {
// than in the same package as the widgets.
String ruleClassName;
ClassLoader classLoader;
- if (realFqcn.startsWith("android.")) { //$NON-NLS-1$
+ if (realFqcn.startsWith("android.") || //$NON-NLS-1$
+ // FIXME: Remove this special case as soon as we pull
+ // the MapViewRule out of this code base and bundle it
+ // with the add ons
+ realFqcn.startsWith("com.google.android.maps.")) { //$NON-NLS-1$
// This doesn't handle a case where there are name conflicts
// (e.g. where there are multiple different views with the same
// class name and only differing in package names, but that's a
@@ -697,6 +793,9 @@ public class RulesEngine {
}
return null;
}
- }
+ public IViewMetadata getMetadata(final String fqcn) {
+ return new ViewMetadata(mEditor.getLayoutEditor(), fqcn);
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadata.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadata.java
new file mode 100644
index 0000000..eeebad7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadata.java
@@ -0,0 +1,131 @@
+/*
+ * 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.eclipse.adt.internal.editors.layout.gre;
+
+/**
+ * An implementation of {@link IViewMetadata} which consults the
+ * SDK descriptors to answer metadata questions about views.
+ */
+import com.android.ide.common.api.IViewMetadata;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.IGraphicalLayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+final class ViewMetadata implements IViewMetadata {
+ /** The {@link ElementDescriptor} for this view, computed lazily */
+ private ElementDescriptor mDescriptor;
+
+ /** The fully qualified class name of the view whose metadata this class represents */
+ private String mFqcn;
+
+ /** The {@link IGraphicalLayoutEditor} associated with the view we're looking up */
+ private LayoutEditor mEditor;
+
+ /**
+ * A map from class names to {@link FillPreference} which indicates how each view
+ * prefers to grow when added in various layout contexts
+ */
+ private static final Map<String,FillPreference> mFill = new HashMap<String,FillPreference>();
+ static {
+ // Hardcoded metadata about fill preferences for various known views. We should
+ // work to try to get this into the platform as designtime annotations.
+
+ mFill.put("android.widget.EditText", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
+ mFill.put("android.widget.DialerFilter", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
+ mFill.put("android.widget.SeekBar", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
+ mFill.put("android.widget.Spinner", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
+ mFill.put("android.widget.AutoComplete", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
+ mFill.put("android.widget.ListView", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
+ mFill.put("android.widget.GridView", FillPreference.OPPOSITE); //$NON-NLS-1$
+ mFill.put("android.widget.Gallery", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
+ mFill.put("android.widget.TabWidget", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
+ mFill.put("android.widget.MapView", FillPreference.OPPOSITE); //$NON-NLS-1$
+ mFill.put("android.widget.WebView", FillPreference.OPPOSITE); //$NON-NLS-1$
+
+ // In addition, all layouts are FillPreference.OPPOSITE - these are computed
+ // lazily rather than enumerating them here
+
+ // For any other view, the fallback fill preference is FillPreference.NONE.
+ }
+
+ public ViewMetadata(LayoutEditor editor, String fqcn) {
+ super();
+ mFqcn = fqcn;
+ mEditor = editor;
+ }
+
+ /** Lazily look up the descriptor for the FQCN of this metadata object */
+ private boolean findDescriptor() {
+ if (mDescriptor == null) {
+ // Look up the corresponding view element node. We don't need the graphical part;
+ // we just need the project context. Maybe I should extract this code into
+ // a utility.
+ mDescriptor = mEditor.getFqcnViewDescriptor(mFqcn);
+ }
+
+ return mDescriptor != null;
+ }
+
+ public boolean isParent() {
+ if (findDescriptor()) {
+ return mDescriptor.hasChildren();
+ }
+
+ return false;
+ }
+
+ public String getDisplayName() {
+ if (findDescriptor()) {
+ return mDescriptor.getUiName();
+ }
+
+ return mFqcn.substring(mFqcn.lastIndexOf('.') + 1); // This also works when there is no "."
+ }
+
+ public String getTooltip() {
+ if (findDescriptor()) {
+ return mDescriptor.getTooltip();
+ }
+
+ return null;
+ }
+
+ /** Returns true if this view represents a layout */
+ private boolean isLayout() {
+ if (findDescriptor()) {
+ return mDescriptor.hasChildren();
+ }
+ return false;
+ }
+
+ public FillPreference getFillPreference() {
+ FillPreference fillPreference = mFill.get(mFqcn);
+ if (fillPreference == null) {
+ if (isLayout()) {
+ fillPreference = FillPreference.OPPOSITE;
+ } else {
+ fillPreference = FillPreference.NONE;
+ }
+ mFill.put(mFqcn, fillPreference);
+ }
+
+ return fillPreference;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/RectTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/RectTest.java
index 043c9ee..36fa743 100755
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/RectTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/RectTest.java
@@ -16,11 +16,6 @@
package com.android.ide.common.api;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-
-import org.eclipse.swt.graphics.Rectangle;
-
import junit.framework.TestCase;
public class RectTest extends TestCase {
@@ -54,16 +49,6 @@ public class RectTest extends TestCase {
assertEquals(r2, r);
}
- public final void testRectRectangle() {
- Rectangle r = new Rectangle(3, 4, 20, 30);
- Rect r2 = new Rect(r);
-
- assertEquals(3, r2.x);
- assertEquals(4, r2.y);
- assertEquals(20, r2.w);
- assertEquals(30, r2.h);
- }
-
public final void testSetIntIntIntInt() {
Rect r = new Rect(1, 2, 3, 4);
Rect r2 = r.set(3, 4, 20, 30);
@@ -88,19 +73,6 @@ public class RectTest extends TestCase {
assertEquals(30, r.h);
}
- public final void testSetRectangle() {
- Rect r = new Rect(1, 2, 3, 4);
- Rectangle r2 = new Rectangle(3, 4, 20, 30);
- Rect r3 = r.set(r2);
-
- assertSame(r3, r);
- assertNotSame(r3, r2);
- assertEquals(3, r.x);
- assertEquals(4, r.y);
- assertEquals(20, r.w);
- assertEquals(30, r.h);
- }
-
public final void testCopy() {
Rect r = new Rect(1, 2, 3, 4);
Rect r2 = r.copy();
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java
index 2f4e22f..7853410 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java
@@ -54,10 +54,10 @@ public class AbsoluteLayoutRuleTest extends AbstractLayoutRuleTest {
"useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
// Drop preview
- "useStyle(DROP_PREVIEW), drawRect(Rect[30,-10,105,80])");
+ "useStyle(DROP_PREVIEW), drawRect(Rect[-22,-50,105,80])");
- assertEquals("30dip", inserted.getStringAttr(BaseLayout.ANDROID_URI, "layout_x"));
- assertEquals("-10dip", inserted.getStringAttr(BaseLayout.ANDROID_URI, "layout_y"));
+ assertEquals("-22dip", inserted.getStringAttr(BaseLayout.ANDROID_URI, "layout_x"));
+ assertEquals("-50dip", inserted.getStringAttr(BaseLayout.ANDROID_URI, "layout_y"));
// Without drag bounds we should just draw guide lines instead
inserted = dragInto(new Rect(0, 0, 0, 0), new Point(30, -10), 4, -1,
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java
index e383796..c575f46 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java
@@ -16,8 +16,11 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
+import com.android.ide.common.api.Rect;
+
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
@@ -134,4 +137,25 @@ public class SwtUtilsTest extends TestCase {
}
}
+ public final void testSetRectangle() {
+ Rect r = new Rect(1, 2, 3, 4);
+ Rectangle r2 = new Rectangle(3, 4, 20, 30);
+ SwtUtils.set(r, r2);
+
+ assertEquals(3, r.x);
+ assertEquals(4, r.y);
+ assertEquals(20, r.w);
+ assertEquals(30, r.h);
+ }
+
+ public final void testRectRectangle() {
+ Rectangle r = new Rectangle(3, 4, 20, 30);
+ Rect r2 = SwtUtils.toRect(r);
+
+ assertEquals(3, r2.x);
+ assertEquals(4, r2.y);
+ assertEquals(20, r2.w);
+ assertEquals(30, r2.h);
+ }
+
}