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
[13/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/field/SingleLineTextField.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/SingleLineTextField.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/SingleLineTextField.java
new file mode 100644
index 0000000..0794e3b
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/SingleLineTextField.java
@@ -0,0 +1,129 @@
+/*
+ * 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.field;
+
+import org.apache.isis.core.commons.exceptions.IsisException;
+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.ViewConstants;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.content.TextParseableContent;
+import org.apache.isis.viewer.dnd.view.text.TextContent;
+import org.apache.isis.viewer.dnd.view.text.TextUtils;
+
+public class SingleLineTextField extends TextField {
+ private static final int LIMIT = 20;
+ private int offset = 0;
+
+ public SingleLineTextField(final TextParseableContent content, final ViewSpecification specification, final boolean showLines) {
+ super(content, specification, showLines, TextContent.NO_WRAPPING);
+ }
+
+ @Override
+ protected void align() {
+ final String line = textContent.getText(0);
+ if (line != null) {
+ final int maxWidth = getMaxFieldWidth();
+ final int leftLimit = offset + LIMIT;
+ final int rightLimit = offset + maxWidth - LIMIT;
+
+ if (cursor.getCharacter() > line.length()) {
+ cursor.end();
+ }
+
+ final int cursorPosition = style.stringWidth(line.substring(0, cursor.getCharacter()));
+ if (cursorPosition > rightLimit) {
+ offset = offset + (cursorPosition - rightLimit);
+ offset = Math.min(style.stringWidth(line), offset);
+ } else if (cursorPosition < leftLimit) {
+ offset = offset - (leftLimit - cursorPosition);
+ offset = Math.max(0, offset);
+ }
+ }
+ }
+
+ @Override
+ protected void drawHighlight(final Canvas canvas, final int maxWidth) {
+ final int baseline = getBaseline();
+ final int top = baseline - style.getAscent();
+
+ int from = selection.from().getCharacter();
+ int to = selection.to().getCharacter();
+
+ final String line = textContent.getText(0);
+ if (to >= line.length()) {
+ to = line.length();
+ }
+ if (from >= line.length()) {
+ from = line.length();
+ }
+ if (line != null) {
+ final int start = style.stringWidth(line.substring(0, from));
+ final int end = style.stringWidth(line.substring(0, to));
+ canvas.drawSolidRectangle(start + (ViewConstants.HPADDING), top, end - start, style.getLineHeight(), Toolkit.getColor(ColorsAndFonts.COLOR_TEXT_HIGHLIGHT));
+ }
+ }
+
+ @Override
+ protected void drawLines(final Canvas canvas, final Color color, final int width) {
+ final int baseline = getBaseline();
+ canvas.drawLine(ViewConstants.HPADDING, baseline, ViewConstants.HPADDING + width, baseline, color);
+ }
+
+ @Override
+ protected void drawText(final Canvas canvas, final Color textColor, final int width) {
+ final String[] lines = textContent.getDisplayLines();
+ if (lines.length > 1) {
+ throw new IsisException("Single line text field should contain a string that contains no line breaks; contains " + lines.length);
+ }
+
+ final String chars = lines[0];
+ if (chars == null) {
+ throw new IsisException();
+ }
+ if (chars.endsWith("\n")) {
+ throw new RuntimeException();
+ }
+
+ final int baseline = getBaseline();
+
+ // draw cursor
+ if (hasFocus() && canChangeValue().isAllowed()) {
+ final int at = Math.min(cursor.getCharacter(), chars.length());
+ final int pos = style.stringWidth(chars.substring(0, at)) - offset + ViewConstants.HPADDING;
+ canvas.drawLine(pos, (baseline + style.getDescent()), pos, baseline - style.getAscent(), Toolkit.getColor(ColorsAndFonts.COLOR_TEXT_CURSOR));
+ }
+
+ // draw text
+ final String line = hasFocus() ? chars : TextUtils.limitText(chars, style, width);
+ canvas.drawText(line, ViewConstants.HPADDING - offset, baseline, textColor, style);
+ }
+
+ @Override
+ public void setMaximumSize(final Size size) {
+ final int width = Math.max(180, size.getWidth() - ViewConstants.HPADDING);
+ setWidth(width);
+ invalidateLayout();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextField.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextField.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextField.java
new file mode 100644
index 0000000..58c3605
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextField.java
@@ -0,0 +1,757 @@
+/*
+ * 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.field;
+
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.commons.debug.DebugBuilder;
+import org.apache.isis.core.commons.exceptions.IsisException;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
+import org.apache.isis.core.metamodel.facets.object.parseable.InvalidEntryException;
+import org.apache.isis.core.metamodel.facets.object.parseable.TextEntryParseException;
+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.Size;
+import org.apache.isis.viewer.dnd.drawing.Text;
+import org.apache.isis.viewer.dnd.interaction.SimpleInternalDrag;
+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.InternalDrag;
+import org.apache.isis.viewer.dnd.view.KeyboardAction;
+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.ViewConstants;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.base.TextView;
+import org.apache.isis.viewer.dnd.view.border.BackgroundBorder;
+import org.apache.isis.viewer.dnd.view.border.LineBorder;
+import org.apache.isis.viewer.dnd.view.content.TextParseableContent;
+import org.apache.isis.viewer.dnd.view.text.CursorPosition;
+import org.apache.isis.viewer.dnd.view.text.TextBlockTarget;
+import org.apache.isis.viewer.dnd.view.text.TextContent;
+import org.apache.isis.viewer.dnd.view.text.TextSelection;
+
+public abstract class TextField extends TextParseableFieldAbstract implements TextBlockTarget {
+ private static final Logger LOG = Logger.getLogger(TextField.class);
+ protected static final Text style = Toolkit.getText(ColorsAndFonts.TEXT_NORMAL);
+ protected CursorPosition cursor;
+ private boolean identified;
+ private String invalidReason = null;
+ private boolean isSaved = true;
+ private final int maxLength;
+ private int displayWidth;
+ protected TextSelection selection;
+ private final boolean showLines;
+ protected TextContent textContent;
+ protected boolean useEmptyLines;
+
+ public TextField(final TextParseableContent content, final ViewSpecification specification, final boolean showLines, final int wrapStyle) {
+ this(content, specification, showLines, wrapStyle, false);
+ }
+
+ public TextField(final TextParseableContent content, final ViewSpecification specification, final boolean showLines, final int wrapStyle, final boolean useEmptyLines) {
+ super(content, specification);
+ this.showLines = showLines;
+
+ int typicalLength = content.getTypicalLineLength();
+ typicalLength = typicalLength == 0 ? TEXT_WIDTH : typicalLength / content.getNoLines();
+ setTextWidth(typicalLength);
+
+ this.maxLength = content.getMaximumLength();
+ this.useEmptyLines = useEmptyLines;
+
+ textContent = new TextContent(this, 1, wrapStyle, this.useEmptyLines);
+ cursor = new CursorPosition(textContent, 0, 0);
+ selection = new TextSelection(textContent);
+ final ObjectAdapter value = getValue();
+ textContent.setText(value == null ? "" : titleString(value));
+ cursor.home();
+ isSaved = true;
+ }
+
+ protected abstract void align();
+
+ @Override
+ public void contentMenuOptions(final UserActionSet options) {
+ super.contentMenuOptions(options);
+ options.add(new RevertFieldOption(this));
+ }
+
+ @Override
+ protected void clear() {
+ clearValue();
+ editComplete(false, false);
+ }
+
+ void clearValue() {
+ textContent.setText("");
+ cursor.home();
+ selection.resetTo(cursor);
+ changeMade();
+ }
+
+ @Override
+ protected void copyToClipboard() {
+ final boolean noSelection = selection.to().samePosition(selection.from());
+ final String text = noSelection ? textContent.getText() : textContent.getText(selection);
+ getViewManager().setClipboard(text, String.class);
+ LOG.debug("copied " + text);
+ }
+
+ @Override
+ public void debug(final DebugBuilder debug) {
+ super.debug(debug);
+ debug.appendln("text", textContent);
+ }
+
+ /**
+ * Delete the character to the left of the cursor.
+ */
+ public void delete() {
+ if (selection.hasSelection()) {
+ textContent.delete(selection);
+ selection.resetTo(selection.from());
+ } else {
+ textContent.deleteLeft(cursor);
+ cursor.left();
+ selection.resetTo(cursor);
+ }
+ changeMade();
+ }
+
+ /**
+ * Delete the character to the right of the cursor.
+ */
+ public void deleteForward() {
+ if (selection.hasSelection()) {
+ textContent.delete(selection);
+ selection.resetTo(selection.from());
+ } else {
+ textContent.deleteRight(cursor);
+ }
+ changeMade();
+ }
+
+ protected void down(final boolean shift) {
+ cursor.lineDown();
+ highlight(shift);
+ markDamaged();
+ }
+
+ @Override
+ public void drag(final InternalDrag drag) {
+ if (canChangeValue().isAllowed()) {
+ selection.extendTo(drag.getLocation());
+ markDamaged();
+ }
+ }
+
+ @Override
+ public DragEvent dragStart(final DragStart drag) {
+ final Location at = drag.getLocation();
+
+ final Location anchor = getAbsoluteLocation();
+ // TODO adjust anchor so only the field is rubberbanded
+ /*
+ * final Size size = getView().getSize(); final ViewAxis axis =
+ * getViewAxis(LabelAxis.class); if (axis instanceof LabelAxis) { final
+ * int width = ((LabelAxis) axis).getWidth(); size.contractWidth(width);
+ * anchor.add(width, 0); }
+ */
+ if (canChangeValue().isAllowed()) {
+ cursor.cursorAt(at);
+ resetSelection();
+ return new SimpleInternalDrag(this, anchor);
+ }
+
+ markDamaged();
+
+ return null;
+ }
+
+ @Override
+ public void dragTo(final InternalDrag drag) {
+ final Location at = drag.getLocation();
+ if (canChangeValue().isAllowed()) {
+ selection.extendTo(at);
+ markDamaged();
+ }
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ super.draw(canvas);
+
+ final int width = getMaxFieldWidth();
+
+ align();
+
+ if (hasFocus() && selection.hasSelection()) {
+ drawHighlight(canvas, width);
+ }
+
+ /*
+ * if (showLines == true && canChangeValue().isAllowed()) { Color color
+ * = identified ? Toolkit.getColor(ColorsAndFonts.COLOR_IDENTIFIED) :
+ * Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY3); color = hasFocus()
+ * ? Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY1) : color;
+ * drawLines(canvas, color, width); }
+ */
+ Color textColor;
+ Color lineColor;
+ if (getState().isInvalid()) {
+ textColor = Toolkit.getColor(ColorsAndFonts.COLOR_INVALID);
+ lineColor = Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY3);
+ } else if (hasFocus()) {
+ if (isSaved) {
+ textColor = Toolkit.getColor(ColorsAndFonts.COLOR_TEXT_SAVED);
+ lineColor = Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY1);
+ } else {
+ textColor = Toolkit.getColor(ColorsAndFonts.COLOR_TEXT_EDIT);
+ lineColor = Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY1);
+ }
+ } else if (identified) {
+ textColor = Toolkit.getColor(ColorsAndFonts.COLOR_IDENTIFIED);
+ lineColor = Toolkit.getColor(ColorsAndFonts.COLOR_IDENTIFIED);
+ } else {
+ textColor = Toolkit.getColor(ColorsAndFonts.COLOR_BLACK);
+ lineColor = Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY3);
+ }
+
+ if (showLines == true && canChangeValue().isAllowed()) {
+ drawLines(canvas, lineColor, width);
+ }
+ drawText(canvas, textColor, width);
+ }
+
+ protected abstract void drawHighlight(final Canvas canvas, final int maxWidth);
+
+ protected abstract void drawLines(final Canvas canvas, final Color color, final int width);
+
+ protected abstract void drawText(final Canvas canvas, final Color textColor, final int width);
+
+ @Override
+ public void editComplete(final boolean moveFocus, final boolean toNextField) {
+ if (canChangeValue().isAllowed() && !isSaved) {
+ isSaved = true;
+ initiateSave(moveFocus);
+ } else if (moveFocus) {
+ if (toNextField) {
+ getFocusManager().focusNextView();
+ } else {
+ getFocusManager().focusPreviousView();
+ }
+ }
+ }
+
+ protected void end(final boolean alt, final boolean shift) {
+ if (alt) {
+ cursor.bottom();
+ } else {
+ cursor.end();
+ }
+
+ highlight(shift);
+ markDamaged();
+ }
+
+ /**
+ * Called when 'enter' has been pressed. Return indicates whether event has
+ * been consumed; by default it hasn't. This default implementation marks
+ * field as having edit completed.
+ */
+ protected boolean enter() {
+ editComplete(false, false);
+ return false;
+ }
+
+ @Override
+ public void entered() {
+ if (canChangeValue().isAllowed()) {
+ getFeedbackManager().showTextCursor();
+ identified = true;
+ markDamaged();
+ }
+ super.entered();
+ }
+
+ protected void escape() {
+ if (isSaved) {
+ clearValue();
+ } else {
+ invalidReason = null;
+ refresh();
+ markDamaged();
+ }
+ }
+
+ @Override
+ public void exited() {
+ if (canChangeValue().isAllowed()) {
+ getFeedbackManager().showDefaultCursor();
+ identified = false;
+ markDamaged();
+ }
+ super.exited();
+ }
+
+ /**
+ * Responds to first click by placing the cursor between the two characters
+ * nearest the point of the mouse.
+ */
+ @Override
+ public void firstClick(final Click click) {
+ if (canChangeValue().isAllowed()) {
+ final Location at = click.getLocation();
+ at.subtract(ViewConstants.HPADDING, ViewConstants.VPADDING);
+ cursor.cursorAt(at);
+ resetSelection();
+
+ // testing
+ if (cursor.getLine() > textContent.getNoLinesOfContent()) {
+ throw new IsisException("not inside content for line " + cursor.getLine() + " : " + textContent.getNoLinesOfContent());
+ }
+
+ markDamaged();
+ }
+
+ if (!canChangeValue().isAllowed() || click.isShift() || click.button2()) {
+ final ObjectAdapter valueAdapter = getContent().getAdapter();
+ if (valueAdapter != null && valueAdapter.titleString().length() > 0) {
+ final View textView = new BackgroundBorder(Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY3), new LineBorder(1, Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY1), new TextView(getContent(), null)));
+ getViewManager().setOverlayView(textView);
+
+ final int offset = getView().getPadding().getLeft();
+ final Location location = getAbsoluteLocation();
+ location.add(offset, 0);
+ textView.setLocation(location);
+ textView.markDamaged();
+ }
+ }
+ }
+
+ @Override
+ public void focusLost() {
+ super.focusLost();
+ editComplete(false, false);
+ }
+
+ @Override
+ public void focusReceived() {
+ getFeedbackManager().setError(invalidReason == null ? "" : invalidReason);
+ resetSelection();
+ }
+
+ @Override
+ public int getBaseline() {
+ return getText().getAscent();
+ }
+
+ @Override
+ public Size getRequiredSize(final Size availableSpace) {
+ final int width = ViewConstants.HPADDING + displayWidth + ViewConstants.HPADDING;
+ int height;
+ if (textContent.getNoDisplayLines() == 1) {
+ height = getText().getTextHeight();
+ } else {
+ height = textContent.getNoDisplayLines() * getText().getLineHeight();
+ }
+ height = Math.max(height, Toolkit.defaultFieldHeight());
+ return new Size(width, height);
+ }
+
+ @Override
+ public int getMaxFieldWidth() {
+ return displayWidth;
+ }
+
+ @Override
+ public Text getText() {
+ return style;
+ }
+
+ @Override
+ String getSelectedText() {
+ return textContent.getText(selection);
+ }
+
+ private ObjectAdapter getValue() {
+ return getContent().getAdapter();
+ }
+
+ /**
+ * modifies the selection object so that text is selected if the flag is
+ * true, or text is unselected if false.
+ */
+ private void highlight(final boolean select) {
+ if (canChangeValue().isAllowed()) {
+ if (!select) {
+ selection.resetTo(cursor);
+ } else {
+ selection.extendTo(cursor);
+ }
+ }
+ }
+
+ protected void home(final boolean alt, final boolean shift) {
+ if (alt) {
+ cursor.top();
+ } else {
+ cursor.home();
+ }
+
+ highlight(shift);
+ markDamaged();
+ }
+
+ protected void changeMade() {
+ isSaved = false;
+ markDamaged();
+ if (getState().isInvalid()) {
+ getState().clearInvalid();
+ getFeedbackManager().clearError();
+ }
+ }
+
+ private void insert(final String characters) {
+ if (withinMaximum(characters.length())) {
+ final int noLines = textContent.getNoDisplayLines();
+ textContent.insert(cursor, characters);
+ cursor.right(characters.length());
+ if (textContent.getNoDisplayLines() != noLines) {
+ invalidateLayout();
+ }
+ changeMade();
+ } else {
+ getFeedbackManager().setError("Entry can be no longer than " + maxLength + " characters");
+ }
+ }
+
+ public boolean isIdentified() {
+ return identified;
+ }
+
+ /**
+ * Called when the user presses any key on the keyboard while this view has
+ * the focus.
+ */
+ @Override
+ public void keyPressed(final KeyboardAction key) {
+ if (!canChangeValue().isAllowed()) {
+ return;
+ }
+
+ final int keyCode = key.getKeyCode();
+ if (keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_SHIFT || keyCode == KeyEvent.VK_ALT) {
+ return;
+ }
+
+ final int modifiers = key.getModifiers();
+ // modifiers
+ final boolean alt = (modifiers & InputEvent.ALT_MASK) > 0;
+ final boolean shift = (modifiers & InputEvent.SHIFT_MASK) > 0;
+ final boolean ctrl = (modifiers & InputEvent.CTRL_MASK) > 0;
+
+ switch (keyCode) {
+ case KeyEvent.VK_PAGE_UP:
+ key.consume();
+ pageUp(shift, ctrl);
+ break;
+ case KeyEvent.VK_PAGE_DOWN:
+ key.consume();
+ pageDown(shift, ctrl);
+ break;
+ case KeyEvent.VK_V:
+ if (ctrl) {
+ key.consume();
+ pasteFromClipboard();
+ highlight(false);
+ }
+ break;
+ case KeyEvent.VK_C:
+ if (ctrl) {
+ key.consume();
+ copyToClipboard();
+ }
+ break;
+ case KeyEvent.VK_DOWN:
+ key.consume();
+ down(shift);
+ break;
+ case KeyEvent.VK_UP:
+ key.consume();
+ up(shift);
+ break;
+ case KeyEvent.VK_HOME:
+ key.consume();
+ home(alt, shift);
+ break;
+ case KeyEvent.VK_END:
+ key.consume();
+ end(alt, shift);
+ break;
+ case KeyEvent.VK_LEFT:
+ key.consume();
+ left(alt, shift);
+ break;
+ case KeyEvent.VK_RIGHT:
+ key.consume();
+ right(alt, shift);
+ break;
+ case KeyEvent.VK_DELETE:
+ key.consume();
+ deleteForward();
+ break;
+ case KeyEvent.VK_BACK_SPACE:
+ key.consume();
+ delete();
+ break;
+ case KeyEvent.VK_TAB:
+ key.consume();
+ final boolean moveToNextField = !shift;
+ tab(moveToNextField);
+ break;
+ case KeyEvent.VK_ENTER:
+ if (!enter()) {
+ getParent().keyPressed(key);
+ }
+ break;
+ case KeyEvent.VK_ESCAPE:
+ key.consume();
+ escape();
+ break;
+
+ default:
+ break;
+ }
+
+ LOG.debug("character at " + cursor.getCharacter() + " line " + cursor.getLine());
+ LOG.debug(selection);
+ }
+
+ /**
+ * Called when the user releases any key on the keyboard while this view has
+ * the focus.
+ */
+ @Override
+ public void keyReleased(final KeyboardAction action) {
+ }
+
+ /**
+ * Called when the user presses a non-control key (i.e. data entry keys and
+ * not shift, up-arrow etc). Such a key press will result in a prior call to
+ * <code>keyPressed</code> and a subsequent call to <code>keyReleased</code>
+ * .
+ */
+ @Override
+ public void keyTyped(final KeyboardAction action) {
+ if (canChangeValue().isAllowed()) {
+ insert("" + action.getKeyChar());
+ }
+ }
+
+ protected void left(final boolean alt, final boolean shift) {
+ if (alt) {
+ cursor.wordLeft();
+ } else {
+ cursor.left();
+ }
+
+ highlight(shift);
+ markDamaged();
+ }
+
+ protected void pageDown(final boolean shift, final boolean ctrl) {
+ if (ctrl) {
+ if (textContent.decreaseDepth()) {
+ textContent.alignDisplay(cursor.getLine());
+ invalidateLayout();
+ }
+ } else {
+ cursor.pageDown();
+ highlight(shift);
+ }
+ markDamaged();
+ }
+
+ protected void pageUp(final boolean shift, final boolean ctrl) {
+ if (ctrl) {
+ textContent.increaseDepth();
+ textContent.alignDisplay(cursor.getLine());
+ invalidateLayout();
+ } else {
+ cursor.pageUp();
+ highlight(shift);
+ }
+ markDamaged();
+ }
+
+ @Override
+ protected void pasteFromClipboard() {
+ try {
+ final String text = (String) getViewManager().getClipboard(String.class);
+ insert(text);
+ LOG.debug("pasted " + text);
+ } catch (final Throwable e) {
+ LOG.error("invalid paste operation " + e);
+ }
+ }
+
+ private String titleString(final ObjectAdapter object) {
+ return ((TextParseableContent) getContent()).titleString(object);
+ }
+
+ @Override
+ public void refresh() {
+ super.refresh();
+ final ObjectAdapter object = getValue();
+ if (object == null) {
+ textContent.setText("");
+ } else {
+ textContent.setText(titleString(object));
+ }
+ isSaved = true;
+ }
+
+ private void resetSelection() {
+ selection.resetTo(cursor);
+ }
+
+ protected void right(final boolean alt, final boolean shift) {
+ if (alt) {
+ cursor.wordRight();
+ } else {
+ cursor.right();
+ }
+
+ highlight(shift);
+ markDamaged();
+ }
+
+ @Override
+ protected void save() {
+ final String entry = textContent.getText();
+
+ // do nothing if entry is same as the value object
+ final ObjectAdapter value = getValue();
+ if (!entry.equals(value == null ? "" : value.titleString())) {
+ LOG.debug("field edited: \'" + entry + "\' to replace \'" + (value == null ? "" : value.titleString()) + "\'");
+
+ try {
+ parseEntry(entry.toString());
+ invalidReason = null;
+ getViewManager().getSpy().addAction("VALID ENTRY: " + entry);
+ markDamaged();
+ getParent().invalidateContent();
+ } catch (final TextEntryParseException e) {
+ invalidReason = "Invalid Entry: " + e.getMessage();
+ getFeedbackManager().setError(invalidReason);
+ getState().setInvalid();
+ markDamaged();
+ } catch (final InvalidEntryException e) {
+ invalidReason = "Invalid Entry: " + e.getMessage();
+ getFeedbackManager().setError(invalidReason);
+ getState().setInvalid();
+ markDamaged();
+ } catch (final ConcurrencyException e) {
+ invalidReason = "Update Failure: " + e.getMessage();
+ getState().setOutOfSynch();
+ markDamaged();
+ throw e;
+ } catch (final IsisException e) {
+ invalidReason = "Update Failure: " + e.getMessage();
+ getFeedbackManager().setError(invalidReason);
+ getState().setOutOfSynch();
+ markDamaged();
+ throw e;
+ }
+ }
+ }
+
+ @Override
+ public void secondClick(final Click click) {
+ if (canChangeValue().isAllowed()) {
+ selection.selectWord();
+ }
+ }
+
+ /**
+ * Set the maximum width of the field, as a number of characters
+ */
+ private void setTextWidth(final int noCharacters) {
+ displayWidth = getText().charWidth('5') * (noCharacters + 3);
+ }
+
+ /**
+ * Set the width of the field, as a number of pixels
+ */
+ public void setWidth(final int width) {
+ displayWidth = width;
+ }
+
+ @Override
+ public void setSize(final Size size) {
+ super.setSize(size);
+ setWidth(size.getWidth() - 2 * ViewConstants.HPADDING);
+ }
+
+ protected void tab(final boolean moveToNextField) {
+ editComplete(true, moveToNextField);
+ }
+
+ @Override
+ public void thirdClick(final Click click) {
+ if (canChangeValue().isAllowed()) {
+ selection.selectSentence();
+ markDamaged();
+ }
+ }
+
+ protected void up(final boolean shift) {
+ cursor.lineUp();
+ highlight(shift);
+ markDamaged();
+ }
+
+ private boolean withinMaximum(final int characters) {
+ return maxLength == 0 || textContent.getText().length() + characters <= maxLength;
+ }
+
+ void revertInvalidEntry() {
+ invalidReason = null;
+ refresh();
+ cursor.home();
+ getState().clearInvalid();
+ getFeedbackManager().clearError();
+ markDamaged();
+ }
+
+ public boolean hasInvalidEntry() {
+ return invalidReason != null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextFieldBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextFieldBorder.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextFieldBorder.java
new file mode 100644
index 0000000..9826266
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextFieldBorder.java
@@ -0,0 +1,49 @@
+/*
+ * 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.field;
+
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+import org.apache.isis.viewer.dnd.drawing.ColorsAndFonts;
+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;
+
+/**
+ * Border decorator to draw a white background and 3D style border around a text
+ * field.
+ */
+public class TextFieldBorder extends AbstractBorder {
+
+ public TextFieldBorder(final View view) {
+ super(view);
+ top = bottom = left = right = 2;
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ final int height = getSize().getHeight() - 2;
+ final int width = getSize().getWidth();
+ canvas.drawSolidRectangle(0, 1, width - 1, height - 2, Toolkit.getColor(ColorsAndFonts.COLOR_WHITE));
+ canvas.drawRectangle(0, 1, width - 3, height - 2, Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY1));
+ canvas.drawRectangle(1, 2, width - 1, height - 2, Toolkit.getColor(ColorsAndFonts.COLOR_WHITE));
+
+ 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/field/TextFieldSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextFieldSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextFieldSpecification.java
new file mode 100644
index 0000000..ee38309
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextFieldSpecification.java
@@ -0,0 +1,59 @@
+/*
+ * 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.field;
+
+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.base.AbstractFieldSpecification;
+import org.apache.isis.viewer.dnd.view.border.TextFieldResizeBorder;
+import org.apache.isis.viewer.dnd.view.content.TextParseableContent;
+import org.apache.isis.viewer.dnd.view.lookup.OpenValueDropDownBorder;
+
+/**
+ * Creates a single line text field with the base line drawn.
+ */
+public class TextFieldSpecification extends AbstractFieldSpecification {
+ @Override
+ public boolean canDisplay(final ViewRequirement requirement) {
+ return requirement.isTextParseable() && ((TextParseableContent) requirement.getContent()).getNoLines() == 1;
+ }
+
+ @Override
+ public View createView(final Content content, final Axes axes, final int sequence) {
+ final View field = new TextFieldResizeBorder(new SingleLineTextField((TextParseableContent) content, this, true));
+ if (content.isOptionEnabled()) {
+ return new OpenValueDropDownBorder(field);
+ } else {
+ return field;
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "Single Line Text Field";
+ }
+
+ @Override
+ public boolean isAligned() {
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextParseableFieldAbstract.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextParseableFieldAbstract.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextParseableFieldAbstract.java
new file mode 100644
index 0000000..bc1a1c8
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/TextParseableFieldAbstract.java
@@ -0,0 +1,106 @@
+/*
+ * 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.field;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.content.TextParseableContent;
+
+public abstract class TextParseableFieldAbstract extends AbstractField {
+ private static final Logger LOG = Logger.getLogger(TextField.class);
+
+ protected TextParseableFieldAbstract(final Content content, final ViewSpecification design) {
+ super(content, design);
+ }
+
+ @Override
+ protected boolean provideClearCopyPaste() {
+ return true;
+ }
+
+ @Override
+ protected void pasteFromClipboard() {
+ try {
+ final String text = (String) getViewManager().getClipboard(String.class);
+ final TextParseableContent content = (TextParseableContent) getContent();
+ content.parseTextEntry(text);
+ content.entryComplete();
+ LOG.debug("pasted " + text);
+ } catch (final Throwable e) {
+ LOG.error("invalid paste operation " + e);
+ }
+ }
+
+ @Override
+ protected Consent canClear() {
+ final TextParseableContent field = (TextParseableContent) getContent();
+ return field.canClear();
+ }
+
+ @Override
+ protected void clear() {
+ try {
+ final TextParseableContent content = (TextParseableContent) getContent();
+ content.parseTextEntry("");
+ content.entryComplete();
+ LOG.debug("cleared");
+ } catch (final Throwable e) {
+ LOG.error("invalid paste operation " + e);
+ }
+ }
+
+ @Override
+ protected void copyToClipboard() {
+ final TextParseableContent content = (TextParseableContent) getContent();
+ final ObjectAdapter object = content.getAdapter();
+ if (object != null) {
+ final String text = object.titleString();
+ getViewManager().setClipboard(text, String.class);
+ LOG.debug("copied " + text);
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ final TextParseableContent content = (TextParseableContent) getContent();
+ return content.isEmpty();
+ }
+
+ @Override
+ public Consent canChangeValue() {
+ final TextParseableContent cont = (TextParseableContent) getContent();
+ return cont.isEditable();
+ }
+
+ protected void saveValue(final ObjectAdapter value) {
+ parseEntry(value.titleString());
+ }
+
+ protected void parseEntry(final String entryText) {
+ final TextParseableContent content = (TextParseableContent) getContent();
+ content.parseTextEntry(entryText);
+ content.entryComplete();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/WrappedTextField.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/WrappedTextField.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/WrappedTextField.java
new file mode 100644
index 0000000..34c4fab
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/WrappedTextField.java
@@ -0,0 +1,158 @@
+/*
+ * 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.field;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.commons.exceptions.IsisException;
+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.ViewConstants;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.content.TextParseableContent;
+import org.apache.isis.viewer.dnd.view.text.CursorPosition;
+import org.apache.isis.viewer.dnd.view.text.TextContent;
+
+public class WrappedTextField extends TextField {
+ private static final Logger LOG = Logger.getLogger(WrappedTextField.class);
+
+ public WrappedTextField(final TextParseableContent content, final ViewSpecification specification, final boolean showLines) {
+ super(content, specification, showLines, TextContent.WRAPPING);
+ }
+
+ public void setWrapping(final boolean wrapping) {
+ }
+
+ @Override
+ protected void drawLines(final Canvas canvas, final Color color, final int width) {
+ int baseline = getBaseline();
+ final int noDisplayLines = textContent.getNoDisplayLines();
+ for (int line = 0; line < noDisplayLines; line++) {
+ canvas.drawLine(ViewConstants.HPADDING, baseline, ViewConstants.HPADDING + width, baseline, color);
+ baseline += getText().getLineHeight();
+ }
+ }
+
+ @Override
+ protected void drawHighlight(final Canvas canvas, final int maxWidth) {
+ final int baseline = getBaseline();
+ int top = baseline - style.getAscent();
+
+ final CursorPosition from = selection.from();
+ final CursorPosition to = selection.to();
+
+ final String[] lines = textContent.getDisplayLines();
+ final int displayFromLine = textContent.getDisplayFromLine();
+ final int displayToLine = displayFromLine + lines.length;
+ for (int i = displayFromLine; i <= displayToLine; i++) {
+ if ((i >= from.getLine()) && (i <= to.getLine())) {
+ final String line = textContent.getText(i);
+ int start = 0;
+ int end = style.stringWidth(line);
+
+ if (from.getLine() == i) {
+ final int at = Math.min(from.getCharacter(), line.length());
+ start = style.stringWidth(line.substring(0, at));
+ }
+
+ if (to.getLine() == i) {
+ final int at = Math.min(to.getCharacter(), line.length());
+ end = style.stringWidth(line.substring(0, at));
+ }
+
+ canvas.drawSolidRectangle(start + (ViewConstants.HPADDING), top, end - start, getText().getLineHeight(), Toolkit.getColor(ColorsAndFonts.COLOR_TEXT_HIGHLIGHT));
+ }
+
+ top += getText().getLineHeight();
+ }
+ }
+
+ @Override
+ protected void drawText(final Canvas canvas, final Color textColor, final int width) {
+ int baseline = getBaseline();
+ final String[] lines = textContent.getDisplayLines();
+ final int cursorLine = cursor.getLine() - textContent.getDisplayFromLine();
+ for (int i = 0; i < lines.length; i++) {
+ final String chars = lines[i];
+ if (chars == null) {
+ throw new IsisException();
+ }
+ if (chars.endsWith("\n")) {
+ throw new RuntimeException();
+ }
+
+ // draw cursor
+ if (hasFocus() && canChangeValue().isAllowed() && cursorLine == i) {
+ final int at = Math.min(cursor.getCharacter(), chars.length());
+ final int pos = style.stringWidth(chars.substring(0, at)) + ViewConstants.HPADDING;
+ canvas.drawLine(pos, (baseline + style.getDescent()), pos, baseline - style.getAscent(), Toolkit.getColor(ColorsAndFonts.COLOR_TEXT_CURSOR));
+ }
+
+ // draw text
+ canvas.drawText(chars, ViewConstants.HPADDING, baseline, textColor, style);
+ baseline += getText().getLineHeight();
+ }
+ /*
+ * if (end < entryLength) { int x = style.stringWidth(new String(buffer,
+ * start, end)); g.setColor(Color.red); g.drawString("\u00bb", x,
+ * baseline - lineHeight()); }
+ */
+ }
+
+ @Override
+ protected boolean enter() {
+ textContent.breakBlock(cursor);
+ cursor.lineDown();
+ cursor.home();
+ markDamaged();
+ return true;
+ }
+
+ /**
+ * Sets the number of lines to display
+ */
+ public void setNoLines(final int noLines) {
+ textContent.setNoDisplayLines(noLines);
+ }
+
+ @Override
+ public void setSize(final Size size) {
+ super.setSize(size);
+ textContent.setNoDisplayLines(size.getHeight() / style.getLineHeight());
+ }
+
+ @Override
+ public void setMaximumSize(final Size size) {
+ final int lines = Math.max(1, size.getHeight() / getText().getLineHeight());
+ setNoLines(lines);
+ final int width = Math.max(180, size.getWidth() - ViewConstants.HPADDING);
+ setWidth(width);
+ LOG.debug(lines + " x " + width);
+ invalidateLayout();
+ }
+
+ @Override
+ protected void align() {
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/AbstractFormSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/AbstractFormSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/AbstractFormSpecification.java
new file mode 100644
index 0000000..70cb041
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/AbstractFormSpecification.java
@@ -0,0 +1,52 @@
+/*
+ * 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.form;
+
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.ViewFactory;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.composite.StandardFields;
+
+public abstract class AbstractFormSpecification extends AbstractObjectViewSpecification {
+
+ @Override
+ protected ViewFactory createFieldFactory() {
+ return new StandardFields() {
+ @Override
+ protected int collectionRequirement() {
+ return AbstractFormSpecification.this.collectionRequirement();
+ }
+
+ @Override
+ protected boolean include(final Content content, final int sequence) {
+ return AbstractFormSpecification.this.include(content, sequence);
+ }
+ };
+ }
+
+ protected int collectionRequirement() {
+ return ViewRequirement.OPEN | ViewRequirement.SUBVIEW;
+ }
+
+ protected boolean include(final Content content, final int sequence) {
+ return true;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/AbstractObjectViewSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/AbstractObjectViewSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/AbstractObjectViewSpecification.java
new file mode 100644
index 0000000..65feca0
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/AbstractObjectViewSpecification.java
@@ -0,0 +1,55 @@
+/*
+ * 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.form;
+
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.ViewFactory;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.base.Layout;
+import org.apache.isis.viewer.dnd.view.composite.CompositeViewSpecification;
+import org.apache.isis.viewer.dnd.view.composite.ObjectFieldBuilder;
+import org.apache.isis.viewer.dnd.view.composite.StackLayout;
+import org.apache.isis.viewer.dnd.view.composite.StandardFields;
+
+public abstract class AbstractObjectViewSpecification extends CompositeViewSpecification {
+
+ public AbstractObjectViewSpecification() {
+ builder = new ObjectFieldBuilder(createFieldFactory());
+ init();
+ }
+
+ protected void init() {
+ }
+
+ protected ViewFactory createFieldFactory() {
+ return new StandardFields();
+ }
+
+ @Override
+ public Layout createLayout(final Content content, final Axes axes) {
+ return new StackLayout();
+ }
+
+ @Override
+ public boolean canDisplay(final ViewRequirement requirement) {
+ return requirement.isObject() && !requirement.isTextParseable() && requirement.hasReference() && requirement.isOpen();
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/ExpandableFormSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/ExpandableFormSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/ExpandableFormSpecification.java
new file mode 100644
index 0000000..173eb26
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/ExpandableFormSpecification.java
@@ -0,0 +1,49 @@
+/*
+ * 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.form;
+
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.border.IconBorder;
+import org.apache.isis.viewer.dnd.view.composite.FieldLabelsDecorator;
+
+public class ExpandableFormSpecification extends AbstractFormSpecification {
+
+ @Override
+ public boolean canDisplay(final ViewRequirement requirement) {
+ return super.canDisplay(requirement) && !requirement.is(ViewRequirement.SUBVIEW);
+ }
+
+ @Override
+ protected void init() {
+ addSubviewDecorator(new ExpandableViewBorder.Factory());
+ addSubviewDecorator(new FieldLabelsDecorator());
+ addViewDecorator(new IconBorder.Factory());
+ }
+
+ @Override
+ protected int collectionRequirement() {
+ return super.collectionRequirement() | ViewRequirement.EXPANDABLE;
+ }
+
+ @Override
+ public String getName() {
+ return "Expanding Form (experimental)";
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/ExpandableViewBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/ExpandableViewBorder.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/ExpandableViewBorder.java
new file mode 100644
index 0000000..1a07ff0
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/ExpandableViewBorder.java
@@ -0,0 +1,246 @@
+/*
+ * 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.form;
+
+import java.util.List;
+
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.core.commons.debug.DebugBuilder;
+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.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociationFilters;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+import org.apache.isis.viewer.dnd.drawing.ColorsAndFonts;
+import org.apache.isis.viewer.dnd.drawing.Shape;
+import org.apache.isis.viewer.dnd.icon.SubviewIconSpecification;
+import org.apache.isis.viewer.dnd.list.InternalListSpecification;
+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.ObjectContent;
+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.ViewAxis;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.base.AbstractBorder;
+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.OneToOneFieldImpl;
+
+public class ExpandableViewBorder extends AbstractBorder {
+ public static final int CAN_OPEN = 1;
+ public static final int CANT_OPEN = 2;
+ public static final int UNKNOWN = 0;
+
+ public static class Factory implements SubviewDecorator {
+ private final ViewSpecification openObjectViewSpecification;
+ private final ViewSpecification closedViewSpecification;
+ private final ViewSpecification openCollectionViewSpecification;
+
+ public Factory() {
+ this.closedViewSpecification = new SubviewIconSpecification();
+ this.openObjectViewSpecification = new InternalFormSpecification();
+ this.openCollectionViewSpecification = new InternalListSpecification();
+ }
+
+ public Factory(final ViewSpecification closedViewSpecification, final ViewSpecification openObjectViewSpecification, final ViewSpecification openCollectionViewSpecification) {
+ this.closedViewSpecification = closedViewSpecification;
+ this.openObjectViewSpecification = openObjectViewSpecification;
+ this.openCollectionViewSpecification = openCollectionViewSpecification;
+ }
+
+ @Override
+ public ViewAxis createAxis(final Content content) {
+ return null;
+ }
+
+ @Override
+ public View decorate(final Axes axes, final View view) {
+ if (view.getContent().isObject()) {
+ return new ExpandableViewBorder(view, closedViewSpecification, openObjectViewSpecification);
+ } else if (view.getContent().isCollection()) {
+ return new ExpandableViewBorder(view, closedViewSpecification, openCollectionViewSpecification);
+ } else {
+ return view;
+ }
+ }
+ }
+
+ private boolean isOpen = false;
+ private final ViewSpecification openViewSpecification;
+ private final ViewSpecification closedViewSpecification;
+ private int canOpen;
+
+ // 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 ExpandableViewBorder(final View view, final ViewSpecification closedViewSpecification, final ViewSpecification openViewSpecification) {
+ super(view);
+ left = Toolkit.defaultFieldHeight();
+ this.openViewSpecification = openViewSpecification;
+ this.closedViewSpecification = closedViewSpecification;
+ canOpen();
+ }
+
+ @Override
+ protected void debugDetails(final DebugBuilder debug) {
+ super.debugDetails(debug);
+ debug.appendln("open spec", openViewSpecification);
+ debug.appendln("closed spec", closedViewSpecification);
+ debug.appendln("open", isOpen);
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ Shape pointer;
+ if (isOpen) {
+ pointer = new Shape(0, left / 2);
+ pointer.addPoint(left - 2 - 2, left / 2);
+ pointer.addPoint(left / 2 - 2, left - 2);
+ } else {
+ pointer = new Shape(2, 2);
+ pointer.addPoint(2, left - 2);
+ pointer.addPoint(left / 2, 2 + (left - 2) / 2);
+ }
+ if (canOpen == CAN_OPEN) {
+ canvas.drawSolidShape(pointer, Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY1));
+ } else if (canOpen == UNKNOWN) {
+ canvas.drawShape(pointer, Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY1));
+ } else {
+ canvas.drawShape(pointer, Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY3));
+ }
+
+ super.draw(canvas);
+ }
+
+ @Override
+ public void firstClick(final Click click) {
+ if (click.getLocation().getX() < left) {
+ if (canOpen == UNKNOWN) {
+ resolveContent();
+ markDamaged();
+ }
+ if (canOpen != CANT_OPEN) {
+ isOpen = !isOpen;
+
+ final View parent = wrappedView.getParent();
+
+ getViewManager().removeFromNotificationList(wrappedView);
+ if (isOpen) {
+ wrappedView = createOpenView();
+ } else {
+ wrappedView = createClosedView();
+ }
+ setView(this);
+ setParent(parent);
+ getParent().invalidateLayout();
+ canOpen();
+ }
+ } else {
+ super.firstClick(click);
+ }
+ }
+
+ private View createClosedView() {
+ return closedViewSpecification.createView(getContent(), getViewAxes(), -1);
+ }
+
+ private View createOpenView() {
+ return openViewSpecification.createView(getContent(), getViewAxes(), -1);
+ }
+
+ @Override
+ public void update(final ObjectAdapter object) {
+ super.update(object);
+ canOpen();
+ }
+
+ private void canOpen() {
+ final Content content = getContent();
+ if (content.isCollection()) {
+ canOpen = canOpenCollection(content);
+ } else if (content.isObject()) {
+ canOpen = canOpenObject(content);
+ }
+ }
+
+ private int canOpenCollection(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;
+ }
+ }
+
+ private int canOpenObject(final Content content) {
+ final ObjectAdapter object = ((ObjectContent) content).getObject();
+ if (object != null) {
+ 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;
+ } else if (fields.get(i).isOneToOneAssociation() && !fields.get(i).getSpecification().isParseable() && fieldContainsReference(object, fields.get(i))) {
+ return CAN_OPEN;
+ }
+ }
+ }
+ final boolean openForObjectsWithOutReferences = true;
+ return openForObjectsWithOutReferences ? CAN_OPEN : CANT_OPEN;
+ }
+
+ private boolean fieldContainsReference(final ObjectAdapter parent, final ObjectAssociation field) {
+ final OneToOneAssociation association = (OneToOneAssociation) field;
+ final OneToOneFieldImpl fieldContent = new OneToOneFieldImpl(parent, field.get(parent), association);
+ if (openViewSpecification.canDisplay(new ViewRequirement(fieldContent, ViewRequirement.OPEN))) {
+ return true;
+ }
+ return false;
+ }
+
+ 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());
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/FormSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/FormSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/FormSpecification.java
new file mode 100644
index 0000000..b96dc58
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/FormSpecification.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.isis.viewer.dnd.form;
+
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.border.IconBorder;
+import org.apache.isis.viewer.dnd.view.composite.FieldLabelsDecorator;
+
+public class FormSpecification extends AbstractFormSpecification {
+
+ @Override
+ public boolean canDisplay(final ViewRequirement requirement) {
+ return super.canDisplay(requirement) && !requirement.is(ViewRequirement.SUBVIEW);
+ }
+
+ @Override
+ protected void init() {
+ addSubviewDecorator(new FieldLabelsDecorator());
+ addViewDecorator(new IconBorder.Factory());
+ }
+
+ @Override
+ public String getName() {
+ return "Form";
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/FormWithDetailSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/FormWithDetailSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/FormWithDetailSpecification.java
new file mode 100644
index 0000000..5cbbe92
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/FormWithDetailSpecification.java
@@ -0,0 +1,95 @@
+/*
+ * 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.form;
+
+import java.util.List;
+
+import org.apache.isis.applib.filter.Filter;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+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 FormWithDetailSpecification implements ViewSpecification {
+ private final FormSpecification leftHandSideSpecification;
+
+ public FormWithDetailSpecification() {
+ leftHandSideSpecification = new FormSpecification();
+ leftHandSideSpecification.addSubviewDecorator(new SelectObjectBorder.Factory());
+ }
+
+ @Override
+ public boolean canDisplay(final ViewRequirement requirement) {
+ return requirement.isObject() && requirement.isOpen() && !requirement.isSubview() && containsEnoughFields(requirement.getContent());
+ }
+
+ private boolean containsEnoughFields(final Content content) {
+ final ObjectSpecification specification = content.getSpecification();
+ final List<ObjectAssociation> associations = specification.getAssociations(new Filter<ObjectAssociation>() {
+ @Override
+ public boolean accept(final ObjectAssociation t) {
+ return t.isOneToManyAssociation() || (t.isOneToOneAssociation() && !((OneToOneAssociation) t).getSpecification().isParseable());
+ }
+ });
+ return associations.size() >= 1;
+ }
+
+ @Override
+ public View createView(final Content content, final Axes axes, final int sequence) {
+ return new MasterDetailPanel(content, this, leftHandSideSpecification);
+ }
+
+ @Override
+ public String getName() {
+ return "Form and details (experimental)";
+ }
+
+ @Override
+ public boolean isAligned() {
+ return false;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+ @Override
+ public boolean isReplaceable() {
+ return true;
+ }
+
+ @Override
+ public boolean isResizeable() {
+ return true;
+ }
+
+ @Override
+ public boolean isSubView() {
+ return false;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/InternalFormSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/InternalFormSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/InternalFormSpecification.java
new file mode 100644
index 0000000..05603d2
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/InternalFormSpecification.java
@@ -0,0 +1,47 @@
+/*
+ * 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.form;
+
+import org.apache.isis.viewer.dnd.drawing.ColorsAndFonts;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.border.IconBorder;
+import org.apache.isis.viewer.dnd.view.composite.FieldLabelsDecorator;
+
+public class InternalFormSpecification extends AbstractFormSpecification {
+
+ @Override
+ public boolean canDisplay(final ViewRequirement requirement) {
+ return super.canDisplay(requirement) && requirement.is(ViewRequirement.SUBVIEW);
+ }
+
+ @Override
+ protected void init() {
+ addSubviewDecorator(new FieldLabelsDecorator());
+ addViewDecorator(new IconBorder.Factory(Toolkit.getText(ColorsAndFonts.TEXT_NORMAL)));
+
+ }
+
+ @Override
+ public String getName() {
+ return "Internal form";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/SummaryFields.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/SummaryFields.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/SummaryFields.java
new file mode 100644
index 0000000..4d7bacf
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/SummaryFields.java
@@ -0,0 +1,37 @@
+/*
+ * 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.form;
+
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.composite.StandardFields;
+
+public class SummaryFields extends StandardFields {
+
+ @Override
+ protected int collectionRequirement() {
+ return ViewRequirement.NONE;
+ }
+
+ @Override
+ protected boolean include(final Content content, final int sequence) {
+ return sequence < 4 && content.getAdapter() != null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/SummaryFormSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/SummaryFormSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/SummaryFormSpecification.java
new file mode 100644
index 0000000..ac2cc27
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/form/SummaryFormSpecification.java
@@ -0,0 +1,60 @@
+/*
+ * 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.form;
+
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewFactory;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.border.BackgroundBorder;
+import org.apache.isis.viewer.dnd.view.border.EmptyBorder;
+import org.apache.isis.viewer.dnd.view.border.IconBorder;
+import org.apache.isis.viewer.dnd.view.border.LineBorder;
+import org.apache.isis.viewer.dnd.view.composite.CompositeViewDecorator;
+
+public class SummaryFormSpecification extends AbstractFormSpecification {
+
+ @Override
+ public boolean canDisplay(final ViewRequirement requirement) {
+ return requirement.isObject() && !requirement.isTextParseable() && requirement.hasReference() && requirement.isOpen() && requirement.isSubview() && requirement.isFixed();
+ }
+
+ @Override
+ protected void init() {
+ addViewDecorator(new IconBorder.Factory());
+ addViewDecorator(new CompositeViewDecorator() {
+ @Override
+ public View decorate(final View view, final Axes axes) {
+ return new EmptyBorder(3, new BackgroundBorder(new LineBorder(1, 8, new EmptyBorder(3, view))));
+ }
+ });
+ }
+
+ @Override
+ protected ViewFactory createFieldFactory() {
+ return new SummaryFields();
+ }
+
+ @Override
+ public String getName() {
+ return "Summary";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/grid/GridSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/grid/GridSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/grid/GridSpecification.java
new file mode 100644
index 0000000..c3896ac
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/grid/GridSpecification.java
@@ -0,0 +1,114 @@
+/*
+ * 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.grid;
+
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+import org.apache.isis.viewer.dnd.drawing.ColorsAndFonts;
+import org.apache.isis.viewer.dnd.form.AbstractFormSpecification;
+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.Toolkit;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewAxis;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.base.AbstractBorder;
+import org.apache.isis.viewer.dnd.view.base.Layout;
+import org.apache.isis.viewer.dnd.view.border.EmptyBorder;
+import org.apache.isis.viewer.dnd.view.composite.CollectionElementBuilder;
+import org.apache.isis.viewer.dnd.view.composite.ColumnLayout;
+import org.apache.isis.viewer.dnd.view.composite.CompositeViewDecorator;
+import org.apache.isis.viewer.dnd.view.composite.CompositeViewSpecification;
+import org.apache.isis.viewer.dnd.view.content.FieldContent;
+
+public class GridSpecification extends CompositeViewSpecification {
+
+ public GridSpecification() {
+ builder = new CollectionElementBuilder(new AbstractFormSpecification() {
+ @Override
+ public String getName() {
+ return "";
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+ addSubviewDecorator(new SubviewDecorator() {
+ @Override
+ public ViewAxis createAxis(final Content content) {
+ return null;
+ }
+
+ @Override
+ public View decorate(final Axes axes, final View view) {
+ return new EmptyBorder(0, 0, 5, 0, view);
+ }
+ });
+ }
+ });
+
+ addViewDecorator(new ColumnLabelBorder.Factory());
+ }
+
+ @Override
+ public Layout createLayout(final Content content, final Axes axes) {
+ return new ColumnLayout(true);
+ }
+
+ @Override
+ public String getName() {
+ return "Grid (experimental)";
+ }
+
+ @Override
+ public boolean canDisplay(final ViewRequirement requirement) {
+ return requirement.isCollection() && requirement.isOpen();
+ }
+}
+
+class ColumnLabelBorder extends AbstractBorder {
+
+ public static class Factory implements CompositeViewDecorator {
+ @Override
+ public View decorate(final View view, final Axes axes) {
+ return new ColumnLabelBorder(view);
+ }
+ }
+
+ protected ColumnLabelBorder(final View view) {
+ super(view);
+ left = 100;
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ final View subview = getSubviews()[0];
+
+ final int top = subview.getPadding().getTop();
+ for (final View view : subview.getSubviews()) {
+ final String fieldName = ((FieldContent) view.getContent()).getFieldName();
+ canvas.drawText(fieldName + ":", 0, view.getLocation().getY() + top + view.getBaseline(), Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY1), Toolkit.getText(ColorsAndFonts.TEXT_LABEL));
+ // canvas.drawRectangle(0, view.getLocation().getY() + top, 80, 10,
+ // Toolkit.getColor("primary1"));
+ }
+
+ super.draw(canvas);
+ }
+}