18fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael/*
28fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael * Copyright (C) 2009 The Android Open Source Project
38fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael *
48fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael * Licensed under the Eclipse Public License, Version 1.0 (the "License");
58fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael * you may not use this file except in compliance with the License.
68fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael * You may obtain a copy of the License at
78fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael *
88fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael *      http://www.eclipse.org/org/documents/epl-v10.php
98fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael *
108fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael * Unless required by applicable law or agreed to in writing, software
118fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael * distributed under the License is distributed on an "AS IS" BASIS,
128fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael * See the License for the specific language governing permissions and
148fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael * limitations under the License.
158fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael */
168fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael
178fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphaelpackage com.android.ide.eclipse.adt.internal.editors.layout.gle2;
188fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael
1912d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.FQCN_SPACE;
2012d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.FQCN_SPACE_V7;
2112d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.GESTURE_OVERLAY_VIEW;
2212d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.VIEW_MERGE;
239155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
2412d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport com.android.SdkConstants;
259bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbyeimport com.android.annotations.NonNull;
269bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbyeimport com.android.annotations.Nullable;
2780d9301c2e874b29889c41adb0623666cf534fa0Tor Norbyeimport com.android.ide.common.api.Margins;
2883dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbyeimport com.android.ide.common.api.Rect;
294eacdfbcc84ad11f599020b12ad76aebed70537fTor Norbyeimport com.android.ide.common.layout.GridLayoutRule;
308386da5e451eec396d0e71576e7366a98017674fTor Norbyeimport com.android.ide.common.rendering.api.Capability;
319155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbyeimport com.android.ide.common.rendering.api.MergeCookie;
3295b17a5e62eeeb7b38ef668508df43a1ee9e0880Xavier Ducrohetimport com.android.ide.common.rendering.api.ViewInfo;
3383dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
3423258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.UiElementPullParser;
358fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphaelimport com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
3683dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode;
3783dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
3885e4a1a9dd133abb879ec211ce8dd385004edf22Xavier Ducrohetimport com.android.utils.Pair;
398fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael
408fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphaelimport org.eclipse.swt.graphics.Rectangle;
41f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Mollimport org.eclipse.ui.views.properties.IPropertyDescriptor;
42f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Mollimport org.eclipse.ui.views.properties.IPropertySheetPage;
43f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Mollimport org.eclipse.ui.views.properties.IPropertySource;
4499fd7eee15c89fd45b884842c44371326f851930Tor Norbyeimport org.w3c.dom.Element;
4576bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbyeimport org.w3c.dom.Node;
468fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael
478fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphaelimport java.util.ArrayList;
489155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbyeimport java.util.Collections;
499155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbyeimport java.util.HashMap;
508386da5e451eec396d0e71576e7366a98017674fTor Norbyeimport java.util.LinkedList;
518386da5e451eec396d0e71576e7366a98017674fTor Norbyeimport java.util.List;
529155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbyeimport java.util.Map;
538fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael
548fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael/**
55f29be828de51dbe2f55508cd620142e35cd19cbdXavier Ducrohet * Maps a {@link ViewInfo} in a structure more adapted to our needs.
568fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael * The only large difference is that we keep both the original bounds of the view info
57f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll * and we pre-compute the selection bounds which are absolute to the rendered image
58f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll * (whereas the original bounds are relative to the parent view.)
598fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael * <p/>
60f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll * Each view also knows its parent and children.
618fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael * <p/>
62f29be828de51dbe2f55508cd620142e35cd19cbdXavier Ducrohet * We can't alter {@link ViewInfo} as it is part of the LayoutBridge and needs to
638fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael * have a fixed API.
64f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll * <p/>
65f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll * The view info also implements {@link IPropertySource}, which enables a linked
66f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll * {@link IPropertySheetPage} to display the attributes of the selected element.
67f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll * This class actually delegates handling of {@link IPropertySource} to the underlying
68f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll * {@link UiViewElementNode}, if any.
698fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael */
70f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Mollpublic class CanvasViewInfo implements IPropertySource {
718fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael
728fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    /**
738fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     * Minimal size of the selection, in case an empty view or layout is selected.
748fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     */
759bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    public static final int SELECTION_MIN_SIZE = 6;
768fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael
778fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    private final Rectangle mAbsRect;
788fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    private final Rectangle mSelectionRect;
798fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    private final String mName;
80d9881e4b0ed00c7f7fd529f482cfd08b7d9ec396Xavier Ducrohet    private final Object mViewObject;
8171f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet    private final UiViewElementNode mUiViewNode;
829155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    private CanvasViewInfo mParent;
8380d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye    private ViewInfo mViewInfo;
844e9c7b5df119dda3acd516fcea2ff59c22a19565Tor Norbye    private final List<CanvasViewInfo> mChildren = new ArrayList<CanvasViewInfo>();
858fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael
868fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    /**
8723258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     * Is this view info an individually exploded view? This is the case for views
8823258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     * that were specially inflated by the {@link UiElementPullParser} and assigned
8923258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     * fixed padding because they were invisible and somebody requested visibility.
9023258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     */
9123258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye    private boolean mExploded;
9223258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye
9323258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye    /**
949155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * Node sibling. This is usually null, but it's possible for a single node in the
959155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * model to have <b>multiple</b> separate views in the canvas, for example
969155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * when you {@code <include>} a view that has multiple widgets inside a
979155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * {@code <merge>} tag. In this case, all the views have the same node model,
989155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * the include tag, and selecting the include should highlight all the separate
999155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * views that are linked to this node. That's what this field is all about: it is
1009155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * a <b>circular</b> list of all the siblings that share the same node.
1019155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     */
1029155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    private List<CanvasViewInfo> mNodeSiblings;
1039155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
1049155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    /**
1058386da5e451eec396d0e71576e7366a98017674fTor Norbye     * Constructs a {@link CanvasViewInfo} initialized with the given initial values.
1068fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     */
1078386da5e451eec396d0e71576e7366a98017674fTor Norbye    private CanvasViewInfo(CanvasViewInfo parent, String name,
1088386da5e451eec396d0e71576e7366a98017674fTor Norbye            Object viewObject, UiViewElementNode node, Rectangle absRect,
10980d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye            Rectangle selectionRect, ViewInfo viewInfo) {
1108fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael        mParent = parent;
1118386da5e451eec396d0e71576e7366a98017674fTor Norbye        mName = name;
1128386da5e451eec396d0e71576e7366a98017674fTor Norbye        mViewObject = viewObject;
11380d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye        mViewInfo = viewInfo;
1148386da5e451eec396d0e71576e7366a98017674fTor Norbye        mUiViewNode  = node;
1158386da5e451eec396d0e71576e7366a98017674fTor Norbye        mAbsRect = absRect;
1168386da5e451eec396d0e71576e7366a98017674fTor Norbye        mSelectionRect = selectionRect;
1178fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    }
1188fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael
1198fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    /**
120f29be828de51dbe2f55508cd620142e35cd19cbdXavier Ducrohet     * Returns the original {@link ViewInfo} bounds in absolute coordinates
1218fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     * over the whole graphic.
1228386da5e451eec396d0e71576e7366a98017674fTor Norbye     *
1238386da5e451eec396d0e71576e7366a98017674fTor Norbye     * @return the bounding box in absolute coordinates
1248fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     */
1259bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @NonNull
1268fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    public Rectangle getAbsRect() {
1278fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael        return mAbsRect;
1288fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    }
1298fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael
1309bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    /**
1319bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye     * Returns the absolute selection bounds of the view info as a rectangle.
1329bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye     * The selection bounds will always have a size greater or equal to
1339bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye     * {@link #SELECTION_MIN_SIZE}.
1349bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye     * The width/height is inclusive (i.e. width = right-left-1).
1359bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye     * This is in absolute "screen" coordinates (relative to the rendered bitmap).
1369bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye     *
1379bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye     * @return the absolute selection bounds
1389bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye     */
1399bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @NonNull
1408fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    public Rectangle getSelectionRect() {
1418fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael        return mSelectionRect;
1428fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    }
1438fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael
1448fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    /**
14571f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet     * Returns the view node. Could be null, although unlikely.
1468fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     * @return An {@link UiViewElementNode} that uniquely identifies the object in the XML model.
14771f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet     * @see ViewInfo#getCookie()
1488fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     */
1499bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @Nullable
15071f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet    public UiViewElementNode getUiViewNode() {
15171f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet        return mUiViewNode;
1528fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    }
1538fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael
1548fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    /**
1558fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     * Returns the parent {@link CanvasViewInfo}.
1568fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     * It is null for the root and non-null for children.
1578386da5e451eec396d0e71576e7366a98017674fTor Norbye     *
1588386da5e451eec396d0e71576e7366a98017674fTor Norbye     * @return the parent {@link CanvasViewInfo}, which can be null
1598fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     */
1609bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @Nullable
1618fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    public CanvasViewInfo getParent() {
1628fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael        return mParent;
1638fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    }
1648fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael
1658fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    /**
1668fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     * Returns the list of children of this {@link CanvasViewInfo}.
1678fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     * The list is never null. It can be empty.
1688fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     * By contract, this.getChildren().get(0..n-1).getParent() == this.
1698386da5e451eec396d0e71576e7366a98017674fTor Norbye     *
1708386da5e451eec396d0e71576e7366a98017674fTor Norbye     * @return the children, never null
1718fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     */
1729bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @NonNull
1738386da5e451eec396d0e71576e7366a98017674fTor Norbye    public List<CanvasViewInfo> getChildren() {
1748fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael        return mChildren;
1758fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    }
1768fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael
1778fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    /**
1789155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * For nodes that have multiple views rendered from a single node, such as the
1799155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * children of a {@code <merge>} tag included into a separate layout, return the
1809155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * "primary" view, the first view that is rendered
1819155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     */
1829bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @Nullable
1839155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    private CanvasViewInfo getPrimaryNodeSibling() {
1849155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        if (mNodeSiblings == null || mNodeSiblings.size() == 0) {
1859155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            return null;
1869155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        }
1879155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
1889155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        return mNodeSiblings.get(0);
1899155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    }
1909155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
1919155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    /**
1929155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * Returns true if this view represents one view of many linked to a single node, and
1939155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * where this is the primary view. The primary view is the one that will be shown
1949155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * in the outline for example (since we only show nodes, not views, in the outline,
1959155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * and therefore don't want repetitions when a view has more than one view info.)
1969155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     *
1979155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * @return true if this is the primary view among more than one linked to a single
1989155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     *         node
1999155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     */
2009155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    private boolean isPrimaryNodeSibling() {
2019155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        return getPrimaryNodeSibling() == this;
2029155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    }
2039155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
2049155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    /**
2059155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * Returns the list of node sibling of this view (which <b>will include this
2069155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * view</b>). For most views this is going to be null, but for views that share a
2079155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * single node (such as widgets inside a {@code <merge>} tag included into another
2089155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * layout), this will provide all the views that correspond to the node.
2099155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     *
2109155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * @return a non-empty list of siblings (including this), or null
2119155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     */
2129bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @Nullable
2139155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    public List<CanvasViewInfo> getNodeSiblings() {
2149155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        return mNodeSiblings;
2159155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    }
2169155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
2179155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    /**
2189155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * Returns all the children of the canvas view info where each child corresponds to a
2198925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye     * unique node that the user can see and select. This is intended for use by the
2208925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye     * outline for example, where only the actual nodes are displayed, not the views
2218925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye     * themselves.
2229155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * <p>
2239155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * Most views have their own nodes, so this is generally the same as
2249155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * {@link #getChildren}, except in the case where you for example include a view that
2259155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * has multiple widgets inside a {@code <merge>} tag, where all these widgets have the
2269155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * same node (the {@code <merge>} tag).
2279155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     *
2289155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * @return list of {@link CanvasViewInfo} objects that are children of this view,
2299155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     *         never null
2309155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     */
2319bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @NonNull
2329155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    public List<CanvasViewInfo> getUniqueChildren() {
2338925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye        boolean haveHidden = false;
2348925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye
2359155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        for (CanvasViewInfo info : mChildren) {
2369155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            if (info.mNodeSiblings != null) {
2379155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // We have secondary children; must create a new collection containing
2389155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // only non-secondary children
2399155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                List<CanvasViewInfo> children = new ArrayList<CanvasViewInfo>();
2409155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                for (CanvasViewInfo vi : mChildren) {
2419155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    if (vi.mNodeSiblings == null) {
2429155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        children.add(vi);
2439155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    } else if (vi.isPrimaryNodeSibling()) {
2449155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        children.add(vi);
2459155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
2469155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                }
2479155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                return children;
2489155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            }
2498925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye
2508925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye            haveHidden |= info.isHidden();
2518925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye        }
2528925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye
2538925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye        if (haveHidden) {
2548925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye            List<CanvasViewInfo> children = new ArrayList<CanvasViewInfo>(mChildren.size());
2558925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye            for (CanvasViewInfo vi : mChildren) {
2568925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye                if (!vi.isHidden()) {
2578925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye                    children.add(vi);
2588925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye                }
2598925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye            }
2608925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye
2618925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye            return children;
2629155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        }
2639155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
2649155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        return mChildren;
2659155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    }
2669155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
2679155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    /**
268671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael     * Returns true if the specific {@link CanvasViewInfo} is a parent
269671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael     * of this {@link CanvasViewInfo}. It can be a direct parent or any
270671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael     * grand-parent higher in the hierarchy.
2718386da5e451eec396d0e71576e7366a98017674fTor Norbye     *
2728386da5e451eec396d0e71576e7366a98017674fTor Norbye     * @param potentialParent the view info to check
2738386da5e451eec396d0e71576e7366a98017674fTor Norbye     * @return true if the given info is a parent of this view
274671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael     */
2759bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    public boolean isParent(@NonNull CanvasViewInfo potentialParent) {
276671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael        CanvasViewInfo p = mParent;
277671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael        while (p != null) {
278671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael            if (p == potentialParent) {
279671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael                return true;
280671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael            }
281671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael            p = p.getParent();
282671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael        }
283671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael        return false;
284671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael    }
285671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael
286671636a1a16dbe4fb73509b5e3cb92ca96a778c7Raphael    /**
2878fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     * Returns the name of the {@link CanvasViewInfo}.
2888fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     * Could be null, although unlikely.
2898fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     * Experience shows this is the full qualified Java name of the View.
2908925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye     * TODO: Rename this method to getFqcn.
291f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll     *
2929bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye     * @return the name of the view info
2938386da5e451eec396d0e71576e7366a98017674fTor Norbye     *
294f29be828de51dbe2f55508cd620142e35cd19cbdXavier Ducrohet     * @see ViewInfo#getClassName()
2958fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael     */
2969bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @NonNull
2978fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    public String getName() {
2988fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael        return mName;
2998fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael    }
300f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll
301d9881e4b0ed00c7f7fd529f482cfd08b7d9ec396Xavier Ducrohet    /**
302d9881e4b0ed00c7f7fd529f482cfd08b7d9ec396Xavier Ducrohet     * Returns the View object associated with the {@link CanvasViewInfo}.
303d9881e4b0ed00c7f7fd529f482cfd08b7d9ec396Xavier Ducrohet     * @return the view object or null.
304d9881e4b0ed00c7f7fd529f482cfd08b7d9ec396Xavier Ducrohet     */
3059bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @Nullable
306d9881e4b0ed00c7f7fd529f482cfd08b7d9ec396Xavier Ducrohet    public Object getViewObject() {
307d9881e4b0ed00c7f7fd529f482cfd08b7d9ec396Xavier Ducrohet        return mViewObject;
308d9881e4b0ed00c7f7fd529f482cfd08b7d9ec396Xavier Ducrohet    }
309d9881e4b0ed00c7f7fd529f482cfd08b7d9ec396Xavier Ducrohet
3109bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    /**
3119bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye     * Returns the baseline of this object, or -1 if it does not support a baseline
3129bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye     *
3139bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye     * @return the baseline or -1
3149bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye     */
31580d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye    public int getBaseline() {
31680d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye        if (mViewInfo != null) {
31780d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye            int baseline = mViewInfo.getBaseLine();
31880d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye            if (baseline != Integer.MIN_VALUE) {
31980d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye                return baseline;
32080d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye            }
32180d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye        }
32280d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye
32380d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye        return -1;
32480d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye    }
32580d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye
32680d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye    /**
32780d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye     * Returns the {@link Margins} for this {@link CanvasViewInfo}
32880d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye     *
32980d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye     * @return the {@link Margins} for this {@link CanvasViewInfo}
33080d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye     */
3319bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @Nullable
33280d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye    public Margins getMargins() {
33380d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye        if (mViewInfo != null) {
33480d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye            int leftMargin = mViewInfo.getLeftMargin();
33580d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye            int topMargin = mViewInfo.getTopMargin();
33680d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye            int rightMargin = mViewInfo.getRightMargin();
33780d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye            int bottomMargin = mViewInfo.getBottomMargin();
33880d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye            return new Margins(
33980d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye                leftMargin != Integer.MIN_VALUE ? leftMargin : 0,
34080d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye                rightMargin != Integer.MIN_VALUE ? rightMargin : 0,
34180d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye                topMargin != Integer.MIN_VALUE ? topMargin : 0,
34280d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye                bottomMargin != Integer.MIN_VALUE ? bottomMargin : 0
34380d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye            );
34480d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye        }
34580d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye
34680d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye        return null;
34780d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye    }
34880d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye
349f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll    // ---- Implementation of IPropertySource
3509bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    // TODO: Get rid of this once the old propertysheet implementation is fully gone
351f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll
352ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
353f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll    public Object getEditableValue() {
35471f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet        UiViewElementNode uiView = getUiViewNode();
355f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        if (uiView != null) {
356f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll            return ((IPropertySource) uiView).getEditableValue();
357f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        }
358f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        return null;
359f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll    }
360f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll
361ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
362f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll    public IPropertyDescriptor[] getPropertyDescriptors() {
36371f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet        UiViewElementNode uiView = getUiViewNode();
364f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        if (uiView != null) {
365f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll            return ((IPropertySource) uiView).getPropertyDescriptors();
366f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        }
367f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        return null;
368f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll    }
369f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll
370ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
371f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll    public Object getPropertyValue(Object id) {
37271f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet        UiViewElementNode uiView = getUiViewNode();
373f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        if (uiView != null) {
374f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll            return ((IPropertySource) uiView).getPropertyValue(id);
375f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        }
376f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        return null;
377f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll    }
378f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll
379ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
380f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll    public boolean isPropertySet(Object id) {
38171f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet        UiViewElementNode uiView = getUiViewNode();
382f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        if (uiView != null) {
383f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll            return ((IPropertySource) uiView).isPropertySet(id);
384f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        }
385f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        return false;
386f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll    }
387f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll
388ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
389f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll    public void resetPropertyValue(Object id) {
39071f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet        UiViewElementNode uiView = getUiViewNode();
391f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        if (uiView != null) {
392f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll            ((IPropertySource) uiView).resetPropertyValue(id);
393f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        }
394f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll    }
395f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll
396ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
397f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll    public void setPropertyValue(Object id, Object value) {
39871f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet        UiViewElementNode uiView = getUiViewNode();
399f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        if (uiView != null) {
400f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll            ((IPropertySource) uiView).setPropertyValue(id, value);
401f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll        }
402f3199b3448c9c80d68a3bce8b3166632d9b3e767Raphael Moll    }
40376bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbye
40476bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbye    /**
40576bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbye     * Returns the XML node corresponding to this info, or null if there is no
40676bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbye     * such XML node.
40776bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbye     *
40876bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbye     * @return The XML node corresponding to this info object, or null
40976bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbye     */
4109bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @Nullable
41176bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbye    public Node getXmlNode() {
41271f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet        UiViewElementNode uiView = getUiViewNode();
41376bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbye        if (uiView != null) {
41476bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbye            return uiView.getXmlNode();
41576bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbye        }
41676bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbye
41776bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbye        return null;
41876bbc57fcc07859cd1d7f1c9be102b1dc5218f27Tor Norbye    }
41983dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye
42083dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye    /**
42183dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye     * Returns true iff this view info corresponds to a root element.
42283dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye     *
42383dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye     * @return True iff this is a root view info.
42483dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye     */
42583dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye    public boolean isRoot() {
42683dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        // Select the visual element -- unless it's the root.
42783dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        // The root element is the one whose GRAND parent
42883dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        // is null (because the parent will be a -document-
42983dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        // node).
4303cf5ce58a324efb926b862b16ad8b18d033f5be9Tor Norbye
4313cf5ce58a324efb926b862b16ad8b18d033f5be9Tor Norbye        // Special case: a gesture overlay is sometimes added as the root, but for all intents
4323cf5ce58a324efb926b862b16ad8b18d033f5be9Tor Norbye        // and purposes it is its layout child that is the real root so treat that one as the
4333cf5ce58a324efb926b862b16ad8b18d033f5be9Tor Norbye        // root as well (such that the whole layout canvas does not highlight as part of hovers
4343cf5ce58a324efb926b862b16ad8b18d033f5be9Tor Norbye        // etc)
4353cf5ce58a324efb926b862b16ad8b18d033f5be9Tor Norbye        if (mParent != null
4360757ce4af2764e4dd564acc0b1a013e910abc8daTor Norbye                && mParent.mName.endsWith(GESTURE_OVERLAY_VIEW)
4374e9c7b5df119dda3acd516fcea2ff59c22a19565Tor Norbye                && mParent.isRoot()
4384e9c7b5df119dda3acd516fcea2ff59c22a19565Tor Norbye                && mParent.mChildren.size() == 1) {
4393cf5ce58a324efb926b862b16ad8b18d033f5be9Tor Norbye            return true;
4403cf5ce58a324efb926b862b16ad8b18d033f5be9Tor Norbye        }
4413cf5ce58a324efb926b862b16ad8b18d033f5be9Tor Norbye
44271f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet        return mUiViewNode == null || mUiViewNode.getUiParent() == null ||
44371f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet            mUiViewNode.getUiParent().getUiParent() == null;
44483dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye    }
44583dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye
44683dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye    /**
4474563c4e2f168df1d6c97206a4ac6444dfa2264baTor Norbye     * Returns true if this {@link CanvasViewInfo} represents an invisible widget that
4484563c4e2f168df1d6c97206a4ac6444dfa2264baTor Norbye     * should be highlighted when selected.  This is the case for any layout that is less than the minimum
4494563c4e2f168df1d6c97206a4ac6444dfa2264baTor Norbye     * threshold ({@link #SELECTION_MIN_SIZE}), or any other view that has -0- bounds.
45023258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     *
4514563c4e2f168df1d6c97206a4ac6444dfa2264baTor Norbye     * @return True if this is a tiny layout or invisible view
45223258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     */
4534563c4e2f168df1d6c97206a4ac6444dfa2264baTor Norbye    public boolean isInvisible() {
4548925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye        if (isHidden()) {
4558925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye            // Don't expand and highlight hidden widgets
4568925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye            return false;
4578925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye        }
4588925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye
45923258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye        if (mAbsRect.width < SELECTION_MIN_SIZE || mAbsRect.height < SELECTION_MIN_SIZE) {
4604563c4e2f168df1d6c97206a4ac6444dfa2264baTor Norbye            return mUiViewNode != null && (mUiViewNode.getDescriptor().hasChildren() ||
4614563c4e2f168df1d6c97206a4ac6444dfa2264baTor Norbye                    mAbsRect.width <= 0 || mAbsRect.height <= 0);
46223258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye        }
46323258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye
46423258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye        return false;
46523258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye    }
46623258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye
46723258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye    /**
4688925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye     * Returns true if this {@link CanvasViewInfo} represents a widget that should be
4698925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye     * hidden, such as a {@code <Space>} which are typically not manipulated by the user
4708925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye     * through dragging etc.
4718925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye     *
4728925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye     * @return true if this is a hidden view
4738925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye     */
4748925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye    public boolean isHidden() {
4754eacdfbcc84ad11f599020b12ad76aebed70537fTor Norbye        if (GridLayoutRule.sDebugGridLayout) {
4764eacdfbcc84ad11f599020b12ad76aebed70537fTor Norbye            return false;
4774eacdfbcc84ad11f599020b12ad76aebed70537fTor Norbye        }
4784eacdfbcc84ad11f599020b12ad76aebed70537fTor Norbye
4793e75d4f79a8328ed18505830c786402369082efaTor Norbye        return FQCN_SPACE.equals(mName) || FQCN_SPACE_V7.equals(mName);
4808925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye    }
4818925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye
4828925febd3beb31e4c5e1a3329ed1e73291dd3936Tor Norbye    /**
48323258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     * Is this {@link CanvasViewInfo} a view that has had its padding inflated in order to
48423258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     * make it visible during selection or dragging? Note that this is NOT considered to
48523258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     * be the case in the explode-all-views mode where all nodes have their padding
48623258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     * increased; it's only used for views that individually exploded because they were
4874563c4e2f168df1d6c97206a4ac6444dfa2264baTor Norbye     * requested visible and they returned true for {@link #isInvisible()}.
48823258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     *
48923258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     * @return True if this is an exploded node.
49023258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     */
49123258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye    public boolean isExploded() {
49223258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye        return mExploded;
49323258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye    }
49423258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye
49523258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye    /**
49623258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     * Mark this {@link CanvasViewInfo} as having been exploded or not. See the
49723258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     * {@link #isExploded()} method for details on what this property means.
49823258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     *
49923258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     * @param exploded New value of the exploded property to mark this info with.
50023258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye     */
5019bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    void setExploded(boolean exploded) {
502a21b9b44b0db30f497b3507c8b97683387960b59Tor Norbye        mExploded = exploded;
50323258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye    }
50423258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye
50523258d5ae21d164049eef14eebb2ef28a43a7b40Tor Norbye    /**
50683dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye     * Returns the info represented as a {@link SimpleElement}.
50783dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye     *
50883dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye     * @return A {@link SimpleElement} wrapping this info.
50983dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye     */
5109bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @NonNull
5119bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    SimpleElement toSimpleElement() {
51283dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye
51371f1ce4538caa9f7d0824c7f2090d95a6c6b7d71Xavier Ducrohet        UiViewElementNode uiNode = getUiViewNode();
51483dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye
51583dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        String fqcn = SimpleXmlTransfer.getFqcn(uiNode.getDescriptor());
51683dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        String parentFqcn = null;
517a2d7874ed23bfc2fa7665cc84901e0f4781b4e51Tor Norbye        Rect bounds = SwtUtils.toRect(getAbsRect());
51883dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        Rect parentBounds = null;
51983dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye
52083dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        UiElementNode uiParent = uiNode.getUiParent();
52183dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        if (uiParent != null) {
52283dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye            parentFqcn = SimpleXmlTransfer.getFqcn(uiParent.getDescriptor());
52383dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        }
52483dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        if (getParent() != null) {
525a2d7874ed23bfc2fa7665cc84901e0f4781b4e51Tor Norbye            parentBounds = SwtUtils.toRect(getParent().getAbsRect());
52683dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        }
52783dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye
52883dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        SimpleElement e = new SimpleElement(fqcn, parentFqcn, bounds, parentBounds);
52983dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye
53086618cc78fb2f207c477527b4413ff234f474431Raphael Moll        for (UiAttributeNode attr : uiNode.getAllUiAttributes()) {
53183dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye            String value = attr.getCurrentValue();
53283dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye            if (value != null && value.length() > 0) {
53383dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye                AttributeDescriptor attrDesc = attr.getDescriptor();
53483dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye                SimpleAttribute a = new SimpleAttribute(
53583dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye                        attrDesc.getNamespaceUri(),
53683dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye                        attrDesc.getXmlLocalName(),
53783dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye                        value);
53883dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye                e.addAttribute(a);
53983dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye            }
54083dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        }
54183dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye
54283dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        for (CanvasViewInfo childVi : getChildren()) {
54383dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye            SimpleElement e2 = childVi.toSimpleElement();
54483dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye            if (e2 != null) {
54583dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye                e.addInnerElement(e2);
54683dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye            }
54783dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        }
54883dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye
54983dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye        return e;
55083dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye    }
55183dba505e22985fd2f9414e7c6ef14ce29d31713Tor Norbye
55299fd7eee15c89fd45b884842c44371326f851930Tor Norbye    /**
553c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye     * Returns the layout url attribute value for the closest surrounding include or
554c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye     * fragment element parent, or null if this {@link CanvasViewInfo} is not rendered as
555c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye     * part of an include or fragment tag.
55699fd7eee15c89fd45b884842c44371326f851930Tor Norbye     *
55799fd7eee15c89fd45b884842c44371326f851930Tor Norbye     * @return the layout url attribute value for the surrounding include tag, or null if
55899fd7eee15c89fd45b884842c44371326f851930Tor Norbye     *         not applicable
55999fd7eee15c89fd45b884842c44371326f851930Tor Norbye     */
5609bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @Nullable
56199fd7eee15c89fd45b884842c44371326f851930Tor Norbye    public String getIncludeUrl() {
56299fd7eee15c89fd45b884842c44371326f851930Tor Norbye        CanvasViewInfo curr = this;
56399fd7eee15c89fd45b884842c44371326f851930Tor Norbye        while (curr != null) {
56499fd7eee15c89fd45b884842c44371326f851930Tor Norbye            if (curr.mUiViewNode != null) {
56599fd7eee15c89fd45b884842c44371326f851930Tor Norbye                Node node = curr.mUiViewNode.getXmlNode();
566c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye                if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
567c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye                    String nodeName = node.getNodeName();
568c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye                    if (node.getNamespaceURI() == null
56912d4581faa6438941e65a9dc83213be34c6ca970Tor Norbye                            && SdkConstants.VIEW_INCLUDE.equals(nodeName)) {
570c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye                        // Note: the layout attribute is NOT in the Android namespace
571c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye                        Element element = (Element) node;
57212d4581faa6438941e65a9dc83213be34c6ca970Tor Norbye                        String url = element.getAttribute(SdkConstants.ATTR_LAYOUT);
573c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye                        if (url.length() > 0) {
574c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye                            return url;
575c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye                        }
57612d4581faa6438941e65a9dc83213be34c6ca970Tor Norbye                    } else if (SdkConstants.VIEW_FRAGMENT.equals(nodeName)) {
577c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye                        String url = FragmentMenu.getFragmentLayout(node);
578c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye                        if (url != null) {
579c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye                            return url;
580c4674db5cbd9216d5793a004ef158d5ba98cdc40Tor Norbye                        }
58199fd7eee15c89fd45b884842c44371326f851930Tor Norbye                    }
58299fd7eee15c89fd45b884842c44371326f851930Tor Norbye                }
58399fd7eee15c89fd45b884842c44371326f851930Tor Norbye            }
58499fd7eee15c89fd45b884842c44371326f851930Tor Norbye            curr = curr.mParent;
58599fd7eee15c89fd45b884842c44371326f851930Tor Norbye        }
58699fd7eee15c89fd45b884842c44371326f851930Tor Norbye
58799fd7eee15c89fd45b884842c44371326f851930Tor Norbye        return null;
58899fd7eee15c89fd45b884842c44371326f851930Tor Norbye    }
5898386da5e451eec396d0e71576e7366a98017674fTor Norbye
5909155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    /** Adds the given {@link CanvasViewInfo} as a new last child of this view */
5919bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    private void addChild(@NonNull CanvasViewInfo child) {
5929155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        mChildren.add(child);
5939155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    }
5949155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
5959155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    /** Adds the given {@link CanvasViewInfo} as a child at the given index */
5969bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    private void addChildAt(int index, @NonNull CanvasViewInfo child) {
5979155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        mChildren.add(index, child);
5989155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    }
5999155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
6009155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    /**
6019155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * Removes the given {@link CanvasViewInfo} from the child list of this view, and
6029155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * returns true if it was successfully removed
6039155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     *
6049155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * @param child the child to be removed
6059155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * @return true if it was a child and was removed
6069155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     */
6079bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    public boolean removeChild(@NonNull CanvasViewInfo child) {
6089155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        return mChildren.remove(child);
6099155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    }
6109155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
6119155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    @Override
6129155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    public String toString() {
6139155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        return "CanvasViewInfo [name=" + mName + ", node=" + mUiViewNode + "]";
6149155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    }
6159155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
6168386da5e451eec396d0e71576e7366a98017674fTor Norbye    // ---- Factory functionality ----
6178386da5e451eec396d0e71576e7366a98017674fTor Norbye
6188386da5e451eec396d0e71576e7366a98017674fTor Norbye    /**
6198386da5e451eec396d0e71576e7366a98017674fTor Norbye     * Creates a new {@link CanvasViewInfo} hierarchy based on the given {@link ViewInfo}
6208386da5e451eec396d0e71576e7366a98017674fTor Norbye     * hierarchy. Note that this will not necessarily create one {@link CanvasViewInfo}
6218386da5e451eec396d0e71576e7366a98017674fTor Norbye     * for each {@link ViewInfo}. It will generally only create {@link CanvasViewInfo}
6228386da5e451eec396d0e71576e7366a98017674fTor Norbye     * objects for {@link ViewInfo} objects that contain a reference to an
6238386da5e451eec396d0e71576e7366a98017674fTor Norbye     * {@link UiViewElementNode}, meaning that it corresponds to an element in the XML
6248386da5e451eec396d0e71576e7366a98017674fTor Norbye     * file for this layout file. This is not always the case, such as in the following
6258386da5e451eec396d0e71576e7366a98017674fTor Norbye     * scenarios:
6268386da5e451eec396d0e71576e7366a98017674fTor Norbye     * <ul>
6278386da5e451eec396d0e71576e7366a98017674fTor Norbye     * <li>we link to other layouts with {@code <include>}
6288386da5e451eec396d0e71576e7366a98017674fTor Norbye     * <li>the current view is rendered within another view ("Show Included In") such that
6298386da5e451eec396d0e71576e7366a98017674fTor Norbye     * the outer file does not correspond to elements in the current included XML layout
6308386da5e451eec396d0e71576e7366a98017674fTor Norbye     * <li>on older platforms that don't support {@link Capability#EMBEDDED_LAYOUT} there
6318386da5e451eec396d0e71576e7366a98017674fTor Norbye     * is no reference to the {@code <include>} tag
6328386da5e451eec396d0e71576e7366a98017674fTor Norbye     * <li>with the {@code <merge>} tag we don't get a reference to the corresponding
6338386da5e451eec396d0e71576e7366a98017674fTor Norbye     * element
6348386da5e451eec396d0e71576e7366a98017674fTor Norbye     * <ul>
6358386da5e451eec396d0e71576e7366a98017674fTor Norbye     * <p>
6368386da5e451eec396d0e71576e7366a98017674fTor Norbye     * This method will build up a set of {@link CanvasViewInfo} that corresponds to the
6378386da5e451eec396d0e71576e7366a98017674fTor Norbye     * actual <b>selectable</b> views (which are also shown in the Outline).
6388386da5e451eec396d0e71576e7366a98017674fTor Norbye     *
6392fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye     * @param layoutlib5 if true, the {@link ViewInfo} hierarchy was created by layoutlib
6402fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye     *    version 5 or higher, which means this algorithm can make certain assumptions
6412fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye     *    (for example that {@code <merge>} siblings will provide {@link MergeCookie}
6422fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye     *    references, so we don't have to search for them.)
6438386da5e451eec396d0e71576e7366a98017674fTor Norbye     * @param root the root {@link ViewInfo} to build from
6448386da5e451eec396d0e71576e7366a98017674fTor Norbye     * @return a {@link CanvasViewInfo} hierarchy
6458386da5e451eec396d0e71576e7366a98017674fTor Norbye     */
6469bd06947302ca6ca3e0b90eef894e553c6c3e067Tor Norbye    @NonNull
6472fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye    public static Pair<CanvasViewInfo,List<Rectangle>> create(ViewInfo root, boolean layoutlib5) {
6482fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye        return new Builder(layoutlib5).create(root);
6498386da5e451eec396d0e71576e7366a98017674fTor Norbye    }
6508386da5e451eec396d0e71576e7366a98017674fTor Norbye
6519155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    /** Builder object which walks over a tree of {@link ViewInfo} objects and builds
6529155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye     * up a corresponding {@link CanvasViewInfo} hierarchy. */
6539155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye    private static class Builder {
6542fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye        public Builder(boolean layoutlib5) {
6552fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye            mLayoutLib5 = layoutlib5;
6562fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye        }
6579155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
6582fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye        /**
6592fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye         * The mapping from nodes that have a {@code <merge>} as a parent in the node
6602fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye         * model to their corresponding views
6612fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye         */
6622fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye        private Map<UiViewElementNode, List<CanvasViewInfo>> mMergeNodeMap;
6632fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye
6642fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye        /**
6652fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye         * Whether the ViewInfos are provided by a layout library that is version 5 or
6662fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye         * later, since that will allow us to take several shortcuts
6672fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye         */
6682fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye        private boolean mLayoutLib5;
6692fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye
6702fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye        /**
6712fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye         * Creates a hierarchy of {@link CanvasViewInfo} objects and merge bounding
6722fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye         * rectangles from the given {@link ViewInfo} hierarchy
6732fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye         */
6742fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye        private Pair<CanvasViewInfo,List<Rectangle>> create(ViewInfo root) {
6759155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            Object cookie = root.getCookie();
6769155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            if (cookie == null) {
6779155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // Special case: If the root-most view does not have a view cookie,
6789155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // then we are rendering some outer layout surrounding this layout, and in
6799155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // that case we must search down the hierarchy for the (possibly multiple)
6809155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // sub-roots that correspond to elements in this layout, and place them inside
6819155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // an outer view that has no node. In the outline this item will be used to
6829155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // show the inclusion-context.
6839155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                CanvasViewInfo rootView = createView(null, root, 0, 0);
6849155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                addKeyedSubtrees(rootView, root, 0, 0);
6859155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
6869155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                List<Rectangle> includedBounds = new ArrayList<Rectangle>();
6879155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                for (CanvasViewInfo vi : rootView.getChildren()) {
688e7a41238a05f9b4a992b0d2631620c1d07bfee8aTor Norbye                    if (vi.getNodeSiblings() == null || vi.isPrimaryNodeSibling()) {
6899155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        includedBounds.add(vi.getAbsRect());
6909155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
6919155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                }
6928386da5e451eec396d0e71576e7366a98017674fTor Norbye
6939155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // There are <merge> nodes here; see if we can insert it into the hierarchy
6949155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                if (mMergeNodeMap != null) {
6959155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // Locate all the nodes that have a <merge> as a parent in the node model,
6969155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // and where the view sits at the top level inside the include-context node.
6979155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    UiViewElementNode merge = null;
6989155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    List<CanvasViewInfo> merged = new ArrayList<CanvasViewInfo>();
6999155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    for (Map.Entry<UiViewElementNode, List<CanvasViewInfo>> entry : mMergeNodeMap
7009155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            .entrySet()) {
7019155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        UiViewElementNode node = entry.getKey();
7029155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        if (!hasMergeParent(node)) {
7039155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            continue;
7049155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        }
7059155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        List<CanvasViewInfo> views = entry.getValue();
7069155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        assert views.size() > 0;
7079155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        CanvasViewInfo view = views.get(0); // primary
7089155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        if (view.getParent() != rootView) {
7099155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            continue;
7109155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        }
7119155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        UiElementNode parent = node.getUiParent();
7129155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        if (merge != null && parent != merge) {
7139155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            continue;
7149155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        }
7159155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        merge = (UiViewElementNode) parent;
7169155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        merged.add(view);
7179155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
7189155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    if (merged.size() > 0) {
7199155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        // Compute a bounding box for the merged views
7209155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        Rectangle absRect = null;
7219155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        for (CanvasViewInfo child : merged) {
7229155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            Rectangle rect = child.getAbsRect();
7239155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            if (absRect == null) {
7249155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                absRect = rect;
7259155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            } else {
7269155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                absRect = absRect.union(rect);
7279155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            }
7289155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        }
7298386da5e451eec396d0e71576e7366a98017674fTor Norbye
7309155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        CanvasViewInfo mergeView = new CanvasViewInfo(rootView, VIEW_MERGE, null,
73180d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye                                merge, absRect, absRect, null /* viewInfo */);
7329155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        for (CanvasViewInfo view : merged) {
7339155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            if (rootView.removeChild(view)) {
7349155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                mergeView.addChild(view);
7359155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            }
7369155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        }
7379155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        rootView.addChild(mergeView);
7389155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
7399155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                }
7408386da5e451eec396d0e71576e7366a98017674fTor Norbye
7419155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                return Pair.of(rootView, includedBounds);
7429155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            } else {
7439155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // We have a view key at the top, so just go and create {@link CanvasViewInfo}
7449155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // objects for each {@link ViewInfo} until we run into a null key.
7459155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                CanvasViewInfo rootView = addKeyedSubtrees(null, root, 0, 0);
7469155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
7479155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // Special case: look to see if the root element is really a <merge>, and if so,
7489155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // manufacture a view for it such that we can target this root element
7499155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // in drag & drop operations, such that we can show it in the outline, etc
7509155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                if (rootView != null && hasMergeParent(rootView.getUiViewNode())) {
7519155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    CanvasViewInfo merge = new CanvasViewInfo(null, VIEW_MERGE, null,
7529155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            (UiViewElementNode) rootView.getUiViewNode().getUiParent(),
75380d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye                            rootView.getAbsRect(), rootView.getSelectionRect(),
75480d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye                            null /* viewInfo */);
7559155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // Insert the <merge> as the new real root
7569155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    rootView.mParent = merge;
7579155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    merge.addChild(rootView);
7589155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    rootView = merge;
7599155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                }
7608386da5e451eec396d0e71576e7366a98017674fTor Norbye
7619155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                return Pair.of(rootView, null);
7629155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            }
7639155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        }
7648386da5e451eec396d0e71576e7366a98017674fTor Norbye
7659155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        private boolean hasMergeParent(UiViewElementNode rootNode) {
7669155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            UiElementNode rootParent = rootNode.getUiParent();
7679155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            return (rootParent instanceof UiViewElementNode
7689155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    && VIEW_MERGE.equals(rootParent.getDescriptor().getXmlName()));
7698386da5e451eec396d0e71576e7366a98017674fTor Norbye        }
7708386da5e451eec396d0e71576e7366a98017674fTor Norbye
7719155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        /** Creates a {@link CanvasViewInfo} for a given {@link ViewInfo} but does not recurse */
7729155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        private CanvasViewInfo createView(CanvasViewInfo parent, ViewInfo root, int parentX,
7739155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                int parentY) {
7749155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            Object cookie = root.getCookie();
7759155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            UiViewElementNode node = null;
7769155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            if (cookie instanceof UiViewElementNode) {
7779155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                node = (UiViewElementNode) cookie;
7789155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            } else if (cookie instanceof MergeCookie) {
7799155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                cookie = ((MergeCookie) cookie).getCookie();
7809155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                if (cookie instanceof UiViewElementNode) {
7819155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    node = (UiViewElementNode) cookie;
7829155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    CanvasViewInfo view = createView(parent, root, parentX, parentY, node);
7839155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    if (root.getCookie() instanceof MergeCookie && view.mNodeSiblings == null) {
7849155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        List<CanvasViewInfo> v = mMergeNodeMap == null ?
7859155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                null : mMergeNodeMap.get(node);
7869155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        if (v != null) {
7879155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            v.add(view);
7889155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        } else {
7899155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            v = new ArrayList<CanvasViewInfo>();
7909155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            v.add(view);
7919155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            if (mMergeNodeMap == null) {
7929155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                mMergeNodeMap =
7939155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                    new HashMap<UiViewElementNode, List<CanvasViewInfo>>();
7949155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            }
7959155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            mMergeNodeMap.put(node, v);
7969155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        }
7979155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        view.mNodeSiblings = v;
7989155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
7999155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
8009155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    return view;
8019155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                }
8029155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            }
8039155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
8049155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            return createView(parent, root, parentX, parentY, node);
8058386da5e451eec396d0e71576e7366a98017674fTor Norbye        }
8068386da5e451eec396d0e71576e7366a98017674fTor Norbye
8079155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        /**
8089155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye         * Creates a {@link CanvasViewInfo} for a given {@link ViewInfo} but does not recurse.
8099155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye         * This method specifies an explicit {@link UiViewElementNode} to use rather than
8109155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye         * relying on the view cookie in the info object.
8119155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye         */
8129155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        private CanvasViewInfo createView(CanvasViewInfo parent, ViewInfo root, int parentX,
8139155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                int parentY, UiViewElementNode node) {
8148386da5e451eec396d0e71576e7366a98017674fTor Norbye
8159155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            int x = root.getLeft();
8169155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            int y = root.getTop();
8179155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            int w = root.getRight() - x;
8189155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            int h = root.getBottom() - y;
8198386da5e451eec396d0e71576e7366a98017674fTor Norbye
8209155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            x += parentX;
8219155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            y += parentY;
8228386da5e451eec396d0e71576e7366a98017674fTor Norbye
8239155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            Rectangle absRect = new Rectangle(x, y, w - 1, h - 1);
8248386da5e451eec396d0e71576e7366a98017674fTor Norbye
8259155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            if (w < SELECTION_MIN_SIZE) {
8269155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                int d = (SELECTION_MIN_SIZE - w) / 2;
8279155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                x -= d;
8289155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                w += SELECTION_MIN_SIZE - w;
8299155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            }
8308386da5e451eec396d0e71576e7366a98017674fTor Norbye
8319155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            if (h < SELECTION_MIN_SIZE) {
8329155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                int d = (SELECTION_MIN_SIZE - h) / 2;
8339155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                y -= d;
8349155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                h += SELECTION_MIN_SIZE - h;
8358386da5e451eec396d0e71576e7366a98017674fTor Norbye            }
8369155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
8379155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            Rectangle selectionRect = new Rectangle(x, y, w - 1, h - 1);
8389155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
8399155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            return new CanvasViewInfo(parent, root.getClassName(), root.getViewObject(), node,
84080d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye                    absRect, selectionRect, root);
8418386da5e451eec396d0e71576e7366a98017674fTor Norbye        }
8428386da5e451eec396d0e71576e7366a98017674fTor Norbye
8439155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        /** Create a subtree recursively until you run out of keys */
8449155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        private CanvasViewInfo createSubtree(CanvasViewInfo parent, ViewInfo viewInfo,
8459155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                int parentX, int parentY) {
8469155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            assert viewInfo.getCookie() != null;
8479155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
8489155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            CanvasViewInfo view = createView(parent, viewInfo, parentX, parentY);
8498f21dc3e82c298e727b8813e10896e55f82406f9Tor Norbye            // Bug workaround: Ensure that we never have a child node identical
8508f21dc3e82c298e727b8813e10896e55f82406f9Tor Norbye            // to its parent node: this can happen for example when rendering a
8518f21dc3e82c298e727b8813e10896e55f82406f9Tor Norbye            // ZoomControls view where the merge cookies point to the parent.
8528f21dc3e82c298e727b8813e10896e55f82406f9Tor Norbye            if (parent != null && view.mUiViewNode == parent.mUiViewNode) {
8538f21dc3e82c298e727b8813e10896e55f82406f9Tor Norbye                return null;
8548f21dc3e82c298e727b8813e10896e55f82406f9Tor Norbye            }
8559155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
8569155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // Process children:
8579155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            parentX += viewInfo.getLeft();
8589155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            parentY += viewInfo.getTop();
8599155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
8602fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye            List<ViewInfo> children = viewInfo.getChildren();
8612fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye
8622fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye            if (mLayoutLib5) {
8632fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                for (ViewInfo child : children) {
8642fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                    Object cookie = child.getCookie();
8652fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                    if (cookie instanceof UiViewElementNode || cookie instanceof MergeCookie) {
8662fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                        CanvasViewInfo childView = createSubtree(view, child,
8672fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                                parentX, parentY);
8688f21dc3e82c298e727b8813e10896e55f82406f9Tor Norbye                        if (childView != null) {
8698f21dc3e82c298e727b8813e10896e55f82406f9Tor Norbye                            view.addChild(childView);
8708f21dc3e82c298e727b8813e10896e55f82406f9Tor Norbye                        }
8712fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                    } // else: null cookies, adapter item references, etc: No child views.
8722fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                }
8732fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye
8742fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                return view;
8752fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye            }
8762fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye
8779155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // See if we have any missing keys at this level
8789155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            int missingNodes = 0;
8799155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            int mergeNodes = 0;
8808386da5e451eec396d0e71576e7366a98017674fTor Norbye            for (ViewInfo child : children) {
8819155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // Only use children which have a ViewKey of the correct type.
8829155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // We can't interact with those when they have a null key or
8839155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // an incompatible type.
8849155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                Object cookie = child.getCookie();
8859155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                if (!(cookie instanceof UiViewElementNode)) {
8869155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    if (cookie instanceof MergeCookie) {
8879155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        mergeNodes++;
8889155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    } else {
8899155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        missingNodes++;
8908386da5e451eec396d0e71576e7366a98017674fTor Norbye                    }
8918386da5e451eec396d0e71576e7366a98017674fTor Norbye                }
8929155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            }
8939155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
8949155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            if (missingNodes == 0 && mergeNodes == 0) {
8959155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // No missing nodes; this is the normal case, and we can just continue to
8969155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // recursively add our children
8978386da5e451eec396d0e71576e7366a98017674fTor Norbye                for (ViewInfo child : children) {
8989155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    CanvasViewInfo childView = createSubtree(view, child,
8999155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            parentX, parentY);
9009155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    view.addChild(childView);
9018386da5e451eec396d0e71576e7366a98017674fTor Norbye                }
9029155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
9039155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // TBD: Emit placeholder views for keys that have no views?
9049155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            } else {
9059155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // We don't have keys for one or more of the ViewInfos. There are many
9069155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // possible causes: we are on an SDK platform that does not support
9079155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // embedded_layout rendering, or we are including a view with a <merge>
9089155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // as the root element.
9099155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
9102fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                UiViewElementNode uiViewNode = view.getUiViewNode();
9112fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                String containerName = uiViewNode != null
9122fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                    ? uiViewNode.getDescriptor().getXmlLocalName() : ""; //$NON-NLS-1$
91312d4581faa6438941e65a9dc83213be34c6ca970Tor Norbye                if (containerName.equals(SdkConstants.VIEW_INCLUDE)) {
9149155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // This is expected -- we don't WANT to get node keys for the content
9159155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // of an include since it's in a different file and should be treated
9169155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // as a single unit that cannot be edited (hence, no CanvasViewInfo
9179155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // children)
9189155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                } else {
9199155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // We are getting children with null keys where we don't expect it;
9209155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // this usually means that we are dealing with an Android platform
9219155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // that does not support {@link Capability#EMBEDDED_LAYOUT}, or
9229155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // that there are <merge> tags which are doing surprising things
9239155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // to the view hierarchy
9249155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    LinkedList<UiViewElementNode> unused = new LinkedList<UiViewElementNode>();
9252fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                    if (uiViewNode != null) {
9262fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                        for (UiElementNode child : uiViewNode.getUiChildren()) {
9272fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                            if (child instanceof UiViewElementNode) {
9282fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                                unused.addLast((UiViewElementNode) child);
9292fa8370664b25f391eb15dc22a3daa2d55d2b883Tor Norbye                            }
9309155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        }
9319155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
9329155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    for (ViewInfo child : children) {
9339155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        Object cookie = child.getCookie();
9349155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        if (mergeNodes > 0 && cookie instanceof MergeCookie) {
9359155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            cookie = ((MergeCookie) cookie).getCookie();
9369155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        }
9379155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        if (cookie != null) {
9389155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            unused.remove(cookie);
9399155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        }
9409155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
9419155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
9429155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    if (unused.size() > 0 || mergeNodes > 0) {
9439155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        if (unused.size() == missingNodes) {
9449155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            // The number of unmatched elements and ViewInfos are identical;
9459155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            // it's very likely that they match one to one, so just use these
9469155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            for (ViewInfo child : children) {
9479155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                if (child.getCookie() == null) {
9489155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                    // Only create a flat (non-recursive) view
9499155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                    CanvasViewInfo childView = createView(view, child, parentX,
9509155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                            parentY, unused.removeFirst());
9519155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                    view.addChild(childView);
9529155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                } else {
9539155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                    CanvasViewInfo childView = createSubtree(view, child, parentX,
9549155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                            parentY);
9559155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                    view.addChild(childView);
9569155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                }
9579155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            }
9589155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        } else {
9599155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            // We have an uneven match. In this case we might be dealing
9609155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            // with <merge> etc.
9619155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            // We have no way to associate elements back with the
9629155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            // corresponding <include> tags if there are more than one of
9639155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            // them. That's not a huge tragedy since visually you are not
9649155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            // allowed to edit these anyway; we just need to make a visual
9659155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            // block for these for selection and outline purposes.
9669155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            addMismatched(view, parentX, parentY, children, unused);
9679155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        }
9689155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    } else {
9699155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        // No unused keys, but there are views without keys.
9709155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        // We can't represent these since all views must have node keys
9719155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        // such that you can operate on them. Just ignore these.
9728386da5e451eec396d0e71576e7366a98017674fTor Norbye                        for (ViewInfo child : children) {
9739155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            if (child.getCookie() != null) {
9749155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                CanvasViewInfo childView = createSubtree(view, child,
9759155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                        parentX, parentY);
9768386da5e451eec396d0e71576e7366a98017674fTor Norbye                                view.addChild(childView);
9778386da5e451eec396d0e71576e7366a98017674fTor Norbye                            }
9788386da5e451eec396d0e71576e7366a98017674fTor Norbye                        }
9798386da5e451eec396d0e71576e7366a98017674fTor Norbye                    }
9808386da5e451eec396d0e71576e7366a98017674fTor Norbye                }
9818386da5e451eec396d0e71576e7366a98017674fTor Norbye            }
9828386da5e451eec396d0e71576e7366a98017674fTor Norbye
9839155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            return view;
9849155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        }
9858386da5e451eec396d0e71576e7366a98017674fTor Norbye
9869155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        /**
9879155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye         * We have various {@link ViewInfo} children with null keys, and/or nodes in
9889155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye         * the corresponding UI model that are not referenced by any of the {@link ViewInfo}
9899155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye         * objects. This method attempts to account for this, by matching the views in
9909155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye         * the right order.
9919155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye         */
9929155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        private void addMismatched(CanvasViewInfo parentView, int parentX, int parentY,
9939155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                List<ViewInfo> children, LinkedList<UiViewElementNode> unused) {
9949155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            UiViewElementNode afterNode = null;
9959155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            UiViewElementNode beforeNode = null;
9969155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // We have one important clue we can use when matching unused nodes
9979155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // with views: if we have a view V1 with node N1, and a view V2 with node N2,
9989155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // then we can only match unknown node UN with unknown node UV if
9999155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // V1 < UV < V2 and N1 < UN < N2.
10009155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // We can use these constraints to do the matching, for example by
10019155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // a simple DAG traversal. However, since the number of unmatched nodes
10029155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // will typically be very small, we'll just do a simple algorithm here
10039155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // which checks forwards/backwards whether a match is valid.
10049155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            for (int index = 0, size = children.size(); index < size; index++) {
10059155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                ViewInfo child = children.get(index);
10069155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                if (child.getCookie() != null) {
10079155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    CanvasViewInfo childView = createSubtree(parentView, child, parentX, parentY);
10088f21dc3e82c298e727b8813e10896e55f82406f9Tor Norbye                    if (childView != null) {
10098f21dc3e82c298e727b8813e10896e55f82406f9Tor Norbye                        parentView.addChild(childView);
10108f21dc3e82c298e727b8813e10896e55f82406f9Tor Norbye                    }
10119155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    if (child.getCookie() instanceof UiViewElementNode) {
10129155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        afterNode = (UiViewElementNode) child.getCookie();
10139155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
10148386da5e451eec396d0e71576e7366a98017674fTor Norbye                } else {
10159155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    beforeNode = nextViewNode(children, index);
10169155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
10179155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // Find first eligible node from unused
10189155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // TOD: What if there are more eligible? We need to process ALL views
10199155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    // and all nodes in one go here
10209155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
10219155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    UiViewElementNode matching = null;
10229155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    for (UiViewElementNode candidate : unused) {
10239155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        if (afterNode == null || isAfter(afterNode, candidate)) {
10249155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            if (beforeNode == null || isBefore(beforeNode, candidate)) {
10259155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                matching = candidate;
10269155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                break;
10279155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            }
10289155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        }
10299155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
10309155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
10319155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    if (matching != null) {
10329155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        unused.remove(matching);
10339155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        CanvasViewInfo childView = createView(parentView, child, parentX, parentY,
10349155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                matching);
10359155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        parentView.addChild(childView);
10369155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        afterNode = matching;
10379155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    } else {
10389155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        // We have no node for the view -- what do we do??
10399155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        // Nothing - we only represent stuff in the outline that is in the
10409155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        // source model, not in the render
10419155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
10429155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                }
10439155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            }
10449155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
10459155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // Add zero-bounded boxes for all remaining nodes since they need to show
10469155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // up in the outline, need to be selectable so you can press Delete, etc.
10479155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            if (unused.size() > 0) {
10489155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                Map<UiViewElementNode, Integer> rankMap =
10499155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    new HashMap<UiViewElementNode, Integer>();
10509155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                Map<UiViewElementNode, CanvasViewInfo> infoMap =
10519155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    new HashMap<UiViewElementNode, CanvasViewInfo>();
10529155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                UiElementNode parent = unused.get(0).getUiParent();
10539155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                if (parent != null) {
10549155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    int index = 0;
10559155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    for (UiElementNode child : parent.getUiChildren()) {
10569155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        UiViewElementNode node = (UiViewElementNode) child;
10579155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        rankMap.put(node, index++);
10589155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
10599155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    for (CanvasViewInfo child : parentView.getChildren()) {
10609155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        infoMap.put(child.getUiViewNode(), child);
10619155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
10629155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    List<Integer> usedIndexes = new ArrayList<Integer>();
10639155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    for (UiViewElementNode node : unused) {
10649155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        Integer rank = rankMap.get(node);
10659155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        if (rank != null) {
10669155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            usedIndexes.add(rank);
10679155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        }
10689155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
10699155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    Collections.sort(usedIndexes);
10709155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    for (int i = usedIndexes.size() - 1; i >= 0; i--) {
10719155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        Integer rank = usedIndexes.get(i);
10729155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        UiViewElementNode found = null;
10739155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        for (UiViewElementNode node : unused) {
10749155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            if (rankMap.get(node) == rank) {
10759155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                found = node;
10769155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                break;
10779155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            }
10789155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        }
10799155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        if (found != null) {
10809155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            Rectangle absRect = new Rectangle(parentX, parentY, 0, 0);
10819155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            String name = found.getDescriptor().getXmlLocalName();
10829155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            CanvasViewInfo v = new CanvasViewInfo(parentView, name, null, found,
108380d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye                                    absRect, absRect, null /* viewInfo */);
10849155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            // Find corresponding index in the parent view
10859155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            List<CanvasViewInfo> siblings = parentView.getChildren();
10869155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            int insertPosition = siblings.size();
10879155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            for (int j = siblings.size() - 1; j >= 0; j--) {
10889155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                CanvasViewInfo sibling = siblings.get(j);
10899155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                UiViewElementNode siblingNode = sibling.getUiViewNode();
10909155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                if (siblingNode != null) {
10919155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                    Integer siblingRank = rankMap.get(siblingNode);
10929155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                    if (siblingRank != null && siblingRank < rank) {
10939155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                        insertPosition = j + 1;
10949155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                        break;
10959155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                    }
10969155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                                }
10979155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            }
10989155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            parentView.addChildAt(insertPosition, v);
10999155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            unused.remove(found);
11009155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        }
11019155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
11029155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                }
11039155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                // Add in any remaining
11049155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                for (UiViewElementNode node : unused) {
11059155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    Rectangle absRect = new Rectangle(parentX, parentY, 0, 0);
11069155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    String name = node.getDescriptor().getXmlLocalName();
11079155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    CanvasViewInfo v = new CanvasViewInfo(parentView, name, null, node, absRect,
110880d9301c2e874b29889c41adb0623666cf534fa0Tor Norbye                            absRect, null /* viewInfo */);
11099155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    parentView.addChild(v);
11108386da5e451eec396d0e71576e7366a98017674fTor Norbye                }
11118386da5e451eec396d0e71576e7366a98017674fTor Norbye            }
11128386da5e451eec396d0e71576e7366a98017674fTor Norbye        }
11139155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
11149155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        private boolean isBefore(UiViewElementNode beforeNode, UiViewElementNode candidate) {
11159155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            UiElementNode parent = candidate.getUiParent();
11169155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            if (parent != null) {
11179155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                for (UiElementNode sibling : parent.getUiChildren()) {
11189155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    if (sibling == beforeNode) {
11199155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        return false;
11209155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    } else if (sibling == candidate) {
11219155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        return true;
11229155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
11239155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                }
11249155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            }
11259155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            return false;
11268386da5e451eec396d0e71576e7366a98017674fTor Norbye        }
11278386da5e451eec396d0e71576e7366a98017674fTor Norbye
11289155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        private boolean isAfter(UiViewElementNode afterNode, UiViewElementNode candidate) {
11299155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            UiElementNode parent = candidate.getUiParent();
11308386da5e451eec396d0e71576e7366a98017674fTor Norbye            if (parent != null) {
11319155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                for (UiElementNode sibling : parent.getUiChildren()) {
11329155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    if (sibling == afterNode) {
11339155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        return true;
11349155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    } else if (sibling == candidate) {
11359155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                        return false;
11369155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    }
11379155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                }
11388386da5e451eec396d0e71576e7366a98017674fTor Norbye            }
11399155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            return false;
11409155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        }
11419155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye
11429155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        private UiViewElementNode nextViewNode(List<ViewInfo> children, int index) {
11439155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            int size = children.size();
11449155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            for (; index < size; index++) {
11459155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                ViewInfo child = children.get(index);
11469155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                if (child.getCookie() instanceof UiViewElementNode) {
11479155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    return (UiViewElementNode) child.getCookie();
11489155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                }
11498386da5e451eec396d0e71576e7366a98017674fTor Norbye            }
11508386da5e451eec396d0e71576e7366a98017674fTor Norbye
11518386da5e451eec396d0e71576e7366a98017674fTor Norbye            return null;
11528386da5e451eec396d0e71576e7366a98017674fTor Norbye        }
11538386da5e451eec396d0e71576e7366a98017674fTor Norbye
11549155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        /** Search for a subtree with valid keys and add those subtrees */
11559155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye        private CanvasViewInfo addKeyedSubtrees(CanvasViewInfo parent, ViewInfo viewInfo,
11569155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                int parentX, int parentY) {
11579155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // We don't include MergeCookies when searching down for the first non-null key,
11589155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // since this means we are in a "Show Included In" context, and the include tag itself
11599155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // (which the merge cookie is pointing to) is still in the including-document rather
11609155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // than the included document. Therefore, we only accept real UiViewElementNodes here,
11619155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            // not MergeCookies.
11629155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            if (viewInfo.getCookie() != null) {
11639155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                CanvasViewInfo subtree = createSubtree(parent, viewInfo, parentX, parentY);
11648f21dc3e82c298e727b8813e10896e55f82406f9Tor Norbye                if (parent != null && subtree != null) {
11659155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    parent.mChildren.add(subtree);
11669155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                }
11679155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                return subtree;
11689155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            } else {
11699155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                for (ViewInfo child : viewInfo.getChildren()) {
11709155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                    addKeyedSubtrees(parent, child, parentX + viewInfo.getLeft(), parentY
11719155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                            + viewInfo.getTop());
11729155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                }
11738386da5e451eec396d0e71576e7366a98017674fTor Norbye
11749155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye                return null;
11759155df4effaf2079215d5f77dc2e70dd145a6fdfTor Norbye            }
11768386da5e451eec396d0e71576e7366a98017674fTor Norbye        }
11778386da5e451eec396d0e71576e7366a98017674fTor Norbye    }
11788fbb33e21e7b6c0d13c9b368c4a24f6c13809027Raphael}
1179