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/06 18:42:19 UTC
[48/52] [partial] ISIS-188: moving framework/ subdirs up to parent
http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TableHeader.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TableHeader.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TableHeader.java
new file mode 100644
index 0000000..14d66cc
--- /dev/null
+++ b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TableHeader.java
@@ -0,0 +1,241 @@
+/*
+ * 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.core.commons.exceptions.IsisException;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+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.Shape;
+import org.apache.isis.viewer.dnd.drawing.Size;
+import org.apache.isis.viewer.dnd.view.Click;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.DragEvent;
+import org.apache.isis.viewer.dnd.view.DragStart;
+import org.apache.isis.viewer.dnd.view.InternalDrag;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+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.base.AbstractView;
+import org.apache.isis.viewer.dnd.view.border.ResizeDrag;
+import org.apache.isis.viewer.dnd.view.collection.CollectionContent;
+
+public class TableHeader extends AbstractView {
+ private final TableAxis axis;
+ private final int height;
+ private int resizeColumn;
+
+ public TableHeader(final Content content, final TableAxis axis) {
+ super(content, null);
+ this.axis = axis;
+ height = ViewConstants.VPADDING + Toolkit.getText(ColorsAndFonts.TEXT_LABEL).getTextHeight() + ViewConstants.VPADDING;
+ }
+
+ @Override
+ public void firstClick(final Click click) {
+ if (click.getLocation().getY() <= height) {
+ final int column = axis.getColumnAt(click.getLocation().getX()) - 1;
+ if (column == -2) {
+ super.firstClick(click);
+ } else if (column == -1) {
+ ((CollectionContent) getContent()).setOrderByElement();
+ invalidateContent();
+ } else {
+ final ObjectAssociation field = axis.getFieldForColumn(column);
+ ((CollectionContent) getContent()).setOrderByField(field);
+ invalidateContent();
+ }
+ } else {
+ super.firstClick(click);
+ }
+ }
+
+ @Override
+ public void invalidateContent() {
+ getParent().invalidateContent();
+ }
+
+ @Override
+ public Size getRequiredSize(final Size availableSpace) {
+ return new Size(-1, height);
+ }
+
+ @Override
+ public DragEvent dragStart(final DragStart drag) {
+ if (isOverColumnBorder(drag.getLocation())) {
+ resizeColumn = axis.getColumnBorderAt(drag.getLocation().getX());
+ final Bounds resizeArea = new Bounds(getView().getAbsoluteLocation(), getSize());
+ resizeArea.translate(getView().getPadding().getLeft(), getView().getPadding().getTop());
+ if (resizeColumn == 0) {
+ resizeArea.setWidth(axis.getHeaderOffset());
+ } else {
+ resizeArea.translate(axis.getLeftEdge(resizeColumn - 1), 0);
+ resizeArea.setWidth(axis.getColumnWidth(resizeColumn - 1));
+ }
+
+ final Size minimumSize = new Size(70, 0);
+ return new ResizeDrag(this, resizeArea, ResizeDrag.RIGHT, minimumSize, null);
+ } else if (drag.getLocation().getY() <= height) {
+ return null;
+ } else {
+ return super.dragStart(drag);
+ }
+ }
+
+ @Override
+ public void dragTo(final InternalDrag drag) {
+ if (drag.getOverlay() == null) {
+ throw new IsisException("No overlay for drag: " + drag);
+ }
+ int newWidth = drag.getOverlay().getSize().getWidth();
+ newWidth = Math.max(70, newWidth);
+ getViewManager().getSpy().addAction("Resize column to " + newWidth);
+
+ if (resizeColumn == 0) {
+ axis.setOffset(newWidth);
+ } else {
+ axis.setWidth(resizeColumn - 1, newWidth);
+ }
+ axis.invalidateLayout();
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ super.draw(canvas.createSubcanvas());
+
+ final int y = ViewConstants.VPADDING + Toolkit.getText(ColorsAndFonts.TEXT_LABEL).getAscent();
+
+ int x = axis.getHeaderOffset() - 2;
+
+ if (((CollectionContent) getContent()).getOrderByElement()) {
+ drawOrderIndicator(canvas, axis, x - 10);
+ }
+
+ final Color secondary1 = Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY1);
+ canvas.drawLine(0, 0, getSize().getWidth() - 1, 0, secondary1);
+ canvas.drawLine(0, height - 1, getSize().getWidth() - 1, height - 1, secondary1);
+ canvas.drawLine(x, 0, x, getSize().getHeight() - 1, secondary1);
+ x++;
+ final int columns = axis.getColumnCount();
+ final ObjectAssociation fieldSortOrder = ((CollectionContent) getContent()).getFieldSortOrder();
+ for (int i = 0; i < columns; i++) {
+ if (fieldSortOrder == axis.getFieldForColumn(i)) {
+ drawOrderIndicator(canvas, axis, x + axis.getColumnWidth(i) - 10);
+ }
+
+ canvas.drawLine(0, 0, 0, getSize().getHeight() - 1, secondary1);
+ canvas.drawLine(x, 0, x, getSize().getHeight() - 1, secondary1);
+ final Canvas headerCanvas = canvas.createSubcanvas(x, 0, axis.getColumnWidth(i) - 1, height);
+ headerCanvas.drawText(axis.getColumnName(i), ViewConstants.HPADDING, y, secondary1, Toolkit.getText(ColorsAndFonts.TEXT_LABEL));
+ x += axis.getColumnWidth(i);
+ }
+ // Color secondary2 = Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY2);
+ // canvas.drawLine(x, 0, x, getSize().getHeight() - 1, secondary2);
+ // canvas.drawRectangle(0, height, getSize().getWidth() - 1,
+ // getSize().getHeight() - height - 1, secondary2);
+ }
+
+ private void drawOrderIndicator(final Canvas canvas, final TableAxis axis, final int x) {
+ Shape arrow;
+ arrow = new Shape();
+ if (((CollectionContent) getContent()).getReverseSortOrder()) {
+ arrow.addPoint(0, 7);
+ arrow.addPoint(3, 0);
+ arrow.addPoint(6, 7);
+ } else {
+ arrow.addPoint(0, 0);
+ arrow.addPoint(6, 0);
+ arrow.addPoint(3, 7);
+ }
+ // canvas.drawRectangle(x + axis.getColumnWidth(i) - 10, 3, 7, 8,
+ // Toolkit.getColor("secondary3"));
+ canvas.drawShape(arrow, x, 3, Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY2));
+ }
+
+ @Override
+ public View identify(final Location location) {
+ getViewManager().getSpy().addTrace("Identify over column " + location);
+ if (isOverColumnBorder(location)) {
+ getViewManager().getSpy().addAction("Identified over column ");
+ return getView();
+ }
+ return super.identify(location);
+ }
+
+ private boolean isOverColumnBorder(final Location at) {
+ final int x = at.getX();
+ return axis.getColumnBorderAt(x) >= 0;
+ }
+
+ @Override
+ public void mouseMoved(final Location at) {
+ if (isOverColumnBorder(at)) {
+ getFeedbackManager().showResizeRightCursor();
+ } else {
+ super.mouseMoved(at);
+ getFeedbackManager().showDefaultCursor();
+ }
+ }
+
+ @Override
+ public void secondClick(final Click click) {
+ if (isOverColumnBorder(click.getLocation())) {
+ final int column = axis.getColumnBorderAt(click.getLocation().getX()) - 1;
+ if (column == -1) {
+ final View[] subviews = getSubviews();
+ for (final View row : subviews) {
+ axis.ensureOffset(((TableRowBorder) row).requiredTitleWidth());
+ }
+
+ } else {
+ final View[] subviews = getSubviews();
+ int max = 0;
+ for (final View row : subviews) {
+ final View cell = row.getSubviews()[column];
+ max = Math.max(max, cell.getRequiredSize(new Size()).getWidth());
+ }
+ axis.setWidth(column, max);
+ }
+ axis.invalidateLayout();
+ } else {
+ super.secondClick(click);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "TableHeader";
+ }
+
+ @Override
+ public ViewAreaType viewAreaType(final Location at) {
+ final int x = at.getX();
+
+ if (axis.getColumnBorderAt(x) >= 0) {
+ return ViewAreaType.INTERNAL;
+ } else {
+ return super.viewAreaType(at);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TableRowBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TableRowBorder.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TableRowBorder.java
new file mode 100644
index 0000000..05810c1
--- /dev/null
+++ b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TableRowBorder.java
@@ -0,0 +1,205 @@
+/*
+ * 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.core.commons.debug.DebugBuilder;
+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.Axes;
+import org.apache.isis.viewer.dnd.view.Click;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.DragEvent;
+import org.apache.isis.viewer.dnd.view.DragStart;
+import org.apache.isis.viewer.dnd.view.Placement;
+import org.apache.isis.viewer.dnd.view.SubviewDecorator;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewAreaType;
+import org.apache.isis.viewer.dnd.view.ViewAxis;
+import org.apache.isis.viewer.dnd.view.ViewConstants;
+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.text.ObjectTitleText;
+import org.apache.isis.viewer.dnd.view.text.TitleText;
+
+// REVIEW can we use ObjectBorder to provide the basic functionality
+public class TableRowBorder extends AbstractBorder {
+ public static class Factory implements SubviewDecorator {
+ @Override
+ public ViewAxis createAxis(final Content content) {
+ final TableAxis axis = new TableAxisImpl((CollectionContent) content);
+ return axis;
+ }
+
+ @Override
+ public View decorate(final Axes axes, final View view) {
+ return new TableRowBorder(axes, view);
+ }
+ }
+
+ private static final int BORDER = 13;
+
+ private final int baseline;
+ private final IconGraphic icon;
+ private final TitleText title;
+
+ private final TableAxis axis;
+
+ public TableRowBorder(final Axes axes, final View wrappedRow) {
+ super(wrappedRow);
+
+ final Text text = Toolkit.getText(ColorsAndFonts.TEXT_NORMAL);
+ icon = new IconGraphic(this, text);
+ title = new ObjectTitleText(this, text);
+ baseline = icon.getBaseline();
+
+ left = requiredTitleWidth() + BORDER;
+
+ axis = axes.getAxis(TableAxis.class);
+ axis.ensureOffset(left);
+ }
+
+ @Override
+ public void debugDetails(final DebugBuilder debug) {
+ debug.appendln("RowBorder " + left + " pixels");
+ debug.appendln("Axis", axis);
+ }
+
+ @Override
+ public DragEvent dragStart(final DragStart drag) {
+ final int x = drag.getLocation().getX();
+ final int left = axis.getHeaderOffset();
+ ;
+ if (x < left - BORDER) {
+ return Toolkit.getViewFactory().createDragContentOutline(this, drag.getLocation());
+ } else if (x < left) {
+ final View dragOverlay = new DragViewOutline(getView());
+ return new ViewDragImpl(this, new Offset(drag.getLocation()), dragOverlay);
+ } else {
+ return super.dragStart(drag);
+ }
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ final int baseline = getBaseline();
+
+ final int width = axis.getHeaderOffset();
+ final Size s = getSize();
+ final Canvas subcanvas = canvas.createSubcanvas(0, 0, width, s.getHeight());
+ int offset = ViewConstants.HPADDING;
+ icon.draw(subcanvas, offset, baseline);
+ offset += icon.getSize().getWidth() + ViewConstants.HPADDING + 0 + ViewConstants.HPADDING;
+ title.draw(subcanvas, offset, baseline, getLeft() - offset);
+
+ final int columns = axis.getColumnCount();
+ int x = -1;
+ x += axis.getHeaderOffset();
+ final Color secondary1 = Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY1);
+ canvas.drawLine(x - 1, 0, x - 1, s.getHeight() - 1, secondary1);
+ canvas.drawLine(x, 0, x, s.getHeight() - 1, secondary1);
+ for (int i = 0; i < columns; i++) {
+ x += axis.getColumnWidth(i);
+ canvas.drawLine(x, 0, x, s.getHeight() - 1, secondary1);
+ }
+ canvas.drawLine(0, 0, 0, s.getHeight() - 1, secondary1);
+
+ final int y = s.getHeight() - 1;
+ final Color secondary2 = Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY2);
+ canvas.drawLine(0, y, s.getWidth(), y, secondary2);
+
+ if (getState().isObjectIdentified()) {
+ final int xExtent = width - 1;
+ canvas.drawLine(xExtent - BORDER, top, xExtent - BORDER, top + s.getHeight() - 1, secondary2);
+ canvas.drawSolidRectangle(xExtent - BORDER + 1, top, BORDER - 2, s.getHeight() - 2 * top - 1, Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY3));
+ }
+
+ // components
+ super.draw(canvas);
+ }
+
+ @Override
+ public int getBaseline() {
+ return baseline;
+ }
+
+ @Override
+ protected int getLeft() {
+ return axis.getHeaderOffset();
+ }
+
+ protected int requiredTitleWidth() {
+ return ViewConstants.HPADDING + icon.getSize().getWidth() + ViewConstants.HPADDING + title.getSize().getWidth() + ViewConstants.HPADDING;
+ }
+
+ @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 secondClick(final Click click) {
+ final int left = axis.getHeaderOffset();
+ ;
+ final int x = click.getLocation().getX();
+ if (x <= left) {
+ final Location location = getAbsoluteLocation();
+ location.translate(click.getLocation());
+ getWorkspace().objectActionResult(getContent().getAdapter(), new Placement(this));
+ } else {
+ super.secondClick(click);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "RowBorder/" + wrappedView;
+ }
+
+ @Override
+ public ViewAreaType viewAreaType(final Location mouseLocation) {
+ if (mouseLocation.getX() <= left) {
+ return ViewAreaType.CONTENT;
+ } else if (mouseLocation.getX() >= getSize().getWidth() - right) {
+ return ViewAreaType.VIEW;
+ } else {
+ return super.viewAreaType(mouseLocation);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TableRowLayout.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TableRowLayout.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TableRowLayout.java
new file mode 100644
index 0000000..9c4d26a
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TableRowSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TableRowSpecification.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TableRowSpecification.java
new file mode 100644
index 0000000..3e1ea41
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TypeBasedColumnWidthStrategy.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TypeBasedColumnWidthStrategy.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/TypeBasedColumnWidthStrategy.java
new file mode 100644
index 0000000..29cb6a5
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/WindowTableSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/WindowTableSpecification.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/table/WindowTableSpecification.java
new file mode 100644
index 0000000..94a4d08
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/toolbar/ToolbarView.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/toolbar/ToolbarView.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/toolbar/ToolbarView.java
new file mode 100644
index 0000000..ad1b393
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/ClosedCollectionNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/ClosedCollectionNodeSpecification.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/ClosedCollectionNodeSpecification.java
new file mode 100644
index 0000000..152ea53
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/ClosedObjectNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/ClosedObjectNodeSpecification.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/ClosedObjectNodeSpecification.java
new file mode 100644
index 0000000..57f5114
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/CompositeNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/CompositeNodeSpecification.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/CompositeNodeSpecification.java
new file mode 100644
index 0000000..d824cbf
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/EmptyNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/EmptyNodeSpecification.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/EmptyNodeSpecification.java
new file mode 100644
index 0000000..98349b5
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/LeafNodeView.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/LeafNodeView.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/LeafNodeView.java
new file mode 100644
index 0000000..f461111
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/ListWithDetailSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/ListWithDetailSpecification.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/ListWithDetailSpecification.java
new file mode 100644
index 0000000..2fdb08c
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/NodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/NodeSpecification.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/NodeSpecification.java
new file mode 100644
index 0000000..dfe14ee
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/OpenCollectionNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/OpenCollectionNodeSpecification.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/OpenCollectionNodeSpecification.java
new file mode 100644
index 0000000..ddd8af2
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/OpenObjectNodeSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/OpenObjectNodeSpecification.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/OpenObjectNodeSpecification.java
new file mode 100644
index 0000000..a07bae3
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/TreeDisplayRules.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/TreeDisplayRules.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/TreeDisplayRules.java
new file mode 100644
index 0000000..4e4479b
--- /dev/null
+++ b/component/viewer/dnd/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/255ef514/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/TreeNodeBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/TreeNodeBorder.java b/component/viewer/dnd/src/main/java/org/apache/isis/viewer/dnd/tree/TreeNodeBorder.java
new file mode 100644
index 0000000..c0bf6cc
--- /dev/null
+++ b/component/viewer/dnd/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;
+ }
+}