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
[21/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/view/border/LabelBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/LabelBorder.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/LabelBorder.java
new file mode 100644
index 0000000..305d8d6
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/LabelBorder.java
@@ -0,0 +1,148 @@
+/*
+ * 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.view.border;
+
+import org.apache.isis.core.commons.debug.DebugBuilder;
+import org.apache.isis.core.commons.lang.ToString;
+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.Text;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewConstants;
+import org.apache.isis.viewer.dnd.view.action.ParameterContent;
+import org.apache.isis.viewer.dnd.view.axis.LabelAxis;
+import org.apache.isis.viewer.dnd.view.base.AbstractBorder;
+import org.apache.isis.viewer.dnd.view.content.FieldContent;
+
+public class LabelBorder extends AbstractBorder {
+ public static final int NORMAL = 0;
+ public static final int DISABLED = 1;
+ public static final int MANDATORY = 2;
+
+ public static View createFieldLabelBorder(final LabelAxis axis, final View wrappedView) {
+ final FieldContent fieldContent = (FieldContent) wrappedView.getContent();
+ return new LabelBorder(fieldContent, axis, wrappedView);
+ }
+
+ public static View createValueParameterLabelBorder(final LabelAxis axis, final View wrappedView) {
+ final ParameterContent fieldContent = (ParameterContent) wrappedView.getContent();
+ return new LabelBorder(fieldContent, axis, wrappedView);
+ }
+
+ private final String label;
+ private Text style;
+ private Color color;
+ private final LabelAxis axis;
+
+ protected LabelBorder(final FieldContent fieldContent, final LabelAxis axis, final View wrappedView) {
+ super(wrappedView);
+ this.axis = axis;
+ if (fieldContent.isEditable().isVetoed()) {
+ setDisabledStyling();
+ } else if (fieldContent.isMandatory()) {
+ setMandatoryStyling();
+ } else {
+ setOptionalStyling();
+ }
+
+ final String name = fieldContent.getFieldName();
+ this.label = name + ":";
+
+ final int width = ViewConstants.HPADDING + style.stringWidth(this.label) + ViewConstants.HPADDING;
+ if (axis == null) {
+ left = width;
+ } else {
+ axis.accommodateWidth(width);
+ }
+ }
+
+ protected LabelBorder(final ParameterContent fieldContent, final LabelAxis axis, final View wrappedView) {
+ super(wrappedView);
+ this.axis = axis;
+ if (fieldContent.isRequired()) {
+ setMandatoryStyling();
+ } else {
+ setOptionalStyling();
+ }
+
+ final String name = fieldContent.getParameterName();
+ this.label = name + ":";
+
+ final int width = ViewConstants.HPADDING + style.stringWidth(this.label) + ViewConstants.HPADDING;
+ if (axis == null) {
+ left = width;
+ } else {
+ axis.accommodateWidth(width);
+ }
+ }
+
+ private void setOptionalStyling() {
+ style = Toolkit.getText(ColorsAndFonts.TEXT_LABEL);
+ color = Toolkit.getColor(ColorsAndFonts.COLOR_LABEL);
+ }
+
+ private void setMandatoryStyling() {
+ style = Toolkit.getText(ColorsAndFonts.TEXT_LABEL_MANDATORY);
+ color = Toolkit.getColor(ColorsAndFonts.COLOR_LABEL_MANDATORY);
+ }
+
+ private void setDisabledStyling() {
+ style = Toolkit.getText(ColorsAndFonts.TEXT_LABEL_DISABLED);
+ color = Toolkit.getColor(ColorsAndFonts.COLOR_LABEL_DISABLED);
+ }
+
+ @Override
+ protected int getLeft() {
+ if (axis == null) {
+ return left;
+ } else {
+ return axis.getWidth();
+ }
+ }
+
+ @Override
+ public void debugDetails(final DebugBuilder debug) {
+ super.debugDetails(debug);
+ debug.appendln("label", "'" + label + "'");
+ debug.appendln("axis", axis);
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ final Color color = textColor();
+ canvas.drawText(label, left, wrappedView.getBaseline(), color, style);
+ super.draw(canvas);
+ }
+
+ protected Color textColor() {
+ return color;
+ }
+
+ @Override
+ public String toString() {
+ return wrappedView.toString() + "/" + ToString.name(this);
+ }
+
+ public View getWrapped() {
+ return wrappedView;
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/LineBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/LineBorder.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/LineBorder.java
new file mode 100644
index 0000000..563af18
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/LineBorder.java
@@ -0,0 +1,110 @@
+/*
+ * 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.view.border;
+
+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.Size;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.base.AbstractBorder;
+
+/**
+ * A line border draws a simple box around a view of a given width and color.
+ */
+public class LineBorder extends AbstractBorder {
+ private Color color;
+ private final int arcRadius;
+ private int width;
+ private int padding;
+
+ public LineBorder(final View wrappedView) {
+ this(1, wrappedView);
+ }
+
+ public LineBorder(final int size, final View wrappedView) {
+ this(size, 0, Toolkit.getColor(ColorsAndFonts.COLOR_BLACK), wrappedView);
+ }
+
+ public LineBorder(final int size, final int arcRadius, final View wrappedView) {
+ this(size, arcRadius, Toolkit.getColor(ColorsAndFonts.COLOR_BLACK), wrappedView);
+ }
+
+ public LineBorder(final Color color, final View wrappedView) {
+ this(1, 0, color, wrappedView);
+ }
+
+ public LineBorder(final int width, final Color color, final View wrappedView) {
+ this(width, 0, color, wrappedView);
+ }
+
+ public LineBorder(final int width, final int arcRadius, final Color color, final View wrappedView) {
+ super(wrappedView);
+ setWidth(width);
+ this.arcRadius = arcRadius;
+ this.color = color;
+ }
+
+ @Override
+ protected void debugDetails(final DebugBuilder debug) {
+ debug.append("LineBorder " + top + " pixels\n");
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ super.draw(canvas);
+ final Size s = getSize();
+ final int width = s.getWidth();
+ for (int i = 0; i < left - padding; i++) {
+ // canvas.drawRectangle(i, i, width - 2 * i, s.getHeight() - 2 * i,
+ // color);
+ canvas.drawRoundedRectangle(i, i, width - 2 * i, s.getHeight() - 2 * i, arcRadius, arcRadius, color);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return wrappedView.toString() + "/LineBorder";
+ }
+
+ public void setWidth(final int width) {
+ this.width = width;
+ calculateBorderWidth();
+ }
+
+ public void setPadding(final int padding) {
+ this.padding = padding;
+ calculateBorderWidth();
+ }
+
+ private void calculateBorderWidth() {
+ top = width + padding;
+ left = width + padding;
+ bottom = width + padding;
+ right = width + padding;
+ }
+
+ public void setColor(final Color color) {
+ this.color = color;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ObjectBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ObjectBorder.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ObjectBorder.java
new file mode 100644
index 0000000..a17e970
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ObjectBorder.java
@@ -0,0 +1,167 @@
+/*
+ * 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.view.border;
+
+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.Image;
+import org.apache.isis.viewer.dnd.drawing.ImageFactory;
+import org.apache.isis.viewer.dnd.drawing.Offset;
+import org.apache.isis.viewer.dnd.drawing.Size;
+import org.apache.isis.viewer.dnd.interaction.ViewDragImpl;
+import org.apache.isis.viewer.dnd.view.DragEvent;
+import org.apache.isis.viewer.dnd.view.DragStart;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewState;
+import org.apache.isis.viewer.dnd.view.base.AbstractBorder;
+import org.apache.isis.viewer.dnd.view.base.DragViewOutline;
+
+/**
+ * A border for objects providing
+ * <ol>
+ * <li>Ability to drag out a new view of the object.</li>
+ * <li>State change when moving over object.
+ * <li>Feedback of the state of the view, eg drop valid, identified etc.
+ * </ol>
+ */
+public class ObjectBorder extends AbstractBorder {
+ private static final int BORDER = 13;
+
+ public ObjectBorder(final int size, final View wrappedView) {
+ super(wrappedView);
+
+ top = size;
+ left = size;
+ bottom = size;
+ right = size + BORDER;
+ }
+
+ public ObjectBorder(final View wrappedView) {
+ this(1, wrappedView);
+ }
+
+ @Override
+ protected void debugDetails(final DebugBuilder debug) {
+ super.debugDetails(debug);
+ debug.appendln("line thickness", left);
+ }
+
+ @Override
+ public DragEvent dragStart(final DragStart drag) {
+ if (drag.getLocation().getX() > getSize().getWidth() - right) {
+ if (getContent().getAdapter() == null) {
+ return null;
+ }
+ 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) {
+ super.draw(canvas);
+
+ Color color = null;
+ final ViewState state = getState();
+ final boolean hasFocus = getViewManager().hasFocus(getView());
+ if (state.canDrop()) {
+ color = Toolkit.getColor(ColorsAndFonts.COLOR_VALID);
+ } else if (state.cantDrop()) {
+ color = Toolkit.getColor(ColorsAndFonts.COLOR_INVALID);
+ } else if (hasFocus) {
+ color = Toolkit.getColor(ColorsAndFonts.COLOR_IDENTIFIED);
+ } else if (state.isObjectIdentified()) {
+ color = Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY2);
+ }
+ final Size s = getSize();
+
+ if (getContent().isPersistable() && getContent().isTransient()) {
+ final int x = s.getWidth() - 13;
+ final int y = 0;
+ final Image icon = ImageFactory.getInstance().loadIcon("transient", 8, null);
+ if (icon == null) {
+ canvas.drawText("*", x, y + Toolkit.getText(ColorsAndFonts.TEXT_NORMAL).getAscent(), Toolkit.getColor(ColorsAndFonts.COLOR_BLACK), Toolkit.getText(ColorsAndFonts.TEXT_NORMAL));
+ } else {
+ canvas.drawImage(icon, x, y, 12, 12);
+ }
+ }
+
+ if (color != null) {
+ if (hasFocus) {
+ final int xExtent = s.getWidth() - left;
+ for (int i = 0; i < left; i++) {
+ canvas.drawRectangle(i, i, xExtent - 2 * i, s.getHeight() - 2 * i, color);
+ }
+ } else {
+ final int xExtent = s.getWidth();
+ for (int i = 0; i < left; i++) {
+ canvas.drawRectangle(i, i, xExtent - 2 * i, s.getHeight() - 2 * i, color);
+ }
+ canvas.drawLine(xExtent - BORDER, top, xExtent - BORDER, top + s.getHeight(), color);
+ canvas.drawSolidRectangle(xExtent - BORDER + 1, top, BORDER - 2, s.getHeight() - 2 * top, Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY3));
+ }
+ }
+ }
+
+ @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 viewMenuOptions(final UserActionSet options) {
+ * super.viewMenuOptions(options); Content content = getContent();
+ * UserActionSet suboptions = options.addNewActionSet("Replace with");
+ * replaceOptions(Toolkit.getViewFactory().availableViews(new
+ * ViewRequirement(content, ViewRequirement.OPEN |
+ * ViewRequirement.REPLACEABLE | ViewRequirement.SUBVIEW)), suboptions);
+ * replaceOptions(Toolkit.getViewFactory().availableViews(new
+ * ViewRequirement(content, ViewRequirement.CLOSED |
+ * ViewRequirement.REPLACEABLE | ViewRequirement.SUBVIEW)), suboptions); }
+ *
+ * protected void replaceOptions(final Enumeration possibleViews, final
+ * UserActionSet options) { if (possibleViews.hasMoreElements()) { while
+ * (possibleViews.hasMoreElements()) { final ViewSpecification specification
+ * = (ViewSpecification) possibleViews.nextElement(); if (specification !=
+ * getSpecification()) { options.add(new ReplaceViewOption(specification) {
+ * protected void replace(View view, View withReplacement) {
+ * replaceWrappedView(withReplacement); } }); } } } }
+ */
+ @Override
+ public String toString() {
+ return wrappedView.toString() + "/ObjectBorder [" + getSpecification() + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ResizeBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ResizeBorder.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ResizeBorder.java
new file mode 100644
index 0000000..63f9f2f
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ResizeBorder.java
@@ -0,0 +1,243 @@
+/*
+ * 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.view.border;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.commons.debug.DebugBuilder;
+import org.apache.isis.viewer.dnd.drawing.Bounds;
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.drawing.Size;
+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.UserActionSet;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewAreaType;
+import org.apache.isis.viewer.dnd.view.Workspace;
+import org.apache.isis.viewer.dnd.view.base.AbstractBorder;
+import org.apache.isis.viewer.dnd.view.option.UserActionAbstract;
+
+public abstract class ResizeBorder extends AbstractBorder {
+ private static final Logger LOG = Logger.getLogger(ResizeBorder.class);
+ private static final Logger UI_LOG = Logger.getLogger("ui." + ResizeBorder.class.getName());
+ public static final int LEFT = 1;
+ public static final int RIGHT = 2;
+ public static final int UP = 4;
+ public static final int DOWN = 8;
+ private int width;
+ private int height;
+ private int requiredDirection;
+ private final int allowDirections;
+ protected boolean resizing;
+ private int onBorder;
+
+ // TODO allow a minimum and maximum sizes to be specified and then ensure
+ // the user doesn't go outside them.
+ public ResizeBorder(final View view, final int allowDirections, final int widthOnMovingSides, final int widthOnStaticSides) {
+ super(view);
+ this.allowDirections = allowDirections;
+ top = canExtend(UP) ? widthOnMovingSides : widthOnStaticSides;
+ bottom = canExtend(DOWN) ? widthOnMovingSides : widthOnStaticSides;
+ left = canExtend(LEFT) ? widthOnMovingSides : widthOnStaticSides;
+ right = canExtend(RIGHT) ? widthOnMovingSides : widthOnStaticSides;
+
+ }
+
+ @Override
+ protected void debugDetails(final DebugBuilder debug) {
+ super.debugDetails(debug);
+ debug.appendln("width", width == 0 ? "no change" : Integer.toString(width));
+ debug.appendln("height ", height == 0 ? "no change" : Integer.toString(height));
+ debug.appendln("resizable ", (canExtend(UP) ? "Up " : "") + (canExtend(DOWN) ? "Down " : "") + (canExtend(LEFT) ? "Left " : "") + (canExtend(RIGHT) ? "Right " : ""));
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ final Size size = getSize();
+ final int width = size.getWidth();
+ final int height = size.getHeight();
+ drawResizeBorder(canvas, size);
+
+ final Canvas subCanvas = canvas.createSubcanvas(left, top, width - left - right, height - top - bottom);
+ wrappedView.draw(subCanvas);
+ }
+
+ protected abstract void drawResizeBorder(final Canvas canvas, final Size size);
+
+ @Override
+ public ViewAreaType viewAreaType(final Location mouseLocation) {
+ if (isOnBorder()) {
+ return ViewAreaType.INTERNAL;
+ }
+ return super.viewAreaType(mouseLocation);
+ }
+
+ @Override
+ public void viewMenuOptions(final UserActionSet menuOptions) {
+ super.viewMenuOptions(menuOptions);
+ menuOptions.add(new UserActionAbstract("Clear resizing") {
+ @Override
+ public void execute(final Workspace workspace, final View view, final Location at) {
+ width = 0;
+ height = 0;
+ invalidateLayout();
+ }
+ });
+ }
+
+ @Override
+ public DragEvent dragStart(final DragStart drag) {
+ final Location location = drag.getLocation();
+ if (overBorder(location)) {
+ requiredDirection = onBorder(location);
+ if (requiredDirection > 0) {
+ return new ResizeDrag(this, new Bounds(getAbsoluteLocation(), getView().getSize()), requiredDirection);
+ }
+ return null;
+ } else {
+ return super.dragStart(drag);
+ }
+ }
+
+ @Override
+ public void drag(final InternalDrag drag) {
+ final ViewResizeOutline outline = ((ViewResizeOutline) drag.getOverlay());
+ if (outline == null) {
+ super.drag(drag);
+ }
+ }
+
+ @Override
+ public void dragTo(final InternalDrag drag) {
+ getFeedbackManager().showDefaultCursor();
+ final ViewResizeOutline outline = ((ViewResizeOutline) drag.getOverlay());
+ if (outline != null) {
+ resizing = false;
+ onBorder = 0;
+
+ if (requiredDirection == ResizeDrag.RIGHT || requiredDirection == ResizeDrag.BOTTOM_RIGHT) {
+ width = outline.getSize().getWidth();
+ }
+ if (requiredDirection == ResizeDrag.BOTTOM || requiredDirection == ResizeDrag.BOTTOM_RIGHT) {
+ height = outline.getSize().getHeight();
+ }
+
+ LOG.debug("resizing view " + width + "," + height);
+ invalidateLayout();
+ } else {
+ super.dragTo(drag);
+ }
+ }
+
+ @Override
+ public Size getRequiredSize(final Size maximumSize) {
+ maximumSize.contract(getLeft() + getRight(), getTop() + getBottom());
+ if (width > 0 && maximumSize.getWidth() > width) {
+ maximumSize.setWidth(width);
+ }
+ if (height > 0 && maximumSize.getHeight() > height) {
+ maximumSize.setHeight(height);
+ }
+ final Size size = wrappedView.getRequiredSize(maximumSize);
+ size.extend(getLeft() + getRight(), getTop() + getBottom());
+ if (width > 0) {
+ size.setWidth(width);
+ }
+ if (height > 0) {
+ size.setHeight(height);
+ }
+ return size;
+ }
+
+ /**
+ * Detects whether the point is on the resize border, and if so changes the
+ * cursor to show it can be resized.
+ */
+ @Override
+ public void mouseMoved(final Location at) {
+ final int onBorder = onBorder(at);
+ if (this.onBorder != onBorder) {
+ switch (onBorder) {
+ case ResizeDrag.RIGHT:
+ getFeedbackManager().showResizeRightCursor();
+ resizing = true;
+ markDamaged();
+ break;
+
+ case ResizeDrag.BOTTOM:
+ getFeedbackManager().showResizeDownCursor();
+ resizing = true;
+ markDamaged();
+ break;
+
+ case ResizeDrag.BOTTOM_RIGHT:
+ getFeedbackManager().showResizeDownRightCursor();
+ resizing = true;
+ markDamaged();
+ break;
+
+ default:
+ getFeedbackManager().showDefaultCursor();
+ super.mouseMoved(at);
+ resizing = false;
+ markDamaged();
+ break;
+ }
+ UI_LOG.debug("on resize border " + onBorder + " " + resizing);
+ }
+ this.onBorder = onBorder;
+ }
+
+ @Override
+ public void exited() {
+ getFeedbackManager().showDefaultCursor();
+ resizing = false;
+ onBorder = 0;
+ markDamaged();
+ UI_LOG.debug("off resize border " + onBorder + " " + resizing);
+ super.exited();
+ }
+
+ private int onBorder(final Location at) {
+ final Bounds area = contentArea();
+ final boolean right = canExtend(RIGHT) && at.getX() >= area.getWidth() && at.getX() <= area.getWidth() + getRight();
+ final boolean bottom = canExtend(DOWN) && at.getY() >= area.getHeight() && at.getY() <= area.getHeight() + getBottom();
+
+ final int status;
+ if (right && bottom) {
+ status = ResizeDrag.BOTTOM_RIGHT;
+ } else if (right) {
+ status = ResizeDrag.RIGHT;
+ } else if (bottom) {
+ status = ResizeDrag.BOTTOM;
+ } else {
+ status = 0;
+ }
+
+ return status;
+ }
+
+ private boolean canExtend(final int extend) {
+ return (extend & allowDirections) == extend;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ResizeDrag.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ResizeDrag.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ResizeDrag.java
new file mode 100644
index 0000000..08524b1
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ResizeDrag.java
@@ -0,0 +1,215 @@
+/*
+ * 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.view.border;
+
+import org.apache.isis.viewer.dnd.drawing.Bounds;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.drawing.Size;
+import org.apache.isis.viewer.dnd.interaction.DragImpl;
+import org.apache.isis.viewer.dnd.view.InternalDrag;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.Viewer;
+
+public class ResizeDrag extends DragImpl implements InternalDrag {
+ public static final int BOTTOM = 2;
+ public static final int BOTTOM_LEFT = 7;
+ public static final int BOTTOM_RIGHT = 8;
+ public static final int LEFT = 3;
+ public static final int RIGHT = 4;
+ public static final int TOP = 1;
+ public static final int TOP_LEFT = 5;
+ public static final int TOP_RIGHT = 6;
+ /**
+ * the location of the corner opposite the pointer that will form the
+ * resizing rectangle.
+ */
+ private final Location anchor;
+ private final int direction;
+ private final ViewResizeOutline overlay;
+ private final View view;
+ private final Size minimumSize;
+ private final Size maximumSize;
+
+ public ResizeDrag(final View view, final Bounds resizeArea, final int direction) {
+ this(view, resizeArea, direction, null, null);
+ }
+
+ public ResizeDrag(final View view, final Bounds resizeArea, final int direction, final Size minimumSize, final Size maximumSize) {
+ this.view = view;
+ this.direction = direction;
+ this.anchor = resizeArea.getLocation();
+ this.minimumSize = minimumSize;
+ this.maximumSize = maximumSize;
+ overlay = new ViewResizeOutline(resizeArea);
+ overlay.setLocation(resizeArea.getLocation());
+ }
+
+ @Override
+ public void cancel(final Viewer viewer) {
+ view.dragCancel(this);
+ }
+
+ @Override
+ public void drag(final View target, final Location location, final int mods) {
+
+ switch (direction) {
+ case TOP:
+ extendUpward(location);
+ break;
+
+ case BOTTOM:
+ extendDownward(location);
+ break;
+
+ case LEFT:
+ extendLeft(location);
+ break;
+
+ case RIGHT:
+ extendRight(location);
+ break;
+
+ case TOP_RIGHT:
+ extendRight(location);
+ extendUpward(location);
+ break;
+
+ case BOTTOM_RIGHT:
+ extendRight(location);
+ extendDownward(location);
+ break;
+
+ case TOP_LEFT:
+ extendLeft(location);
+ extendUpward(location);
+ break;
+
+ case BOTTOM_LEFT:
+ extendLeft(location);
+ extendDownward(location);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void end(final Viewer viewer) {
+ view.dragTo(this);
+ view.getViewManager().clearOverlayView(view);
+ }
+
+ /*
+ * public ViewResizeOutline(View forView, int direction) { this(forView,
+ * direction, forView.getAbsoluteLocation(), forView.getSize()); }
+ *
+ * public ViewResizeOutline(View forView, int direction, Location location,
+ * Size size) { super(forView.getContent(), null, null);
+ *
+ * Logger.getLogger(getClass()).debug("drag outline for " + forView);
+ * setLocation(location); setSize(size);
+ *
+ * Logger.getLogger(getClass()).debug("drag outline initial size " +
+ * getSize() + " " + forView.getSize());
+ *
+ * origin = getBounds();
+ *
+ * switch (direction) { case TOP: getViewManager().showResizeUpCursor();
+ * break;
+ *
+ * case BOTTOM: getViewManager().showResizeDownCursor(); break;
+ *
+ * case LEFT: getViewManager().showResizeLeftCursor(); break;
+ *
+ * case RIGHT: getViewManager().showResizeRightCursor(); break;
+ *
+ * case TOP_LEFT: getViewManager().showResizeUpLeftCursor(); break;
+ *
+ * case TOP_RIGHT: getViewManager().showResizeUpRightCursor(); break;
+ *
+ * case BOTTOM_LEFT: getViewManager().showResizeDownLeftCursor(); break;
+ *
+ * case BOTTOM_RIGHT: getViewManager().showResizeDownRightCursor(); break;
+ *
+ * case CENTER: getViewManager().showMoveCursor(); break;
+ *
+ * default : break; } }
+ */
+
+ private void extendDownward(final Location location) {
+ overlay.markDamaged();
+ final int height = location.getY() - anchor.getY();
+ final int width = overlay.getSize().getWidth();
+ overlay.setSize(new Size(width, height));
+ overlay.markDamaged();
+ }
+
+ private void extendLeft(final Location location) {
+ overlay.markDamaged();
+ final int height = overlay.getSize().getHeight();
+ final int width = anchor.getX() - location.getX();
+ overlay.setSize(new Size(width, height));
+ final int x = anchor.getX() - width;
+ final int y = anchor.getY();
+ overlay.setBounds(new Bounds(x, y, width, height));
+ overlay.markDamaged();
+ }
+
+ private void extendRight(final Location location) {
+ overlay.markDamaged();
+ final int height = overlay.getSize().getHeight();
+ int width = location.getX() - anchor.getX();
+ if (maximumSize != null && width > maximumSize.getWidth()) {
+ width = maximumSize.getWidth();
+ }
+ if (minimumSize != null && width < minimumSize.getWidth()) {
+ width = minimumSize.getWidth();
+ }
+ overlay.setSize(new Size(width, height));
+ overlay.markDamaged();
+ }
+
+ private void extendUpward(final Location location) {
+ overlay.markDamaged();
+ final int height = anchor.getY() - location.getY();
+ final int width = overlay.getSize().getWidth();
+ overlay.setSize(new Size(width, height));
+ final int x = anchor.getX();
+ final int y = anchor.getY() - height;
+ overlay.setBounds(new Bounds(x, y, width, height));
+ overlay.markDamaged();
+ }
+
+ public int getDirection() {
+ return direction;
+ }
+
+ @Override
+ public Location getLocation() {
+ final Size size = overlay.getSize();
+ return new Location(size.getWidth(), size.getHeight());
+ }
+
+ @Override
+ public View getOverlay() {
+ return overlay;
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ResizeViewRender.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ResizeViewRender.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ResizeViewRender.java
new file mode 100644
index 0000000..5e245f2
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ResizeViewRender.java
@@ -0,0 +1,28 @@
+/*
+ * 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.view.border;
+
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+
+public interface ResizeViewRender {
+
+ void draw(Canvas canvas, int x, int width, int height, boolean hasFocus);
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SaveState.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SaveState.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SaveState.java
new file mode 100644
index 0000000..d5b3fa1
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SaveState.java
@@ -0,0 +1,56 @@
+/*
+ * 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.view.border;
+
+class SaveState {
+ StringBuffer missingFields = new StringBuffer();
+ StringBuffer invalidFields = new StringBuffer();
+
+ void addMissingField(final String parameterName) {
+ if (missingFields.length() > 0) {
+ missingFields.append(", ");
+ }
+ missingFields.append(parameterName);
+ }
+
+ void addInvalidField(final String parameterName) {
+ if (invalidFields.length() > 0) {
+ invalidFields.append(", ");
+ }
+ invalidFields.append(parameterName);
+ }
+
+ String getMessage() {
+ String error = "";
+ if (missingFields.length() > 0) {
+ if (error.length() > 0) {
+ error += "; ";
+ }
+ error += "Fields needed: " + missingFields;
+ }
+ if (invalidFields.length() > 0) {
+ if (error.length() > 0) {
+ error += "; ";
+ }
+ error += "Invalid fields: " + invalidFields;
+ }
+ return error;
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SaveTransientObjectBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SaveTransientObjectBorder.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SaveTransientObjectBorder.java
new file mode 100644
index 0000000..2c5f50c
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SaveTransientObjectBorder.java
@@ -0,0 +1,175 @@
+/*
+ * 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.view.border;
+
+import org.apache.log4j.Logger;
+
+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.consent.Veto;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+import org.apache.isis.runtimes.dflt.runtime.system.persistence.Persistor;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.view.ButtonAction;
+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.view.Workspace;
+import org.apache.isis.viewer.dnd.view.content.FieldContent;
+import org.apache.isis.viewer.dnd.view.content.RootObject;
+import org.apache.isis.viewer.dnd.view.control.AbstractButtonAction;
+
+public class SaveTransientObjectBorder extends ButtonBorder {
+ private static final Logger LOG = Logger.getLogger(SaveTransientObjectBorder.class);
+
+ private static class CloseAction extends AbstractButtonAction {
+ public CloseAction() {
+ super("Discard");
+ }
+
+ @Override
+ public void execute(final Workspace workspace, final View view, final Location at) {
+ close(workspace, view);
+ }
+ }
+
+ private static class SaveAction extends AbstractButtonAction {
+ public SaveAction() {
+ super("Save");
+ }
+
+ @Override
+ public Consent disabled(final View view) {
+ return canSave(view);
+ }
+
+ @Override
+ public void execute(final Workspace workspace, final View view, final Location at) {
+ save(view);
+ // by recreating the view the transient border is removed
+ final ViewSpecification spec = view.getSpecification();
+ final View newView = spec.createView(view.getContent(), view.getViewAxes(), -1);
+ workspace.replaceView(view, newView);
+ }
+ }
+
+ private static Consent canSave(final View view) {
+
+ final ObjectAdapter transientNO = view.getContent().getAdapter();
+
+ // check each of the fields, and capture invalid state if known
+ final SaveState saveState = new SaveState();
+ checkFields(saveState, view, transientNO);
+ final StringBuilder errorBuf = new StringBuilder(saveState.getMessage());
+
+ final ObjectSpecification viewContentSpec = view.getContent().getSpecification();
+ final Consent consent = viewContentSpec.isValid(transientNO);
+ if (consent.isVetoed()) {
+ if (errorBuf.length() > 0) {
+ errorBuf.append("; ");
+ }
+ errorBuf.append(consent.getReason());
+ }
+
+ if (errorBuf.length() == 0) {
+ return Allow.DEFAULT;
+ } else {
+ return new Veto(errorBuf.toString());
+ }
+ }
+
+ private static void checkFields(final SaveState saveState, final View view, final ObjectAdapter forObject) {
+ if (view.getContent().getAdapter() != forObject) {
+ return;
+ }
+
+ final View[] subviews = view.getSubviews();
+ for (final View fieldView : subviews) {
+ final Content content = fieldView.getContent();
+ if (content instanceof RootObject) {
+ checkFields(saveState, fieldView, forObject);
+ } else if (content instanceof FieldContent) {
+ final boolean isMandatory = ((FieldContent) content).isMandatory();
+ final boolean isEditable = ((FieldContent) content).isEditable().isAllowed();
+ final ObjectAdapter field = content.getAdapter();
+ final boolean isFieldEmpty = field == null;
+ if (isMandatory && isEditable && isFieldEmpty) {
+ final String parameterName = ((FieldContent) content).getFieldName();
+ saveState.addMissingField(parameterName);
+
+ } else if (fieldView.getState().isInvalid()) {
+ final String parameterName = ((FieldContent) content).getFieldName();
+ saveState.addInvalidField(parameterName);
+ }
+ }
+ }
+ }
+
+ private static class SaveAndCloseAction extends AbstractButtonAction {
+ public SaveAndCloseAction() {
+ super("Save & Close");
+ }
+
+ @Override
+ public Consent disabled(final View view) {
+ return canSave(view);
+ }
+
+ @Override
+ public void execute(final Workspace workspace, final View view, final Location at) {
+ save(view);
+ close(workspace, view);
+ }
+ }
+
+ private static void close(final Workspace workspace, final View view) {
+ view.dispose();
+ }
+
+ private static ObjectAdapter save(final View view) {
+ final ObjectAdapter transientObject = view.getContent().getAdapter();
+ try {
+ getPersistenceSession().makePersistent(transientObject);
+ } catch (final RuntimeException e) {
+ LOG.info("exception saving " + transientObject + ", aborting transaction" + e.getMessage());
+ throw e;
+ }
+ return transientObject;
+ }
+
+ // /////////////////////////////////////////////////////
+ // Constructor
+ // /////////////////////////////////////////////////////
+
+ public SaveTransientObjectBorder(final View view) {
+ super(new ButtonAction[] { new SaveAction(), new SaveAndCloseAction(), new CloseAction(), }, view);
+ }
+
+ // /////////////////////////////////////////////////////
+ // Dependencies (from context)
+ // /////////////////////////////////////////////////////
+
+ private static Persistor getPersistenceSession() {
+ return IsisContext.getPersistenceSession();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ScrollBar.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ScrollBar.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ScrollBar.java
new file mode 100644
index 0000000..bf0f512
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ScrollBar.java
@@ -0,0 +1,88 @@
+/*
+ * 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.view.border;
+
+public class ScrollBar {
+ private int maximum;
+ private int minimum;
+ private int scrollPosition = 0;
+ private int visibleAmount;
+
+ public ScrollBar() {
+ super();
+ }
+
+ public void setPostion(final int position) {
+ scrollPosition = Math.min(position, maximum);
+ scrollPosition = Math.max(scrollPosition, minimum);
+ }
+
+ public void firstClick(final int x, final boolean alt) {
+ if (alt) {
+ setPostion(x - visibleAmount / 2);
+ } else {
+ if (x < scrollPosition) {
+ setPostion(scrollPosition - visibleAmount);
+ } else if (x > scrollPosition + visibleAmount) {
+ setPostion(scrollPosition + visibleAmount);
+ }
+ }
+ }
+
+ public int getMaximum() {
+ return maximum;
+ }
+
+ public int getMinimum() {
+ return minimum;
+ }
+
+ public int getPosition() {
+ return scrollPosition;
+ }
+
+ public int getVisibleAmount() {
+ return visibleAmount;
+ }
+
+ public void limit() {
+ if (scrollPosition > maximum) {
+ scrollPosition = maximum;
+ }
+ }
+
+ public void reset() {
+ scrollPosition = 0;
+ }
+
+ public boolean isOnThumb(final int pos) {
+ return pos > scrollPosition && pos < scrollPosition + visibleAmount;
+ }
+
+ public void setSize(final int viewportSize, final int contentSize) {
+ visibleAmount = contentSize == 0 ? 0 : (viewportSize * viewportSize / contentSize);
+ maximum = viewportSize - visibleAmount;
+ }
+
+ public void secondClick(final int y) {
+ final int midpoint = (maximum + visibleAmount) / 2;
+ setPostion(y < midpoint ? minimum : maximum);
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ScrollBarRender.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ScrollBarRender.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ScrollBarRender.java
new file mode 100644
index 0000000..e3307fb
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ScrollBarRender.java
@@ -0,0 +1,26 @@
+/*
+ * 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.view.border;
+
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+
+public interface ScrollBarRender {
+ void draw(Canvas canvas, boolean isHorizontal, int x, int y, int width, int height, int scrollPosition, int visibleAmount);
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ScrollBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ScrollBorder.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ScrollBorder.java
new file mode 100644
index 0000000..3e092b2
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/ScrollBorder.java
@@ -0,0 +1,773 @@
+/*
+ * 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.view.border;
+
+import org.apache.isis.core.commons.debug.DebugBuilder;
+import org.apache.isis.core.metamodel.spec.ActionType;
+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.interaction.SimpleInternalDrag;
+import org.apache.isis.viewer.dnd.view.Click;
+import org.apache.isis.viewer.dnd.view.ContentDrag;
+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.UserActionSet;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewAreaType;
+import org.apache.isis.viewer.dnd.view.Workspace;
+import org.apache.isis.viewer.dnd.view.base.AbstractViewDecorator;
+import org.apache.isis.viewer.dnd.view.base.NullView;
+import org.apache.isis.viewer.dnd.view.option.UserActionAbstract;
+
+/**
+ * A scroll border provides a window on a larger view, providing scrollbars as a
+ * way of moving the visible part of that view around the actual visible viewing
+ * area. To achieve this the view is divided up into five main areas, not all of
+ * which are used. In the centre is the viewing area of the underlying view. At
+ * the bottom and to the right... At the top and to the left are headers that
+ */
+public class ScrollBorder extends AbstractViewDecorator {
+ private static ScrollBarRender render;
+ private static final int CENTER = 3;
+ private static final int NORTH = 1;
+ private static final int SOUTH = 5;
+ private static final int CORNER = 0;
+ private static final int SCROLLBAR_WIDTH = 16;
+ private static final int WEST = 2;
+ private static final int EAST = 4;
+
+ public static void setRender(final ScrollBarRender render) {
+ ScrollBorder.render = render;
+ }
+
+ private final ScrollBar horizontalScrollBar = new ScrollBar();
+ private final ScrollBar verticalScrollBar = new ScrollBar();
+ protected int bottom;
+ protected int left;
+ private View leftHeader;
+ protected int right;
+ private Size size = new Size();
+ protected int top;
+ private View topHeader;
+ private int dragArea = CENTER;
+ private int offsetToThumbEdge;
+
+ public ScrollBorder(final View view) {
+ this(view, new NullView(), new NullView());
+ }
+
+ /**
+ * Note - the leftHeader, if it is specified, view must be the same height
+ * as the content view and the rightHeader, if it is specified, must be the
+ * same width.
+ */
+ public ScrollBorder(final View content, final View leftHeader, final View topHeader) {
+ super(content);
+ bottom = right = SCROLLBAR_WIDTH;
+ horizontalScrollBar.setPostion(0);
+ verticalScrollBar.setPostion(0);
+ setLeftHeader(leftHeader);
+ setTopHeader(topHeader);
+ }
+
+ public void setTopHeader(final View topHeader) {
+ this.topHeader = topHeader;
+ topHeader.setParent(getView());
+ top = topHeader.getRequiredSize(new Size()).getHeight();
+ }
+
+ public void setLeftHeader(final View leftHeader) {
+ this.leftHeader = leftHeader;
+ leftHeader.setParent(getView());
+ left = leftHeader.getRequiredSize(new Size()).getWidth();
+ }
+
+ private int adjust(final Click click) {
+ return adjust(click.getLocation());
+ }
+
+ private int adjust(final ContentDrag drag) {
+ return adjust(drag.getTargetLocation());
+ }
+
+ private int adjust(final Location location) {
+ final Bounds contentArea = viewportArea();
+ final Offset offset = offset();
+ final int yOffset = offset.getDeltaY();
+ final int xOffset = offset.getDeltaX();
+ if (contentArea.contains(location)) {
+ location.subtract(left, top);
+ location.add(xOffset, yOffset);
+ return CENTER;
+ } else {
+ final int x = location.getX();
+ final int y = location.getY();
+
+ if (x > contentArea.getX2() && y >= contentArea.getY() && y <= contentArea.getY2()) {
+ // vertical scrollbar
+ location.subtract(0, contentArea.getY());
+ return EAST;
+ } else if (y > contentArea.getY2() && x >= contentArea.getX() && x <= contentArea.getX2()) {
+ // horzontal scrollbar
+ location.subtract(contentArea.getX(), 0);
+ return SOUTH;
+ } else if (y < contentArea.getY() && x >= contentArea.getX() && x <= contentArea.getX2()) {
+ // top border
+ location.subtract(left, 0);
+ location.add(xOffset, 0);
+ return NORTH;
+ } else if (x < contentArea.getX() && y >= contentArea.getY() && y <= contentArea.getY2()) {
+ // left border
+ location.subtract(0, top);
+ location.add(0, yOffset);
+ return WEST;
+ } else {
+ // ignore;
+ location.setX(-1);
+ location.setY(-1);
+ return CORNER;
+ }
+ }
+
+ }
+
+ protected Bounds viewportArea() {
+ return new Bounds(left, top, getSize().getWidth() - left - right, getSize().getHeight() - top - bottom);
+ }
+
+ @Override
+ protected void debugDetails(final DebugBuilder debug) {
+ super.debugDetails(debug);
+ debug.append("\n Top header: " + (topHeader == null ? "none" : topHeader.toString()));
+ debug.append("\n Left header: " + (leftHeader == null ? "none" : leftHeader.toString()));
+
+ debug.append("\n Vertical scrollbar ");
+ debug.append("\n offset " + top);
+ debug.append("\n position " + verticalScrollBar.getPosition());
+ debug.append("\n minimum " + verticalScrollBar.getMinimum());
+ debug.append("\n maximum " + verticalScrollBar.getMaximum());
+ debug.append("\n visible amount " + verticalScrollBar.getVisibleAmount());
+
+ debug.append("\n Horizontal scrollbar ");
+ debug.append("\n offset " + left);
+ debug.append("\n position " + horizontalScrollBar.getPosition());
+ debug.append("\n minimum " + horizontalScrollBar.getMinimum());
+ debug.append("\n maximum " + horizontalScrollBar.getMaximum());
+ debug.append("\n visible amount " + horizontalScrollBar.getVisibleAmount());
+ debug.append("\n Viewport area " + viewportArea());
+ debug.appendln("\n Offset " + offset());
+ }
+
+ @Override
+ public void drag(final InternalDrag drag) {
+ switch (dragArea) {
+ case NORTH:
+ drag.getLocation().subtract(offset().getDeltaX(), top);
+ topHeader.drag(drag);
+ break;
+
+ case WEST:
+ drag.getLocation().subtract(left, offset().getDeltaY());
+ leftHeader.drag(drag);
+ break;
+
+ case CENTER:
+ drag.getLocation().subtract(offset());
+ wrappedView.drag(drag);
+ break;
+
+ case SOUTH:
+ final int x = drag.getLocation().getX() - left;
+ horizontalScrollBar.setPostion(x - offsetToThumbEdge);
+ markDamaged();
+ break;
+
+ case EAST:
+ final int y = drag.getLocation().getY() - top;
+ verticalScrollBar.setPostion(y - offsetToThumbEdge);
+ markDamaged();
+ break;
+
+ default:
+ return;
+ }
+ }
+
+ @Override
+ public DragEvent dragStart(final DragStart drag) {
+ final int area = adjust(drag);
+ dragArea = area;
+ switch (dragArea) {
+ case NORTH:
+ return topHeader.dragStart(drag);
+
+ case WEST:
+ return leftHeader.dragStart(drag);
+
+ case CENTER:
+ return wrappedView.dragStart(drag);
+
+ case SOUTH:
+ return dragStartSouth(drag);
+
+ case EAST:
+ return dragStartEast(drag);
+
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public void dragCancel(final InternalDrag drag) {
+ switch (dragArea) {
+ case NORTH:
+ drag.getLocation().subtract(offset().getDeltaX(), top);
+ topHeader.dragCancel(drag);
+ break;
+
+ case WEST:
+ drag.getLocation().subtract(left, offset().getDeltaY());
+ leftHeader.dragCancel(drag);
+ break;
+
+ case CENTER:
+ drag.getLocation().subtract(offset());
+ wrappedView.dragCancel(drag);
+ break;
+ }
+ }
+
+ @Override
+ public void dragTo(final InternalDrag drag) {
+ switch (dragArea) {
+ case NORTH:
+ drag.getLocation().subtract(offset().getDeltaX(), top);
+ topHeader.dragTo(drag);
+ break;
+
+ case WEST:
+ drag.getLocation().subtract(left, offset().getDeltaY());
+ leftHeader.dragTo(drag);
+ break;
+
+ case CENTER:
+ drag.getLocation().subtract(offset());
+ wrappedView.dragTo(drag);
+ break;
+
+ case SOUTH:
+ case EAST:
+ default:
+ // ignore
+
+ }
+ }
+
+ @Override
+ public View dragFrom(final Location location) {
+ adjust(location);
+ switch (dragArea) {
+ case NORTH:
+ return topHeader.dragFrom(location);
+
+ case WEST:
+ return leftHeader.dragFrom(location);
+
+ case CENTER:
+ return wrappedView.dragFrom(location);
+ }
+
+ return null;
+ }
+
+ @Override
+ public void dragIn(final ContentDrag drag) {
+ adjust(drag);
+ switch (dragArea) {
+ case NORTH:
+ topHeader.dragIn(drag);
+ break;
+
+ case WEST:
+ leftHeader.dragIn(drag);
+ break;
+
+ case CENTER:
+ wrappedView.dragIn(drag);
+ break;
+
+ case SOUTH:
+ case EAST:
+ default:
+ System.out.println(this + " ignored");
+
+ // ignore
+ }
+ }
+
+ @Override
+ public void dragOut(final ContentDrag drag) {
+ adjust(drag);
+ switch (dragArea) {
+ case NORTH:
+ topHeader.dragOut(drag);
+ break;
+
+ case WEST:
+ leftHeader.dragOut(drag);
+ break;
+
+ case CENTER:
+ wrappedView.dragOut(drag);
+ break;
+
+ case SOUTH:
+ case EAST:
+ default:
+ // ignore
+ }
+ }
+
+ private DragEvent dragStartEast(final DragStart drag) {
+ final Location location = drag.getLocation();
+ final int y = location.getY();
+ if (verticalScrollBar.isOnThumb(y)) {
+ // offset is the distance from the left/top of the thumb to the
+ // pointer
+ offsetToThumbEdge = y - verticalScrollBar.getPosition();
+ return new SimpleInternalDrag(this, new Offset(super.getAbsoluteLocation()));
+ } else {
+ return null;
+ }
+ }
+
+ private DragEvent dragStartSouth(final DragStart drag) {
+ final Location location = drag.getLocation();
+ final int x = location.getX();
+ if (horizontalScrollBar.isOnThumb(x)) {
+ offsetToThumbEdge = x - horizontalScrollBar.getPosition();
+ return new SimpleInternalDrag(this, new Offset(super.getAbsoluteLocation()));
+ } else {
+ return null;
+ }
+ }
+
+ private int adjust(final DragStart drag) {
+ return adjust(drag.getLocation());
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ final Bounds contents = viewportArea();
+ final Offset offset = offset();
+ final int x = offset.getDeltaX();
+ final int y = offset.getDeltaY();
+
+ final int contentWidth = contents.getWidth();
+ final int contentHeight = contents.getHeight();
+
+ final Canvas headerCanvasLeft = canvas.createSubcanvas(0, top, left, contentHeight);
+ headerCanvasLeft.offset(0, -y);
+ leftHeader.draw(headerCanvasLeft);
+
+ final Canvas headerCanvasRight = canvas.createSubcanvas(left, 0, contentWidth, top);
+ headerCanvasRight.offset(-x, 0);
+ topHeader.draw(headerCanvasRight);
+
+ final Color thumbColor = Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY2);
+ drawVerticalScrollBar(canvas, contentWidth, contentHeight, thumbColor);
+ drawHorizontalScrollBar(canvas, contentWidth, contentHeight, thumbColor);
+
+ final Canvas contentCanvas = canvas.createSubcanvas(left, top, contentWidth, contentHeight);
+ contentCanvas.offset(-x, -y);
+
+ if (Toolkit.debug) {
+ canvas.drawRectangle(contents.getX(), contents.getY(), contents.getWidth(), contents.getHeight(), Toolkit.getColor(ColorsAndFonts.COLOR_DEBUG_BOUNDS_DRAW));
+ }
+
+ // drawContent(canvas, contentWidth, contentHeight);
+ wrappedView.draw(contentCanvas);
+
+ if (Toolkit.debug) {
+ final Size size = getSize();
+ canvas.drawRectangle(0, 0, size.getWidth(), size.getHeight(), Toolkit.getColor(ColorsAndFonts.COLOR_DEBUG_BOUNDS_VIEW));
+ canvas.drawLine(0, size.getHeight() / 2, size.getWidth() - 1, size.getHeight() / 2, Toolkit.getColor(ColorsAndFonts.COLOR_DEBUG_BOUNDS_VIEW));
+ canvas.drawLine(0, getBaseline(), size.getWidth() - 1, getBaseline(), Toolkit.getColor(ColorsAndFonts.COLOR_DEBUG_BASELINE));
+ }
+
+ }
+
+ // TODO merge these two methods
+ private void drawVerticalScrollBar(final Canvas canvas, final int contentWidth, final int contentHeight, final Color color) {
+ final int verticalVisibleAmount = verticalScrollBar.getVisibleAmount();
+ final int verticalScrollPosition = verticalScrollBar.getPosition();
+ if (right > 0 && (verticalScrollPosition > top || verticalVisibleAmount < contentHeight)) {
+ final int x = contentWidth + left;
+ render.draw(canvas, false, x, top, SCROLLBAR_WIDTH, contentHeight, verticalScrollPosition, verticalVisibleAmount);
+ /*
+ * canvas.drawSolidRectangle(x + 1, top, SCROLLBAR_WIDTH - 1,
+ * contentHeight,
+ * Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY3));
+ * canvas.drawSolidRectangle(x + 1, top + verticalScrollPosition,
+ * SCROLLBAR_WIDTH - 2, verticalVisibleAmount, color);
+ * canvas.drawRectangle(x, top, SCROLLBAR_WIDTH, contentHeight,
+ * Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY2));
+ * canvas.drawRectangle(x + 1, top + verticalScrollPosition,
+ * SCROLLBAR_WIDTH - 2, verticalVisibleAmount, Toolkit
+ * .getColor(ColorsAndFonts.COLOR_SECONDARY1));
+ *
+ * DrawingUtil.drawHatching(canvas, x + 3, top +
+ * verticalScrollPosition + 4, SCROLLBAR_WIDTH - 6,
+ * verticalVisibleAmount - 8,
+ * Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY1),
+ * Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY3));
+ */
+ }
+ }
+
+ private void drawHorizontalScrollBar(final Canvas canvas, final int contentWidth, final int contentHeight, final Color color) {
+ final int horizontalScrollPosition = horizontalScrollBar.getPosition();
+ final int horizontalVisibleAmount = horizontalScrollBar.getVisibleAmount();
+ if (bottom > 0 && (horizontalScrollPosition > left || horizontalVisibleAmount < contentWidth)) {
+ final int x = 0; // left + horizontalScrollPosition;
+ final int y = contentHeight + top;
+ render.draw(canvas, true, x, y, contentWidth, SCROLLBAR_WIDTH, horizontalScrollPosition, horizontalVisibleAmount);
+ /*
+ * canvas.drawSolidRectangle(left, y + 1, contentWidth,
+ * SCROLLBAR_WIDTH - 1,
+ * Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY3));
+ * canvas.drawSolidRectangle(x, y + 1, horizontalVisibleAmount,
+ * SCROLLBAR_WIDTH - 2, color); canvas.drawRectangle(left, y,
+ * contentWidth, SCROLLBAR_WIDTH,
+ * Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY2 ));
+ * canvas.drawRectangle(x, y + 1, horizontalVisibleAmount,
+ * SCROLLBAR_WIDTH - 2,
+ * Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY1));
+ *
+ * DrawingUtil.drawHatching(canvas, x + 5, y + 3,
+ * horizontalVisibleAmount - 10, SCROLLBAR_WIDTH - 6, Toolkit
+ * .getColor(ColorsAndFonts.COLOR_PRIMARY1),
+ * Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY3));
+ */
+ }
+ }
+
+ @Override
+ public void firstClick(final Click click) {
+ final int area = adjust(click);
+ switch (area) {
+ case NORTH:
+ topHeader.firstClick(click);
+ break;
+
+ case WEST:
+ leftHeader.firstClick(click);
+ break;
+
+ case CENTER:
+ wrappedView.firstClick(click);
+ break;
+
+ case SOUTH:
+ // TODO allow modified click to move thumb to the pointer, rather
+ // than paging.
+ horizontalScrollBar.firstClick(click.getLocation().getX(), click.button3());
+ break;
+
+ case EAST:
+ verticalScrollBar.firstClick(click.getLocation().getY(), click.button3());
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public Location getAbsoluteLocation() {
+ final Location location = super.getAbsoluteLocation();
+ location.subtract(offset());
+ return location;
+ }
+
+ @Override
+ public Bounds getBounds() {
+ return new Bounds(getLocation(), getSize());
+ }
+
+ @Override
+ public Size getRequiredSize(final Size maximumSize) {
+ final Size size = wrappedView.getRequiredSize(new Size(maximumSize));
+ if (size.getWidth() > maximumSize.getWidth()) {
+ size.extendHeight(SCROLLBAR_WIDTH);
+ }
+ if (size.getHeight() > maximumSize.getHeight()) {
+ size.extendWidth(SCROLLBAR_WIDTH);
+ }
+ size.extend(left, top);
+ size.limitSize(maximumSize);
+ return size;
+ }
+
+ @Override
+ public Size getSize() {
+ return new Size(size);
+ }
+
+ @Override
+ public View identify(final Location location) {
+ getViewManager().getSpy().addTrace(this, "mouse location within border", location);
+ getViewManager().getSpy().addTrace(this, "non border area", viewportArea());
+
+ final int area = adjust(location);
+ switch (area) {
+ case NORTH:
+ return topHeader.identify(location);
+
+ case WEST:
+ return leftHeader.identify(location);
+
+ case CENTER:
+ return wrappedView.identify(location);
+
+ case SOUTH:
+ getViewManager().getSpy().addTrace(this, "over scroll bar area", viewportArea());
+ return getView();
+
+ case EAST:
+ getViewManager().getSpy().addTrace(this, "over scroll bar area", viewportArea());
+ return getView();
+
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public void limitBoundsWithin(final Size size) {
+ super.limitBoundsWithin(size);
+ verticalScrollBar.limit();
+ horizontalScrollBar.limit();
+ }
+
+ @Override
+ public void markDamaged(final Bounds bounds) {
+ /*
+ * TODO this only works for the main content area, not for the headers.
+ * how do we figure out which area to adjust for?
+ */
+ final Offset offset = offset();
+ bounds.translate(-offset.getDeltaX(), -offset.getDeltaY());
+ bounds.translate(left, top);
+ super.markDamaged(bounds);
+ }
+
+ @Override
+ public void mouseMoved(final Location location) {
+ final int area = adjust(location);
+ switch (area) {
+ case NORTH:
+ topHeader.mouseMoved(location);
+ break;
+
+ case WEST:
+ leftHeader.mouseMoved(location);
+ break;
+
+ case CENTER:
+ // location.add(offset());
+ // location.move(-left, -top);
+ wrappedView.mouseMoved(location);
+ break;
+
+ case SOUTH:
+ case EAST:
+ default:
+ break;
+ }
+ }
+
+ private Offset offset() {
+ final Bounds contents = viewportArea();
+ final int width = contents.getWidth();
+ final int x = width == 0 ? 0 : horizontalScrollBar.getPosition() * wrappedView.getRequiredSize(Size.createMax()).getWidth() / width;
+ final int height = contents.getHeight();
+ final int y = height == 0 ? 0 : verticalScrollBar.getPosition() * wrappedView.getRequiredSize(Size.createMax()).getHeight() / height;
+ return new Offset(x, y);
+ }
+
+ protected boolean overContent(final Location location) {
+ return viewportArea().contains(location);
+ }
+
+ public void reset() {
+ horizontalScrollBar.reset();
+ verticalScrollBar.reset();
+ }
+
+ /**
+ * Moves the scrollbar to beginning or the end when a double click occurs on
+ * that side.
+ */
+ @Override
+ public void secondClick(final Click click) {
+ final int area = adjust(click);
+ switch (area) {
+ case NORTH:
+ topHeader.secondClick(click);
+ break;
+
+ case WEST:
+ leftHeader.secondClick(click);
+ break;
+
+ case CENTER:
+ wrappedView.secondClick(click);
+ break;
+
+ case SOUTH:
+ horizontalScrollBar.secondClick(click.getLocation().getX());
+ break;
+
+ case EAST:
+ verticalScrollBar.secondClick(click.getLocation().getY());
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void thirdClick(final Click click) {
+ final int area = adjust(click);
+ switch (area) {
+ case NORTH:
+ topHeader.thirdClick(click);
+ break;
+
+ case WEST:
+ leftHeader.thirdClick(click);
+ break;
+
+ case CENTER:
+ wrappedView.thirdClick(click);
+ break;
+
+ case SOUTH:
+ case EAST:
+ default:
+ // ignore
+ break;
+ }
+ }
+
+ @Override
+ public void setBounds(final Bounds bounds) {
+ setLocation(bounds.getLocation());
+ setSize(bounds.getSize());
+ }
+
+ @Override
+ public void setSize(final Size size) {
+ // TODO need to restore the offset after size change - see limitBounds
+ // float verticalRatio = ((float) verticalScrollPosition) /
+ // contentArea().getHeight();
+
+ this.size = new Size(size);
+
+ final Size contentSize = wrappedView.getRequiredSize(Size.createMax());
+ wrappedView.setSize(contentSize);
+
+ final int availableHeight2 = size.getHeight() - top;
+ final int contentHeight2 = contentSize.getHeight();
+ right = availableHeight2 >= contentHeight2 ? 0 : SCROLLBAR_WIDTH;
+
+ final int availableWidth2 = size.getWidth() - left;
+ final int contentWidth2 = contentSize.getWidth();
+ bottom = availableWidth2 >= contentWidth2 ? 0 : SCROLLBAR_WIDTH;
+
+ final Bounds viewport = viewportArea();
+
+ final int viewportHeight = viewport.getHeight();
+ final int maxContentHeight = Math.max(viewportHeight, contentSize.getHeight());
+
+ verticalScrollBar.setSize(viewportHeight, maxContentHeight);
+ if (leftHeader != null) {
+ leftHeader.setSize(new Size(left, maxContentHeight));
+ }
+
+ final int viewportWidth = viewport.getWidth();
+ final int maxContentWidth = Math.max(viewportWidth, contentSize.getWidth());
+
+ horizontalScrollBar.setSize(viewportWidth, maxContentWidth);
+ if (topHeader != null) {
+ topHeader.setSize(new Size(maxContentWidth, top));
+ }
+ }
+
+ public int getVerticalPosition() {
+ return verticalScrollBar.getPosition();
+ }
+
+ public int getHorizontalPosition() {
+ return horizontalScrollBar.getPosition();
+ }
+
+ @Override
+ public ViewAreaType viewAreaType(final Location location) {
+ final int area = adjust(location);
+ switch (area) {
+ case NORTH:
+ return topHeader.viewAreaType(location);
+
+ case WEST:
+ return leftHeader.viewAreaType(location);
+
+ case CENTER:
+ return wrappedView.viewAreaType(location);
+
+ case SOUTH:
+ case EAST:
+ default:
+ return ViewAreaType.INTERNAL;
+ }
+ }
+
+ @Override
+ public void viewMenuOptions(final UserActionSet menuOptions) {
+ super.viewMenuOptions(menuOptions);
+ menuOptions.add(new UserActionAbstract("Reset scroll border", ActionType.DEBUG) {
+ @Override
+ public void execute(final Workspace workspace, final View view, final Location at) {
+ reset();
+ invalidateLayout();
+ }
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SelectObjectBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SelectObjectBorder.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SelectObjectBorder.java
new file mode 100644
index 0000000..c99271d
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SelectObjectBorder.java
@@ -0,0 +1,130 @@
+/*
+ * 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.view.border;
+
+import java.awt.event.KeyEvent;
+
+import org.apache.isis.core.commons.debug.DebugBuilder;
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+import org.apache.isis.viewer.dnd.drawing.ColorsAndFonts;
+import org.apache.isis.viewer.dnd.drawing.Location;
+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.KeyboardAction;
+import org.apache.isis.viewer.dnd.view.SubviewDecorator;
+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.ViewAxis;
+import org.apache.isis.viewer.dnd.view.Workspace;
+import org.apache.isis.viewer.dnd.view.base.AbstractBorder;
+import org.apache.isis.viewer.dnd.view.option.UserActionAbstract;
+
+public class SelectObjectBorder extends AbstractBorder {
+ private final SelectableViewAxis axis;
+
+ public static class Factory implements SubviewDecorator {
+ @Override
+ public ViewAxis createAxis(final Content content) {
+ return null;
+ }
+
+ @Override
+ public View decorate(final Axes axes, final View view) {
+ if (axes.contains(SelectableViewAxis.class)) {
+ final SelectableViewAxis axis = axes.getAxis(SelectableViewAxis.class);
+ return new SelectObjectBorder(view, axis);
+ } else {
+ return view;
+ }
+ }
+ }
+
+ protected SelectObjectBorder(final View view, final SelectableViewAxis axis) {
+ super(view);
+ this.axis = axis;
+ }
+
+ @Override
+ public Axes getViewAxes() {
+ final Axes viewAxes = super.getViewAxes();
+ viewAxes.add(axis);
+ return viewAxes;
+ }
+
+ @Override
+ protected void debugDetails(final DebugBuilder debug) {
+ super.debugDetails(debug);
+ debug.appendln("axis", axis);
+ }
+
+ @Override
+ public void keyPressed(final KeyboardAction key) {
+ if (key.getKeyCode() == KeyEvent.VK_SPACE) {
+ selectNode();
+ } else {
+ super.keyPressed(key);
+ }
+ }
+
+ @Override
+ public void firstClick(final Click click) {
+ final int x = click.getLocation().getX();
+ final int y = click.getLocation().getY();
+ if (withinSelectorBounds(x, y) && click.button1()) {
+ selectNode();
+ } else {
+ super.firstClick(click);
+ }
+ }
+
+ private void selectNode() {
+ axis.selected(getView());
+ }
+
+ private boolean withinSelectorBounds(final int x, final int y) {
+ return true;
+ }
+
+ @Override
+ public void viewMenuOptions(final UserActionSet options) {
+ super.viewMenuOptions(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";
+ }
+ });
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ if (axis.isSelected(getView())) {
+ clearBackground(canvas, Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY3));
+ }
+ super.draw(canvas);
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SelectableViewAxis.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SelectableViewAxis.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SelectableViewAxis.java
new file mode 100644
index 0000000..b763a66
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/border/SelectableViewAxis.java
@@ -0,0 +1,51 @@
+/*
+ * 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.view.border;
+
+import org.apache.isis.core.commons.lang.ToString;
+import org.apache.isis.viewer.dnd.view.Selectable;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewAxis;
+
+public class SelectableViewAxis implements ViewAxis {
+ private View selectedView;
+ private final Selectable target;
+
+ public SelectableViewAxis(final Selectable view) {
+ target = view;
+ }
+
+ public void selected(final View view) {
+ selectedView = view;
+ target.setSelectedNode(selectedView);
+ }
+
+ public boolean isSelected(final View view) {
+ return selectedView == view;
+ }
+
+ @Override
+ public String toString() {
+ final ToString s = new ToString(this);
+ s.append("target", target.getId());
+ s.append("selected", selectedView == null ? "none" : selectedView.getId());
+ return s.toString();
+ }
+}