You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2012/12/08 15:56:01 UTC

[16/53] [partial] ISIS-188: making structure of component viewers consistent

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/TableRowLayout.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/TableRowLayout.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/TableRowLayout.java
new file mode 100644
index 0000000..9c4d26a
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/TableRowLayout.java
@@ -0,0 +1,93 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.table;
+
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.drawing.Size;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.base.Layout;
+
+public class TableRowLayout implements Layout {
+    private final TableAxis axis;
+
+    public TableRowLayout(final TableAxis axis) {
+        this.axis = axis;
+    }
+
+    @Override
+    public Size getRequiredSize(final View row) {
+        int maxHeight = 0;
+        int totalWidth = 0;
+        final View[] cells = row.getSubviews();
+        final int maxBaseline = maxBaseline(cells);
+
+        for (int i = 0; i < cells.length; i++) {
+            totalWidth += axis.getColumnWidth(i);
+
+            final Size s = cells[i].getRequiredSize(Size.createMax());// TODO
+                                                                      // Need to
+                                                                      // pass in
+                                                                      // a max
+                                                                      // size
+                                                                      // (is 0
+                                                                      // at the
+                                                                      // moment)
+            final int b = cells[i].getBaseline();
+            final int baselineOffset = Math.max(0, maxBaseline - b);
+            maxHeight = Math.max(maxHeight, s.getHeight() + baselineOffset);
+        }
+
+        return new Size(totalWidth, maxHeight);
+    }
+
+    @Override
+    public void layout(final View row, final Size maximumSize) {
+        final View[] cells = row.getSubviews();
+        final int maxBaseline = maxBaseline(cells);
+
+        int x = 0;
+        for (int i = 0; i < cells.length; i++) {
+            final View cell = cells[i];
+            final Size s = cell.getRequiredSize(Size.createMax()); // TODO Need
+                                                                   // to pass in
+                                                                   // a max size
+                                                                   // (is 0 at
+                                                                   // the
+                                                                   // moment)
+            s.setWidth(axis.getColumnWidth(i));
+            cell.setSize(s);
+
+            final int b = cell.getBaseline();
+            final int baselineOffset = Math.max(0, maxBaseline - b);
+            cell.setLocation(new Location(x, baselineOffset));
+
+            x += s.getWidth();
+        }
+    }
+
+    private int maxBaseline(final View[] cells) {
+        int maxBaseline = 0;
+        for (final View cell : cells) {
+            maxBaseline = Math.max(maxBaseline, cell.getBaseline());
+        }
+        return maxBaseline;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/TableRowSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/TableRowSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/TableRowSpecification.java
new file mode 100644
index 0000000..3e1ea41
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/TableRowSpecification.java
@@ -0,0 +1,57 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.table;
+
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.base.Layout;
+import org.apache.isis.viewer.dnd.view.composite.CompositeViewSpecification;
+
+public class TableRowSpecification extends CompositeViewSpecification {
+    public TableRowSpecification() {
+        builder = new TableCellBuilder();
+    }
+
+    @Override
+    public Layout createLayout(final Content content, final Axes axes) {
+        return new TableRowLayout(axes.getAxis(TableAxis.class));
+    }
+
+    @Override
+    public boolean canDisplay(final ViewRequirement requirement) {
+        return requirement.isObject();
+    }
+
+    @Override
+    public String getName() {
+        return "Table Row";
+    }
+
+    @Override
+    public boolean isReplaceable() {
+        return false;
+    }
+
+    @Override
+    public boolean isSubView() {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/TypeBasedColumnWidthStrategy.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/TypeBasedColumnWidthStrategy.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/TypeBasedColumnWidthStrategy.java
new file mode 100644
index 0000000..29cb6a5
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/TypeBasedColumnWidthStrategy.java
@@ -0,0 +1,71 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.table;
+
+import java.util.Hashtable;
+
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+
+public class TypeBasedColumnWidthStrategy implements ColumnWidthStrategy {
+    private final Hashtable<ObjectSpecification, Integer> types = new Hashtable<ObjectSpecification, Integer>();
+
+    public TypeBasedColumnWidthStrategy() {
+        /*
+         * ObjectSpecificationLoader loader = Isis.getSpecificationLoader();
+         * addWidth(loader.loadSpecification(Logical.class), 25);
+         * addWidth(loader.loadSpecification(Date.class), 65);
+         * addWidth(loader.loadSpecification(Time.class), 38);
+         * addWidth(loader.loadSpecification(DateTime.class), 100);
+         * addWidth(loader.loadSpecification(TextString.class), 80);
+         */
+    }
+
+    public void addWidth(final ObjectSpecification specification, final int width) {
+        types.put(specification, new Integer(width));
+    }
+
+    @Override
+    public int getMaximumWidth(final int i, final ObjectAssociation specification) {
+        return 0;
+    }
+
+    @Override
+    public int getMinimumWidth(final int i, final ObjectAssociation specification) {
+        return 15;
+    }
+
+    // TODO improve the width determination
+    @Override
+    public int getPreferredWidth(final int i, final ObjectAssociation specification) {
+        final ObjectSpecification type = specification.getSpecification();
+        if (type == null) {
+            return 200;
+        }
+        final Integer t = types.get(type);
+        if (t != null) {
+            return t.intValue();
+        } else if (type.isNotCollection()) {
+            return 120;
+        } else {
+            return 100;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/WindowTableSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/WindowTableSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/WindowTableSpecification.java
new file mode 100644
index 0000000..94a4d08
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/table/WindowTableSpecification.java
@@ -0,0 +1,77 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.table;
+
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.border.ScrollBorder;
+import org.apache.isis.viewer.dnd.view.composite.CompositeViewDecorator;
+import org.apache.isis.viewer.dnd.viewer.basic.TableFocusManager;
+
+public class WindowTableSpecification extends AbstractTableSpecification {
+    public WindowTableSpecification() {
+        addViewDecorator(new CompositeViewDecorator() {
+            @Override
+            public View decorate(final View view, final Axes axes) {
+                final ScrollBorder scrollingView = new ScrollBorder(view);
+                final View viewWithWindowBorder = scrollingView;
+                // note - the next call needs to be after the creation of the
+                // window border so
+                // that it exists when the header is set up
+                scrollingView.setTopHeader(new TableHeader(view.getContent(), axes.getAxis(TableAxis.class)));
+                viewWithWindowBorder.setFocusManager(new TableFocusManager(viewWithWindowBorder));
+                return viewWithWindowBorder;
+            }
+        });
+
+    }
+
+    /*
+     * @Override public View doCreateView(final View view, final Content
+     * content, final ViewAxis axis) { if (true) return view;
+     * 
+     * final ScrollBorder scrollingView = new ScrollBorder(view); View
+     * viewWithWindowBorder = scrollingView; // note - the next call needs to be
+     * after the creation of the window border so // that it exists when the
+     * header is set up scrollingView.setTopHeader(new TableHeader(content,
+     * view.getViewAxisForChildren())); viewWithWindowBorder.setFocusManager(new
+     * TableFocusManager(viewWithWindowBorder)); return viewWithWindowBorder; }
+     * 
+     * protected View decorateView(View view) { super.decorateView(view);
+     * 
+     * final ScrollBorder scrollingView = new ScrollBorder(view); View
+     * viewWithWindowBorder = scrollingView; // note - the next call needs to be
+     * after the creation of the window border so // that it exists when the
+     * header is set up scrollingView.setTopHeader(new
+     * TableHeader(view.getContent(), view.getViewAxisForChildren()));
+     * viewWithWindowBorder.setFocusManager(new
+     * TableFocusManager(viewWithWindowBorder)); return viewWithWindowBorder; }
+     */
+    @Override
+    public String getName() {
+        return "Table";
+    }
+
+    @Override
+    public boolean isReplaceable() {
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/toolbar/ToolbarView.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/toolbar/ToolbarView.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/toolbar/ToolbarView.java
new file mode 100644
index 0000000..ad1b393
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/toolbar/ToolbarView.java
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.toolbar;
+
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.drawing.Size;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewConstants;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.composite.CompositeView;
+
+public class ToolbarView extends CompositeView {
+
+    public ToolbarView(final Content content, final ViewSpecification specification) {
+        super(content, specification);
+    }
+
+    @Override
+    protected void buildView() {
+    }
+
+    @Override
+    protected void doLayout(final Size maximumSize) {
+        int x = ViewConstants.HPADDING;
+        int y = 0;
+        int lineHeight = 0;
+        for (final View button : getSubviews()) {
+            final Size buttonSize = button.getRequiredSize(Size.createMax());
+            if (x + buttonSize.getWidth() >= maximumSize.getWidth()) {
+                x = ViewConstants.HPADDING;
+                y += lineHeight + ViewConstants.VPADDING;
+                lineHeight = 0;
+            }
+            button.setSize(buttonSize);
+            button.setLocation(new Location(x, y));
+            x += buttonSize.getWidth() + ViewConstants.HPADDING;
+            lineHeight = Math.max(lineHeight, buttonSize.getHeight());
+        }
+    }
+
+    @Override
+    public Size requiredSize(final Size availableSpace) {
+        int lineHeight = 0;
+        int lineWidth = ViewConstants.HPADDING;
+        final Size requiredSize = new Size();
+        for (final View button : getSubviews()) {
+            final Size buttonSize = button.getRequiredSize(availableSpace);
+            lineWidth += buttonSize.getWidth() + ViewConstants.HPADDING;
+            if (lineWidth >= availableSpace.getWidth()) {
+                lineWidth = ViewConstants.HPADDING;
+                requiredSize.extendHeight(lineHeight + ViewConstants.VPADDING);
+                lineHeight = 0;
+            }
+            lineHeight = Math.max(lineHeight, buttonSize.getHeight());
+            requiredSize.ensureWidth(lineWidth);
+        }
+        requiredSize.extendHeight(lineHeight + ViewConstants.VPADDING);
+        return requiredSize;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/ClosedCollectionNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/ClosedCollectionNodeSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/ClosedCollectionNodeSpecification.java
new file mode 100644
index 0000000..152ea53
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/ClosedCollectionNodeSpecification.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacetUtils;
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.collection.CollectionContent;
+
+/**
+ * Specification for a tree node that will display a closed collection as a root
+ * node or within an object.
+ * 
+ * @see org.apache.isis.viewer.dnd.tree.OpenCollectionNodeSpecification for
+ *      displaying an open collection within an object.
+ */
+public class ClosedCollectionNodeSpecification extends NodeSpecification {
+    @Override
+    public boolean canDisplay(final ViewRequirement requirement) {
+        return requirement.isCollection() && requirement.hasReference();
+    }
+
+    @Override
+    public int canOpen(final Content content) {
+        final ObjectAdapter collection = ((CollectionContent) content).getCollection();
+        if (collection.isGhost()) {
+            return UNKNOWN;
+        } else {
+            final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
+            return facet.size(collection) > 0 ? CAN_OPEN : CANT_OPEN;
+        }
+    }
+
+    @Override
+    protected View createNodeView(final Content content, final Axes axes) {
+        final View treeLeafNode = new LeafNodeView(content, this);
+        return treeLeafNode;
+    }
+
+    @Override
+    public String getName() {
+        return "Collection tree node - closed";
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/ClosedObjectNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/ClosedObjectNodeSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/ClosedObjectNodeSpecification.java
new file mode 100644
index 0000000..57f5114
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/ClosedObjectNodeSpecification.java
@@ -0,0 +1,94 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree;
+
+import java.util.List;
+
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facets.object.bounded.BoundedFacetUtils;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociationFilters;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.ObjectContent;
+import org.apache.isis.viewer.dnd.view.SubviewDecorator;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.border.SelectObjectBorder;
+
+/**
+ * Specification for a tree node that will display a closed object as a root
+ * node or within an object. This will indicate that the created view can be
+ * opened if: one of it fields is a collection; it is set up to show objects
+ * within objects and one of the fields is an object but it is not a lookup.
+ * 
+ * @see org.apache.isis.viewer.dnd.tree.OpenObjectNodeSpecification for
+ *      displaying an open collection as part of an object.
+ */
+class ClosedObjectNodeSpecification extends NodeSpecification {
+    private final boolean showObjectContents;
+    private final SubviewDecorator decorator = new SelectObjectBorder.Factory();
+
+    // REVIEW: should provide this rendering context, rather than hardcoding.
+    // the net effect currently is that class members annotated with 
+    // @Hidden(where=Where.ANYWHERE) or @Disabled(where=Where.ANYWHERE) will indeed
+    // be hidden/disabled, but will be visible/enabled (perhaps incorrectly) 
+    // for any other value for Where
+    private final Where where = Where.ANYWHERE;
+
+    public ClosedObjectNodeSpecification(final boolean showObjectContents) {
+        this.showObjectContents = showObjectContents;
+    }
+
+    @Override
+    public boolean canDisplay(final ViewRequirement requirement) {
+        return requirement.isObject() && requirement.hasReference();
+    }
+
+    @Override
+    public int canOpen(final Content content) {
+        final ObjectAdapter object = ((ObjectContent) content).getObject();
+        final List<ObjectAssociation> fields = object.getSpecification().getAssociations(ObjectAssociationFilters.dynamicallyVisible(IsisContext.getAuthenticationSession(), object, where));
+        for (int i = 0; i < fields.size(); i++) {
+            if (fields.get(i).isOneToManyAssociation()) {
+                return CAN_OPEN;
+            }
+
+            if (showObjectContents && fields.get(i).isOneToOneAssociation() && !(BoundedFacetUtils.isBoundedSet(object.getSpecification()))) {
+                return CAN_OPEN;
+            }
+        }
+        return CANT_OPEN;
+    }
+
+    @Override
+    protected View createNodeView(final Content content, final Axes axes) {
+        View treeLeafNode = new LeafNodeView(content, this);
+        treeLeafNode = decorator.decorate(axes, treeLeafNode);
+        return treeLeafNode;
+    }
+
+    @Override
+    public String getName() {
+        return "Object tree node - closed";
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/CompositeNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/CompositeNodeSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/CompositeNodeSpecification.java
new file mode 100644
index 0000000..d824cbf
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/CompositeNodeSpecification.java
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree;
+
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.CompositeViewSpecification;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.base.Layout;
+import org.apache.isis.viewer.dnd.view.composite.CompositeViewUsingBuilder;
+import org.apache.isis.viewer.dnd.view.composite.ViewBuilder;
+
+public abstract class CompositeNodeSpecification extends NodeSpecification implements CompositeViewSpecification {
+    protected ViewBuilder builder;
+    private NodeSpecification collectionLeafNodeSpecification;
+    private NodeSpecification objectLeafNodeSpecification;
+
+    public void setCollectionSubNodeSpecification(final NodeSpecification collectionLeafNodeSpecification) {
+        this.collectionLeafNodeSpecification = collectionLeafNodeSpecification;
+    }
+
+    public void setObjectSubNodeSpecification(final NodeSpecification objectLeafNodeSpecification) {
+        this.objectLeafNodeSpecification = objectLeafNodeSpecification;
+    }
+
+    public void createAxes(final Content content, final Axes axes) {
+    }
+
+    @Override
+    protected View createNodeView(final Content content, final Axes axes) {
+        final CompositeViewUsingBuilder view = new CompositeViewUsingBuilder(content, this, axes, createLayout(content, axes), builder);
+        return view;
+    }
+
+    protected abstract Layout createLayout(Content content, Axes axes);
+
+    /*
+     * public View createView(final Content content, Axes axes, int fieldNumber)
+     * { ViewRequirement requirement = new ViewRequirement(content,
+     * ViewRequirement.CLOSED); if
+     * (collectionLeafNodeSpecification.canDisplay(content, requirement )) {
+     * return collectionLeafNodeSpecification.createView(content, axes, -1); }
+     * 
+     * if (objectLeafNodeSpecification.canDisplay(content, requirement)) {
+     * return objectLeafNodeSpecification.createView(content, axes, -1); }
+     * 
+     * return null; }
+     */
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/EmptyNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/EmptyNodeSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/EmptyNodeSpecification.java
new file mode 100644
index 0000000..98349b5
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/EmptyNodeSpecification.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree;
+
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+
+/**
+ * A simple specification that always returns false when asked if it can display
+ * any content.
+ * 
+ * @see #canDisplay(ViewRequirement)
+ */
+public class EmptyNodeSpecification extends NodeSpecification {
+
+    @Override
+    public int canOpen(final Content content) {
+        return CANT_OPEN;
+    }
+
+    @Override
+    protected View createNodeView(final Content content, final Axes axes) {
+        return null;
+    }
+
+    @Override
+    public boolean canDisplay(final ViewRequirement requirement) {
+        return false;
+    }
+
+    @Override
+    public String getName() {
+        return "Empty tree node";
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/LeafNodeView.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/LeafNodeView.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/LeafNodeView.java
new file mode 100644
index 0000000..f461111
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/LeafNodeView.java
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree;
+
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.FocusManager;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.base.ObjectView;
+
+class LeafNodeView extends ObjectView {
+
+    private FocusManager focusManager;
+
+    public LeafNodeView(final Content content, final ViewSpecification design) {
+        super(content, design);
+    }
+
+    @Override
+    public FocusManager getFocusManager() {
+        return focusManager;
+    }
+
+    @Override
+    public void setFocusManager(final FocusManager focusManager) {
+        this.focusManager = focusManager;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/ListWithDetailSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/ListWithDetailSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/ListWithDetailSpecification.java
new file mode 100644
index 0000000..2fdb08c
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/ListWithDetailSpecification.java
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree;
+
+import org.apache.isis.viewer.dnd.list.SimpleListSpecification;
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.border.SelectObjectBorder;
+import org.apache.isis.viewer.dnd.view.composite.MasterDetailPanel;
+
+public class ListWithDetailSpecification implements ViewSpecification {
+    private final SimpleListSpecification leftHandSideSpecification;
+
+    public ListWithDetailSpecification() {
+        leftHandSideSpecification = new SimpleListSpecification();
+        leftHandSideSpecification.addSubviewDecorator(new SelectObjectBorder.Factory());
+    }
+
+    @Override
+    public boolean canDisplay(final ViewRequirement requirement) {
+        return requirement.isCollection() && requirement.isOpen();
+    }
+
+    @Override
+    public View createView(final Content content, final Axes axes, final int sequence) {
+        return new MasterDetailPanel(content, this, leftHandSideSpecification);
+    }
+
+    @Override
+    public String getName() {
+        return "List and details";
+    }
+
+    @Override
+    public boolean isAligned() {
+        return false;
+    }
+
+    @Override
+    public boolean isOpen() {
+        return true;
+    }
+
+    @Override
+    public boolean isReplaceable() {
+        return true;
+    }
+
+    @Override
+    public boolean isResizeable() {
+        return false;
+    }
+
+    @Override
+    public boolean isSubView() {
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/NodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/NodeSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/NodeSpecification.java
new file mode 100644
index 0000000..dfe14ee
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/NodeSpecification.java
@@ -0,0 +1,76 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree;
+
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.viewer.basic.NullFocusManager;
+
+abstract class NodeSpecification implements ViewSpecification {
+    public static final int CAN_OPEN = 1;
+    public static final int CANT_OPEN = 2;
+    public static final int UNKNOWN = 0;
+    private ViewSpecification replacementNodeSpecification;
+
+    public abstract int canOpen(final Content content);
+
+    protected abstract View createNodeView(final Content content, Axes axes);
+
+    @Override
+    public final View createView(final Content content, final Axes axes, final int sequence) {
+        final View view = createNodeView(content, axes);
+        final TreeNodeBorder newView = new TreeNodeBorder(view, replacementNodeSpecification);
+        newView.setFocusManager(new NullFocusManager());
+
+        return newView;
+    }
+
+    @Override
+    public boolean isAligned() {
+        return false;
+    }
+
+    @Override
+    public boolean isOpen() {
+        return false;
+    }
+
+    @Override
+    public boolean isReplaceable() {
+        return false;
+    }
+
+    @Override
+    public boolean isResizeable() {
+        return false;
+    }
+
+    @Override
+    public boolean isSubView() {
+        return true;
+    }
+
+    final void setReplacementNodeSpecification(final ViewSpecification replacementNodeSpecification) {
+        this.replacementNodeSpecification = replacementNodeSpecification;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/OpenCollectionNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/OpenCollectionNodeSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/OpenCollectionNodeSpecification.java
new file mode 100644
index 0000000..ddd8af2
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/OpenCollectionNodeSpecification.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacetUtils;
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.base.Layout;
+import org.apache.isis.viewer.dnd.view.composite.CollectionElementBuilder;
+import org.apache.isis.viewer.dnd.view.composite.StackLayout;
+
+/**
+ * Specification for a tree node that will display an open collection as a root
+ * node or within an object.
+ * 
+ * @see org.apache.isis.viewer.dnd.tree.ClosedCollectionNodeSpecification for
+ *      displaying a closed collection within an object.
+ */
+public class OpenCollectionNodeSpecification extends CompositeNodeSpecification {
+    /**
+     * A collection tree can only be displayed for a collection that has
+     * elements.
+     */
+    @Override
+    public boolean canDisplay(final ViewRequirement requirement) {
+        final ObjectAdapter collection = requirement.getAdapter();
+        if (collection == null) {
+            return false;
+        } else {
+            final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
+            return facet != null && facet.size(collection) > 0;
+        }
+    }
+
+    public OpenCollectionNodeSpecification() {
+        builder = new CollectionElementBuilder(this);
+    }
+
+    @Override
+    public Layout createLayout(final Content content, final Axes axes) {
+        return new StackLayout();
+    }
+
+    @Override
+    public boolean isOpen() {
+        return true;
+    }
+
+    @Override
+    public boolean isSubView() {
+        return false;
+    }
+
+    @Override
+    public int canOpen(final Content content) {
+        return CAN_OPEN;
+    }
+
+    @Override
+    public String getName() {
+        return "Collection tree node - open";
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/OpenObjectNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/OpenObjectNodeSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/OpenObjectNodeSpecification.java
new file mode 100644
index 0000000..a07bae3
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/OpenObjectNodeSpecification.java
@@ -0,0 +1,109 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree;
+
+import java.util.List;
+
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociationFilters;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.SubviewDecorator;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.base.Layout;
+import org.apache.isis.viewer.dnd.view.border.SelectObjectBorder;
+import org.apache.isis.viewer.dnd.view.composite.ObjectFieldBuilder;
+import org.apache.isis.viewer.dnd.view.composite.StackLayout;
+
+/**
+ * Specification for a tree node that will display an open object as a root node
+ * or within an object.
+ * 
+ * @see org.apache.isis.viewer.dnd.tree.ClosedObjectNodeSpecification for
+ *      displaying a closed collection within an object.
+ */
+public class OpenObjectNodeSpecification extends CompositeNodeSpecification {
+
+    private final SubviewDecorator decorator = new SelectObjectBorder.Factory();
+
+    // REVIEW: should provide this rendering context, rather than hardcoding.
+    // the net effect currently is that class members annotated with 
+    // @Hidden(where=Where.ANYWHERE) or @Disabled(where=Where.ANYWHERE) will indeed
+    // be hidden/disabled, but will be visible/enabled (perhaps incorrectly) 
+    // for any other value for Where
+    private final Where where = Where.ANYWHERE;
+
+    public OpenObjectNodeSpecification() {
+        builder = new ObjectFieldBuilder(this);
+    }
+
+    @Override
+    public Layout createLayout(final Content content, final Axes axes) {
+        return new StackLayout();
+    }
+
+    /**
+     * This is only used to control root nodes. Therefore a object tree can only
+     * be displayed for an object with fields that are collections.
+     */
+    @Override
+    public boolean canDisplay(final ViewRequirement requirement) {
+        if (requirement.isObject() && requirement.hasReference()) {
+            final ObjectAdapter object = requirement.getAdapter();
+            final List<ObjectAssociation> fields = object.getSpecification().getAssociations(ObjectAssociationFilters.dynamicallyVisible(IsisContext.getAuthenticationSession(), object, where));
+            for (int i = 0; i < fields.size(); i++) {
+                if (fields.get(i).isOneToManyAssociation()) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    protected View createNodeView(final Content content, final Axes axes) {
+        return decorator.decorate(axes, super.createNodeView(content, axes));
+    }
+
+    @Override
+    public int canOpen(final Content content) {
+        return CAN_OPEN;
+    }
+
+    @Override
+    public boolean isOpen() {
+        return true;
+    }
+
+    @Override
+    public boolean isSubView() {
+        return false;
+    }
+
+    @Override
+    public String getName() {
+        return "Object tree node - open";
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeDisplayRules.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeDisplayRules.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeDisplayRules.java
new file mode 100644
index 0000000..4e4479b
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeDisplayRules.java
@@ -0,0 +1,90 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.consent.Allow;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.facets.object.bounded.BoundedFacetUtils;
+import org.apache.isis.core.metamodel.spec.ActionType;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.view.UserAction;
+import org.apache.isis.viewer.dnd.view.UserActionSet;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.Workspace;
+
+public class TreeDisplayRules {
+    private static boolean showCollectionsOnly = false;
+
+    private TreeDisplayRules() {
+    }
+
+    public static void menuOptions(final UserActionSet options) {
+        // TODO fix and remove following line
+        if (true) {
+            return;
+        }
+
+        final UserAction option = new UserAction() {
+            @Override
+            public void execute(final Workspace workspace, final View view, final Location at) {
+                showCollectionsOnly = !showCollectionsOnly;
+            }
+
+            @Override
+            public String getName(final View view) {
+                return showCollectionsOnly ? "Show collections only" : "Show all references";
+            }
+
+            @Override
+            public Consent disabled(final View view) {
+                return Allow.DEFAULT;
+            }
+
+            @Override
+            public String getDescription(final View view) {
+                return "This option makes the system only show collections within the trees, and not single elements";
+            }
+
+            @Override
+            public ActionType getType() {
+                return ActionType.USER;
+            }
+
+            @Override
+            public String getHelp(final View view) {
+                return "";
+            }
+        };
+        options.add(option);
+    }
+
+    public static boolean isCollectionsOnly() {
+        return showCollectionsOnly;
+    }
+
+    public static boolean canDisplay(final ObjectAdapter object) {
+        final boolean lookupView = object != null && BoundedFacetUtils.isBoundedSet(object.getSpecification());
+        final boolean showNonCollections = !TreeDisplayRules.isCollectionsOnly();
+        final boolean objectView = object instanceof ObjectAdapter && showNonCollections;
+        final boolean collectionView = object.getSpecification().isParentedOrFreeCollection();
+        return (objectView || collectionView) && !lookupView;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeNodeBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeNodeBorder.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeNodeBorder.java
new file mode 100644
index 0000000..c0bf6cc
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeNodeBorder.java
@@ -0,0 +1,353 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.commons.debug.DebugBuilder;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.ResolveState;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+import org.apache.isis.viewer.dnd.drawing.Bounds;
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+import org.apache.isis.viewer.dnd.drawing.Color;
+import org.apache.isis.viewer.dnd.drawing.ColorsAndFonts;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.drawing.Offset;
+import org.apache.isis.viewer.dnd.drawing.Size;
+import org.apache.isis.viewer.dnd.drawing.Text;
+import org.apache.isis.viewer.dnd.interaction.ViewDragImpl;
+import org.apache.isis.viewer.dnd.view.Click;
+import org.apache.isis.viewer.dnd.view.DragEvent;
+import org.apache.isis.viewer.dnd.view.DragStart;
+import org.apache.isis.viewer.dnd.view.ObjectContent;
+import org.apache.isis.viewer.dnd.view.Placement;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.UserActionSet;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewAreaType;
+import org.apache.isis.viewer.dnd.view.ViewConstants;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.Workspace;
+import org.apache.isis.viewer.dnd.view.base.AbstractBorder;
+import org.apache.isis.viewer.dnd.view.base.DragViewOutline;
+import org.apache.isis.viewer.dnd.view.base.IconGraphic;
+import org.apache.isis.viewer.dnd.view.collection.CollectionContent;
+import org.apache.isis.viewer.dnd.view.collection.CollectionElement;
+import org.apache.isis.viewer.dnd.view.content.FieldContent;
+import org.apache.isis.viewer.dnd.view.field.OneToManyField;
+import org.apache.isis.viewer.dnd.view.option.UserActionAbstract;
+import org.apache.isis.viewer.dnd.view.text.ObjectTitleText;
+import org.apache.isis.viewer.dnd.view.text.TitleText;
+
+// TODO use ObjectBorder to provide the basic border functionality
+public class TreeNodeBorder extends AbstractBorder {
+    private static final int BORDER = 13;
+    private static final int BOX_PADDING = 2;
+    private static final int BOX_SIZE = 9;
+    private static final int BOX_X_OFFSET = 5;
+    private final static Text LABEL_STYLE = Toolkit.getText(ColorsAndFonts.TEXT_NORMAL);
+    private static final Logger LOG = Logger.getLogger(TreeNodeBorder.class);
+    private final int baseline;
+    private final IconGraphic icon;
+    private final ViewSpecification replaceWithSpecification;
+    private final TitleText text;
+
+    public TreeNodeBorder(final View wrappedView, final ViewSpecification replaceWith) {
+        super(wrappedView);
+
+        replaceWithSpecification = replaceWith;
+
+        icon = new IconGraphic(this, LABEL_STYLE);
+        text = new ObjectTitleText(this, LABEL_STYLE);
+        final int height = icon.getSize().getHeight();
+
+        baseline = icon.getBaseline() + 1;
+
+        left = 22;
+        right = 0 + BORDER;
+        top = height + 2;
+        bottom = 0;
+    }
+
+    private int canOpen() {
+        return ((NodeSpecification) getSpecification()).canOpen(getContent());
+    }
+
+    @Override
+    protected Bounds contentArea() {
+        return new Bounds(getLeft(), getTop(), wrappedView.getSize().getWidth(), wrappedView.getSize().getHeight());
+    }
+
+    @Override
+    public void debugDetails(final DebugBuilder debug) {
+        debug.append("TreeNodeBorder " + left + " pixels\n");
+        debug.append("           titlebar " + (top) + " pixels\n");
+        debug.append("           replace with  " + replaceWithSpecification);
+        debug.append("           text " + text);
+        debug.append("           icon " + icon);
+        super.debugDetails(debug);
+
+    }
+
+    @Override
+    public DragEvent dragStart(final DragStart drag) {
+        if (drag.getLocation().getX() > getSize().getWidth() - right) {
+            final View dragOverlay = new DragViewOutline(getView());
+            return new ViewDragImpl(this, new Offset(drag.getLocation()), dragOverlay);
+        } else if (overBorder(drag.getLocation())) {
+            return Toolkit.getViewFactory().createDragContentOutline(this, drag.getLocation());
+        } else {
+            return super.dragStart(drag);
+        }
+    }
+
+    @Override
+    public void draw(final Canvas canvas) {
+        final Color secondary1 = Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY1);
+        final Color secondary2 = Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY2);
+        final Color secondary3 = Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY3);
+        /*
+         * REVIEW if (getViewAxis(TableAxis.class) != null) { if
+         * (((SelectableViewAxis)
+         * getViewAxis(SelectableViewAxis.class)).isSelected(getView())) {
+         * canvas.drawSolidRectangle(left, 0, getSize().getWidth() - left, top,
+         * Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY2)); secondary2 =
+         * secondary1; } }
+         */
+        if (getState().isObjectIdentified()) {
+            canvas.drawRectangle(left, 0, getSize().getWidth() - left, top, secondary2);
+
+            final int xExtent = getSize().getWidth();
+            canvas.drawSolidRectangle(xExtent - BORDER + 1, 1, BORDER - 2, top - 2, secondary3);
+            canvas.drawLine(xExtent - BORDER, 0, xExtent - BORDER, top - 2, secondary2);
+        }
+
+        // lines
+        int x = 0;
+        final int y = top / 2;
+        canvas.drawLine(x, y, x + left, y, secondary2);
+
+        final boolean isOpen = getSpecification().isOpen();
+        final int canOpen = canOpen();
+        final boolean addBox = isOpen || canOpen != NodeSpecification.CANT_OPEN;
+        if (addBox) {
+            x += BOX_X_OFFSET;
+            canvas.drawLine(x, y, x + BOX_SIZE - 1, y, secondary3);
+            canvas.drawSolidRectangle(x, y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE, Toolkit.getColor(ColorsAndFonts.COLOR_WHITE));
+            canvas.drawRectangle(x, y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE, secondary1);
+
+            if (canOpen == NodeSpecification.UNKNOWN) {
+
+            } else {
+                final Color black = Toolkit.getColor(ColorsAndFonts.COLOR_BLACK);
+                canvas.drawLine(x + BOX_PADDING, y, x + BOX_SIZE - 1 - BOX_PADDING, y, black);
+                if (!isOpen) {
+                    x += BOX_SIZE / 2;
+                    canvas.drawLine(x, y - BOX_SIZE / 2 + BOX_PADDING, x, y + BOX_SIZE / 2 - BOX_PADDING, black);
+                }
+            }
+        }
+
+        final View[] nodes = getSubviews();
+        if (nodes.length > 0) {
+            final int y1 = top / 2;
+            final View node = nodes[nodes.length - 1];
+            final int y2 = top + node.getLocation().getY() + top / 2;
+            canvas.drawLine(left - 1, y1, left - 1, y2, secondary2);
+        }
+
+        // icon & title
+        x = left + 1;
+        icon.draw(canvas, x, baseline);
+        x += icon.getSize().getWidth();
+        final int maxWith = getSize().getWidth() - x;
+        text.draw(canvas, x, baseline, maxWith);
+
+        if (Toolkit.debug) {
+            canvas.drawRectangleAround(getBounds(), Toolkit.getColor(ColorsAndFonts.COLOR_DEBUG_BASELINE));
+        }
+
+        // draw components
+        super.draw(canvas);
+    }
+
+    @Override
+    public void entered() {
+        getState().setContentIdentified();
+        getState().setViewIdentified();
+        wrappedView.entered();
+        markDamaged();
+    }
+
+    @Override
+    public void exited() {
+        getState().clearObjectIdentified();
+        getState().clearViewIdentified();
+        wrappedView.exited();
+        markDamaged();
+    }
+
+    @Override
+    public void firstClick(final Click click) {
+        final int x = click.getLocation().getX();
+        final int y = click.getLocation().getY();
+
+        if (withinBox(x, y)) {
+            if (canOpen() == NodeSpecification.UNKNOWN) {
+                resolveContent();
+                markDamaged();
+            }
+            LOG.debug((getSpecification().isOpen() ? "close" : "open") + " node " + getContent().getAdapter());
+            if (canOpen() == NodeSpecification.CAN_OPEN) {
+                final View newView = replaceWithSpecification.createView(getContent(), getViewAxes(), -1);
+                getParent().replaceView(getView(), newView);
+            }
+            /*
+             * } else if (y < top && x > left && click.button1()) { if
+             * (canOpen() == NodeSpecification.UNKNOWN) { resolveContent();
+             * markDamaged(); } selectNode();
+             */
+        } else {
+            super.firstClick(click);
+        }
+    }
+
+    @Override
+    public int getBaseline() {
+        return wrappedView.getBaseline() + baseline;
+    }
+
+    @Override
+    public Size getRequiredSize(final Size maximumSize) {
+        final Size size = super.getRequiredSize(maximumSize);
+        // size.extendHeight(2 * VPADDING);
+        size.ensureWidth(left + ViewConstants.HPADDING + icon.getSize().getWidth() + text.getSize().getWidth() + ViewConstants.HPADDING + right);
+        return size;
+    }
+
+    @Override
+    public void objectActionResult(final ObjectAdapter result, final Placement placement) {
+        if (getContent() instanceof OneToManyField && result instanceof ObjectAdapter) {
+            // same as InternalCollectionBorder
+            final OneToManyField internalCollectionContent = (OneToManyField) getContent();
+            final OneToManyAssociation field = internalCollectionContent.getOneToManyAssociation();
+            final ObjectAdapter target = ((ObjectContent) getParent().getContent()).getObject();
+
+            final Consent about = field.isValidToAdd(target, result);
+            if (about.isAllowed()) {
+                field.addElement(target, result);
+            }
+        }
+        super.objectActionResult(result, placement);
+    }
+
+    private void resolveContent() {
+        ObjectAdapter parent = getParent().getContent().getAdapter();
+        if (!(parent instanceof ObjectAdapter)) {
+            parent = getParent().getParent().getContent().getAdapter();
+        }
+
+        if (getContent() instanceof FieldContent) {
+            final ObjectAssociation field = ((FieldContent) getContent()).getField();
+            IsisContext.getPersistenceSession().resolveField(parent, field);
+        } else if (getContent() instanceof CollectionContent) {
+            IsisContext.getPersistenceSession().resolveImmediately(parent);
+        } else if (getContent() instanceof CollectionElement) {
+            IsisContext.getPersistenceSession().resolveImmediately(getContent().getAdapter());
+        }
+    }
+
+    @Override
+    public void secondClick(final Click click) {
+        final int x = click.getLocation().getX();
+        final int y = click.getLocation().getY();
+        if (y < top && x > left) {
+            if (canOpen() == NodeSpecification.UNKNOWN) {
+                resolveContent();
+                markDamaged();
+            }
+            final Location location = getAbsoluteLocation();
+            location.translate(click.getLocation());
+            getWorkspace().addWindowFor(getContent().getAdapter(), new Placement(this));
+        } else {
+            super.secondClick(click);
+        }
+    }
+
+    // TODO remove
+    private void selectNode() {
+        /*
+         * if (getViewAxis(SelectableViewAxis.class) != null) {
+         * ((SelectableViewAxis)
+         * getViewAxis(SelectableViewAxis.class)).selected(getView()); }
+         */
+    }
+
+    @Override
+    public String toString() {
+        return wrappedView.toString() + "/TreeNodeBorder";
+    }
+
+    @Override
+    public ViewAreaType viewAreaType(final Location mouseLocation) {
+        final Bounds bounds = new Bounds(left + 1, 0, getSize().getWidth() - left - BORDER, top);
+        if (bounds.contains(mouseLocation)) {
+            return ViewAreaType.CONTENT;
+        } else {
+            return super.viewAreaType(mouseLocation);
+        }
+    }
+
+    @Override
+    public void viewMenuOptions(final UserActionSet options) {
+        super.viewMenuOptions(options);
+        TreeDisplayRules.menuOptions(options);
+
+        options.add(new UserActionAbstract("Select node") {
+            @Override
+            public void execute(final Workspace workspace, final View view, final Location at) {
+                selectNode();
+            }
+
+            @Override
+            public String getDescription(final View view) {
+                return "Show this node in the right-hand pane";
+            }
+        });
+
+        final ObjectAdapter adapter = getView().getContent().getAdapter();
+        if (adapter instanceof ObjectAdapter && (adapter.isGhost() /*|| adapter.getResolveState().isPartlyResolved() */)) {
+            options.add(new UserActionAbstract("Load object") {
+                @Override
+                public void execute(final Workspace workspace, final View view, final Location at) {
+                    resolveContent();
+                }
+            });
+        }
+    }
+
+    private boolean withinBox(final int x, final int y) {
+        return x >= BOX_X_OFFSET && x <= BOX_X_OFFSET + BOX_SIZE && y >= (top - BOX_SIZE) / 2 && y <= (top + BOX_SIZE) / 2;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeSpecification.java
new file mode 100644
index 0000000..29754a4
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeSpecification.java
@@ -0,0 +1,104 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree;
+
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+
+/**
+ * Specification for a tree browser frame with a tree displaying only
+ * collections and objects containing collections.
+ */
+public class TreeSpecification implements ViewSpecification {
+    private final OpenCollectionNodeSpecification openCollection;
+    private final OpenObjectNodeSpecification openObject;
+
+    public TreeSpecification() {
+        final ClosedObjectNodeSpecification closedObject = new ClosedObjectNodeSpecification(false); // ,
+                                                                                                     // new
+                                                                                                     // SelectObjectBorder.Factory());
+        final NodeSpecification closedCollection = new ClosedCollectionNodeSpecification();
+        final EmptyNodeSpecification noNode = new EmptyNodeSpecification();
+
+        openCollection = new OpenCollectionNodeSpecification();
+        openCollection.setCollectionSubNodeSpecification(noNode);
+        openCollection.setObjectSubNodeSpecification(closedObject);
+        openCollection.setReplacementNodeSpecification(closedCollection);
+
+        openObject = new OpenObjectNodeSpecification();
+        openObject.setCollectionSubNodeSpecification(closedCollection);
+        openObject.setObjectSubNodeSpecification(noNode);
+        openObject.setReplacementNodeSpecification(closedObject);
+
+        closedObject.setReplacementNodeSpecification(openObject);
+
+        closedCollection.setReplacementNodeSpecification(openCollection);
+    }
+
+    @Override
+    public boolean canDisplay(final ViewRequirement requirement) {
+        return requirement.is(ViewRequirement.OPEN) && (openCollection.canDisplay(requirement) || openObject.canDisplay(requirement)) && requirement.isExpandable();
+    }
+
+    @Override
+    public View createView(final Content content, final Axes axes, final int sequence) {
+        View rootNode;
+        final ViewRequirement requirement = new ViewRequirement(content, ViewRequirement.CLOSED);
+        if (openCollection.canDisplay(requirement)) {
+            rootNode = openCollection.createView(content, axes, -1);
+        } else {
+            rootNode = openObject.createView(content, axes, -1);
+        }
+        return rootNode;
+    }
+
+    @Override
+    public String getName() {
+        return "Tree (not working)";
+    }
+
+    @Override
+    public boolean isAligned() {
+        return false;
+    }
+
+    @Override
+    public boolean isOpen() {
+        return true;
+    }
+
+    @Override
+    public boolean isReplaceable() {
+        return false;
+    }
+
+    @Override
+    public boolean isResizeable() {
+        return false;
+    }
+
+    @Override
+    public boolean isSubView() {
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeWithDetailSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeWithDetailSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeWithDetailSpecification.java
new file mode 100644
index 0000000..80b34ea
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree/TreeWithDetailSpecification.java
@@ -0,0 +1,78 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree;
+
+import org.apache.isis.viewer.dnd.tree2.CollectionTreeNodeSpecification;
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.composite.MasterDetailPanel;
+
+public class TreeWithDetailSpecification implements ViewSpecification {
+    private final ViewSpecification treeSpecification;
+
+    public TreeWithDetailSpecification() {
+        // treeSpecification = new TreeSpecification();
+        treeSpecification = CollectionTreeNodeSpecification.create()[0];
+    }
+
+    @Override
+    public boolean canDisplay(final ViewRequirement requirement) {
+        return treeSpecification.canDisplay(requirement);
+    }
+
+    @Override
+    public View createView(final Content content, final Axes axes, final int sequence) {
+        return new MasterDetailPanel(content, this, treeSpecification);
+    }
+
+    @Override
+    public String getName() {
+        return "Tree and details (experimental)";
+    }
+
+    @Override
+    public boolean isAligned() {
+        return false;
+    }
+
+    @Override
+    public boolean isOpen() {
+        return true;
+    }
+
+    @Override
+    public boolean isReplaceable() {
+        return true;
+    }
+
+    @Override
+    public boolean isResizeable() {
+        return true;
+    }
+
+    @Override
+    public boolean isSubView() {
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree2/CollectionTreeNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree2/CollectionTreeNodeSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree2/CollectionTreeNodeSpecification.java
new file mode 100644
index 0000000..41e5be3
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree2/CollectionTreeNodeSpecification.java
@@ -0,0 +1,69 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree2;
+
+import org.apache.isis.viewer.dnd.drawing.ColorsAndFonts;
+import org.apache.isis.viewer.dnd.form.ExpandableViewBorder;
+import org.apache.isis.viewer.dnd.icon.IconElementFactory;
+import org.apache.isis.viewer.dnd.icon.SubviewIconSpecification;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.ViewFactory;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.border.IconBorder;
+import org.apache.isis.viewer.dnd.view.border.SelectObjectBorder;
+import org.apache.isis.viewer.dnd.view.composite.AbstractCollectionViewSpecification;
+
+public class CollectionTreeNodeSpecification extends AbstractCollectionViewSpecification {
+
+    public static ViewSpecification[] create() {
+        final CollectionTreeNodeSpecification collectionNodeSpec = new CollectionTreeNodeSpecification();
+        final ObjectTreeNodeSpecification objectNodeSpec = new ObjectTreeNodeSpecification();
+        final SubviewIconSpecification iconSpec = new SubviewIconSpecification();
+
+        collectionNodeSpec.addSubviewDecorator(new SelectObjectBorder.Factory());
+        collectionNodeSpec.addSubviewDecorator(new ExpandableViewBorder.Factory(iconSpec, objectNodeSpec, null));
+        collectionNodeSpec.addViewDecorator(new IconBorder.Factory(Toolkit.getText(ColorsAndFonts.TEXT_NORMAL)));
+        objectNodeSpec.addSubviewDecorator(new SelectObjectBorder.Factory());
+        objectNodeSpec.addSubviewDecorator(new ExpandableViewBorder.Factory(iconSpec, objectNodeSpec, collectionNodeSpec));
+        // objectNodeSpec.addSubviewDecorator(new FieldLabelsDecorator());
+        objectNodeSpec.addViewDecorator(new IconBorder.Factory(Toolkit.getText(ColorsAndFonts.TEXT_NORMAL)));
+        return new ViewSpecification[] { collectionNodeSpec, objectNodeSpec };
+    }
+
+    @Override
+    protected ViewFactory createElementFactory() {
+        return new IconElementFactory();
+    }
+
+    @Override
+    public String getName() {
+        return "Collection tree (experimental)";
+    }
+
+    // TODO this should be available if an item can be given more space
+    /*
+     * @Override public boolean canDisplay(final Content content,
+     * ViewRequirement requirement) { return content.isCollection() &&
+     * requirement.is(ViewRequirement.CLOSED) &&
+     * requirement.is(ViewRequirement.SUBVIEW) &&
+     * requirement.is(ViewRequirement.SUBVIEW); }
+     */
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree2/ObjectTreeNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree2/ObjectTreeNodeSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree2/ObjectTreeNodeSpecification.java
new file mode 100644
index 0000000..ede0af1
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree2/ObjectTreeNodeSpecification.java
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree2;
+
+import org.apache.isis.viewer.dnd.form.AbstractObjectViewSpecification;
+import org.apache.isis.viewer.dnd.icon.IconElementFactory;
+import org.apache.isis.viewer.dnd.view.ViewFactory;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+
+public class ObjectTreeNodeSpecification extends AbstractObjectViewSpecification {
+
+    @Override
+    public boolean canDisplay(final ViewRequirement requirement) {
+        return super.canDisplay(requirement) && requirement.isExpandable();
+    }
+
+    @Override
+    protected ViewFactory createFieldFactory() {
+        return new IconElementFactory();
+    }
+
+    @Override
+    public String getName() {
+        return "Object tree (experimental)";
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree2/TreeNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree2/TreeNodeSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree2/TreeNodeSpecification.java
new file mode 100644
index 0000000..6d1ad2b
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/tree2/TreeNodeSpecification.java
@@ -0,0 +1,122 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.tree2;
+
+import org.apache.isis.viewer.dnd.form.ExpandableViewBorder;
+import org.apache.isis.viewer.dnd.form.ExpandableViewBorder.Factory;
+import org.apache.isis.viewer.dnd.icon.IconElementFactory;
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.SubviewDecorator;
+import org.apache.isis.viewer.dnd.view.UserActionSet;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewFactory;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.base.Layout;
+import org.apache.isis.viewer.dnd.view.composite.CollectionElementBuilder;
+import org.apache.isis.viewer.dnd.view.composite.CompositeViewSpecification;
+import org.apache.isis.viewer.dnd.view.composite.ObjectFieldBuilder;
+import org.apache.isis.viewer.dnd.view.composite.StackLayout;
+import org.apache.isis.viewer.dnd.view.composite.ViewBuilder;
+
+public class TreeNodeSpecification extends CompositeViewSpecification {
+
+    public TreeNodeSpecification() {
+        builder = new ViewBuilder() {
+            ViewBuilder objectBuilder = new ObjectFieldBuilder(new ViewFactory() {
+                @Override
+                public View createView(final Content content, final Axes axes, final int sequence) {
+                    if (content.isTextParseable() || content.getAdapter() == null) {
+                        return null;
+                    } else if (content.isObject()) {
+                        return new IconElementFactory().createView(content, axes, 0); // TreeNodeSpecification.this.createView(content,
+                                                                                      // axes);
+                    } else {
+                        return TreeNodeSpecification.this.createView(content, axes, -1);
+                    }
+                }
+            });
+
+            ViewBuilder collectiontBuilder = new CollectionElementBuilder(new IconElementFactory());
+
+            {
+                final Factory decorator = new ExpandableViewBorder.Factory(null, TreeNodeSpecification.this, null);
+                objectBuilder.addSubviewDecorator(decorator);
+                collectiontBuilder.addSubviewDecorator(decorator);
+
+            }
+
+            @Override
+            public void addSubviewDecorator(final SubviewDecorator decorator) {
+            }
+
+            @Override
+            public void build(final View view, final Axes axes) {
+                synchronized (view) {
+                    (view.getContent().isCollection() ? collectiontBuilder : objectBuilder).build(view, axes);
+                }
+            }
+
+            @Override
+            public void createAxes(final Axes axes, final Content content) {
+            }
+
+            @Override
+            public boolean isOpen() {
+                return false;
+            }
+
+            @Override
+            public boolean isReplaceable() {
+                return true;
+            }
+
+            @Override
+            public boolean isSubView() {
+                return true;
+            }
+
+            @Override
+            public boolean canDragView() {
+                return true;
+            }
+
+            @Override
+            public void viewMenuOptions(final UserActionSet options, final View view) {
+            }
+        };
+    }
+
+    @Override
+    public Layout createLayout(final Content content, final Axes axes) {
+        return new StackLayout();
+    }
+
+    @Override
+    public boolean canDisplay(final ViewRequirement requirement) {
+        return requirement.isObject() && requirement.isExpandable();
+    }
+
+    @Override
+    public String getName() {
+        return "Tree Node (not working)";
+    }
+
+}