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:02 UTC
[28/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/option/UserActionAbstract.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/option/UserActionAbstract.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/option/UserActionAbstract.java
new file mode 100644
index 0000000..bc79070
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/option/UserActionAbstract.java
@@ -0,0 +1,96 @@
+/*
+ * 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.option;
+
+import org.apache.isis.core.commons.lang.ToString;
+import org.apache.isis.core.metamodel.consent.Allow;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.spec.ActionType;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.view.UserAction;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.Workspace;
+
+/**
+ * Each option that a user is shown in an objects popup menu a MenuOption. A
+ * MenuOption details: the name of an option (in the users language);
+ * <ul>
+ * the type of object that might result when requesting this option
+ * </ul>
+ * ; a way to determine whether a user can select this option on the current
+ * object.
+ */
+public abstract class UserActionAbstract implements UserAction {
+ private String description;
+ private String name;
+ private final ActionType type;
+
+ public UserActionAbstract(final String name) {
+ this(name, ActionType.USER);
+ }
+
+ public UserActionAbstract(final String name, final ActionType type) {
+ this.name = name;
+ this.type = type;
+ }
+
+ @Override
+ public Consent disabled(final View view) {
+ return Allow.DEFAULT;
+ }
+
+ @Override
+ public abstract void execute(final Workspace workspace, final View view, final Location at);
+
+ @Override
+ public String getDescription(final View view) {
+ return description;
+ }
+
+ @Override
+ public String getHelp(final View view) {
+ return "No help available for user action";
+ }
+
+ /**
+ * Returns the stored name of the menu option.
+ */
+ @Override
+ public String getName(final View view) {
+ return name;
+ }
+
+ @Override
+ public ActionType getType() {
+ return type;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ final ToString str = new ToString(this);
+ str.append("name", name);
+ str.append("type", type);
+ return str.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/CursorPosition.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/CursorPosition.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/CursorPosition.java
new file mode 100644
index 0000000..d5d9d2a
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/CursorPosition.java
@@ -0,0 +1,261 @@
+/*
+ * 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.text;
+
+import org.apache.isis.viewer.dnd.drawing.Location;
+
+/**
+ * Represents the position of a line cursor within a TextContent. The character
+ * position for a line extends from zero to text.length + 1. Where 0 means the
+ * cursor is to the left of the first character, and 1 is to right of the first
+ * character and to the left of the second character.
+ */
+public class CursorPosition {
+ private int character;
+ private int line;
+ private final TextContent textContent;
+
+ public CursorPosition(final TextContent content, final CursorPosition pos) {
+ this(content, pos.line, pos.character);
+ }
+
+ public CursorPosition(final TextContent content, final int line, final int afterCharacter) {
+ this.textContent = content;
+ this.line = line;
+ this.character = afterCharacter;
+ }
+
+ public void asFor(final CursorPosition pos) {
+ line = pos.line;
+ character = pos.character;
+ }
+
+ /**
+ * Move the cursor to the bottom-right of the field
+ */
+ public void bottom() {
+ line = textContent.getNoLinesOfContent() - 1;
+ textContent.alignDisplay(line);
+ end();
+ }
+
+ public void cursorAt(final Location atLocation) {
+ line = textContent.cursorAtLine(atLocation);
+ character = textContent.cursorAtCharacter(atLocation, line);
+
+ if (line >= textContent.getNoLinesOfContent()) {
+ line = textContent.getNoLinesOfContent() - 1;
+ end();
+ }
+ }
+
+ /**
+ * Move the cursor to the end of the line
+ */
+ public void end() {
+ final String text = textContent.getText(line);
+ character = text == null ? 0 : text.length();
+ }
+
+ /**
+ * @return the character within this line.
+ */
+ public int getCharacter() {
+ return character;
+ }
+
+ /**
+ * @return the line within the field
+ */
+ public int getLine() {
+ return line;
+ }
+
+ /**
+ * Move the cursor to the left end of the field
+ */
+ public void home() {
+ character = 0;
+ }
+
+ /**
+ * Movet the cursor left by one character.
+ */
+ public void left() {
+ if (!((line == 0) && (character == 0))) {
+ character--;
+
+ if (character < 0) {
+ line--;
+ textContent.alignDisplay(line);
+ end();
+ }
+ }
+ }
+
+ /**
+ * Move down one line.
+ */
+ public void lineDown() {
+ moveDown(1);
+ }
+
+ /**
+ * Move up one line.
+ */
+ public void lineUp() {
+ moveUp(1);
+ }
+
+ private void moveDown(final int byLines) {
+ final int size = textContent.getNoLinesOfContent();
+
+ if (line < (size - 1)) {
+ line += byLines;
+ line = Math.min(size - 1, line);
+
+ character = Math.min(character, textContent.getText(line).length());
+
+ textContent.alignDisplay(line);
+ }
+ }
+
+ private void moveUp(final int byLines) {
+ if (line > 0) {
+ line -= byLines;
+ line = Math.max(0, line);
+ textContent.alignDisplay(line);
+ }
+ }
+
+ /**
+ * Move down one page.
+ */
+ public void pageDown() {
+ moveDown(textContent.getNoDisplayLines() - 1);
+ }
+
+ /**
+ * Move cursor up by a page
+ */
+ public void pageUp() {
+ moveUp(textContent.getNoDisplayLines() - 1);
+ }
+
+ /**
+ * Move the cursor right by one character.
+ */
+ public void right() {
+ right(1);
+ }
+
+ /**
+ * Move the cursor right by one character.
+ */
+ public void right(final int characters) {
+ final int length = textContent.getText(line).length();
+
+ if ((character + characters) > length) {
+ if ((line + 1) < textContent.getNoLinesOfContent()) {
+ line++;
+ textContent.alignDisplay(line);
+
+ final int remainder = (character + characters) - length;
+ character = 0;
+ right(remainder);
+ }
+ } else {
+ character += characters;
+ }
+ }
+
+ /**
+ * Move the cursor to the top-left of the field
+ */
+ public void top() {
+ line = 0;
+ character = 0;
+ textContent.alignDisplay(line);
+ }
+
+ @Override
+ public String toString() {
+ return "CursorPosition [line=" + line + ",character=" + character + "]";
+ }
+
+ /**
+ * Move the cursor left to the beginning of the previous word.
+ */
+ public void wordLeft() {
+ if (!((line == 0) && (character == 0))) {
+ if (character == 0) {
+ line--;
+ end();
+ }
+
+ final String text = textContent.getText(line);
+
+ do {
+ character--;
+ } while ((character >= 0) && (text.charAt(character) == ' '));
+
+ while ((character >= 0) && (text.charAt(character) != ' ')) {
+ character--;
+ }
+
+ character++;
+ }
+ }
+
+ /**
+ * Move the cursor right to the end of the current word.
+ */
+ public void wordRight() {
+ final String text = textContent.getText(line);
+ final int lineLength = text.length();
+ if (!(line == textContent.getNoLinesOfContent() - 1 && character == lineLength - 1)) {
+ // skip spaces before
+ while (character < lineLength && text.charAt(character) == ' ') {
+ character++;
+ }
+ // skip characters (until next space)
+ while (character < lineLength && text.charAt(character) != ' ') {
+ character++;
+ }
+ // skip spaces after word
+ while (character < lineLength && text.charAt(character) == ' ') {
+ character++;
+ }
+ // wrap to nexrt line if at end
+ if (character >= lineLength && line + 1 < textContent.getNoLinesOfContent()) {
+ line++;
+ character = 0;
+ }
+ }
+ }
+
+ public boolean samePosition(final CursorPosition positionToCompare) {
+ return line == positionToCompare.line && character == positionToCompare.character;
+ }
+
+ public boolean isBefore(final CursorPosition positionToCompare) {
+ return line < positionToCompare.line || (line == positionToCompare.line && character < positionToCompare.character);
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/ObjectTitleText.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/ObjectTitleText.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/ObjectTitleText.java
new file mode 100644
index 0000000..1bb723c
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/ObjectTitleText.java
@@ -0,0 +1,41 @@
+/*
+ * 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.text;
+
+import org.apache.isis.viewer.dnd.drawing.ColorsAndFonts;
+import org.apache.isis.viewer.dnd.drawing.Text;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.View;
+
+public class ObjectTitleText extends TitleText {
+ private final Content content;
+
+ public ObjectTitleText(final View view, final Text style) {
+ super(view, style, Toolkit.getColor(ColorsAndFonts.COLOR_BLACK));
+ content = view.getContent();
+ }
+
+ @Override
+ protected String title() {
+ return content.title();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextBlock.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextBlock.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextBlock.java
new file mode 100644
index 0000000..5747882
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextBlock.java
@@ -0,0 +1,223 @@
+/*
+ * 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.text;
+
+import org.apache.log4j.Logger;
+
+class TextBlock {
+ private static final Logger LOG = Logger.getLogger(TextBlock.class);
+ private static final Logger UI_LOG = Logger.getLogger("ui." + TextBlock.class.getName());
+ private final TextBlockTarget forField;
+ private String text;
+ private int[] lineBreaks;
+ private boolean isFormatted;
+ private int lineCount;
+ private boolean canWrap;
+
+ TextBlock(final TextBlockTarget forField, final String text, final boolean canWrap) {
+ this.forField = forField;
+ this.text = text;
+ isFormatted = false;
+ this.canWrap = canWrap;
+ }
+
+ public String getLine(final int line) {
+ if (line < 0 || line > lineCount) {
+ throw new IllegalArgumentException("line outside of block " + line);
+ }
+
+ format();
+
+ final int from = lineStart(line);
+ final int to = lineEnd(line);
+
+ return text.substring(from, to);
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void deleteLeft(final int line, final int character) {
+ final int pos = pos(line, character);
+ if (pos > 0) {
+ text = text.substring(0, pos - 1) + text.substring(pos);
+ isFormatted = false;
+ }
+ }
+
+ public void delete(final int fromLine, final int fromCharacter, final int toLine, final int toCharacter) {
+ format();
+ final int from = pos(fromLine, fromCharacter);
+ final int to = pos(toLine, toCharacter);
+ text = text.substring(0, from) + text.substring(to);
+ isFormatted = false;
+ }
+
+ public void deleteTo(final int toLine, final int toCharacter) {
+ format();
+ final int from = 0;
+ final int to = pos(toLine, toCharacter);
+ text = text.substring(0, from) + text.substring(to);
+ isFormatted = false;
+ }
+
+ public void deleteFrom(final int fromLine, final int fromCharacter) {
+ format();
+ final int from = pos(fromLine, fromCharacter);
+ final int to = text.length();
+ text = text.substring(0, from) + text.substring(to);
+ isFormatted = false;
+ }
+
+ public void deleteRight(final int line, final int character) {
+ final int pos = pos(line, character);
+ if (pos < text.length()) {
+ text = text.substring(0, pos) + text.substring(pos + 1);
+ isFormatted = false;
+ }
+ }
+
+ public int noLines() {
+ format();
+ return lineCount + 1;
+ }
+
+ private void breakAt(final int breakAt) {
+ // TODO deal with growing array
+ lineBreaks[lineCount] = breakAt;
+ lineCount++;
+ }
+
+ private void format() {
+ if (canWrap && !isFormatted) {
+ lineBreaks = new int[100];
+ lineCount = 0;
+
+ final int length = text.length();
+
+ int lineWidth = 0;
+ int breakAt = -1;
+
+ for (int pos = 0; pos < length; pos++) {
+ final char ch = text.charAt(pos);
+
+ if (ch == '\n') {
+ throw new IllegalStateException("Block must not contain newline characters");
+ }
+
+ lineWidth += forField.getText().charWidth(ch);
+
+ if (lineWidth > forField.getMaxFieldWidth()) {
+ breakAt = (breakAt == -1) ? pos - 1 : breakAt;
+ // ensures that a string without spaces doesn't loop forever
+ breakAt(breakAt);
+
+ // include the remaining chars in the starting width.
+ lineWidth = forField.getText().stringWidth(text.substring(breakAt - 1, pos + 1));
+
+ // reset for next line
+ // start = breakAt;
+ breakAt = -1;
+
+ continue;
+ }
+
+ if (ch == ' ') {
+ breakAt = pos + 1; // break at the character after the space
+ }
+ }
+
+ isFormatted = true;
+ }
+ }
+
+ public void insert(final int line, final int character, final String characters) {
+ if (characters.indexOf('\n') >= 0) {
+ throw new IllegalArgumentException("Insert characters cannot contain newline");
+ }
+ final int pos = pos(line, character);
+ text = text.substring(0, pos) + characters + text.substring(pos);
+ isFormatted = false;
+ }
+
+ private int pos(final int line, final int character) {
+ int pos = lineStart(line);
+ pos += character;
+ LOG.debug("position " + pos);
+ return pos;
+ }
+
+ private int lineStart(final int line) {
+ final int pos = line == 0 ? 0 : lineBreaks[line - 1];
+ UI_LOG.debug("line " + line + " starts at " + pos);
+ return pos;
+ }
+
+ private int lineEnd(final int line) {
+ final int pos = line >= lineCount ? text.length() : lineBreaks[line];
+ UI_LOG.debug("line " + line + " ends at " + pos);
+ return pos;
+ }
+
+ /**
+ * breaks a block at the cursor position by truncating this block and
+ * creating a new block and adding the removed text.
+ */
+ public TextBlock splitAt(final int line, final int character) {
+ format();
+ final int pos = pos(line, character);
+ final TextBlock newBlock = new TextBlock(forField, text.substring(pos), canWrap);
+ text = text.substring(0, pos);
+ isFormatted = false;
+ return newBlock;
+ }
+
+ public void setCanWrap(final boolean canWrap) {
+ this.canWrap = canWrap;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer content = new StringBuffer();
+ content.append("TextBlock [");
+ content.append("formatted=");
+ content.append(isFormatted);
+ content.append(",lines=");
+ content.append(lineCount);
+ content.append(",text=");
+ content.append(text);
+ content.append(",breaks=");
+ if (lineBreaks == null) {
+ content.append("none");
+ } else {
+ for (int i = 0; i < lineBreaks.length; i++) {
+ content.append(i == 0 ? "" : ",");
+ content.append(lineBreaks[i]);
+ }
+ }
+ content.append("]");
+ return content.toString();
+ }
+
+ public void join(final TextBlock textBlock) {
+ text += textBlock.text;
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextBlockTarget.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextBlockTarget.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextBlockTarget.java
new file mode 100644
index 0000000..309e5c7
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextBlockTarget.java
@@ -0,0 +1,32 @@
+/*
+ * 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.text;
+
+import org.apache.isis.viewer.dnd.drawing.Text;
+
+public interface TextBlockTarget {
+
+ /**
+ * Maximum text entry width.
+ */
+ int getMaxFieldWidth();
+
+ Text getText();
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextContent.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextContent.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextContent.java
new file mode 100644
index 0000000..97bf245
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextContent.java
@@ -0,0 +1,372 @@
+/*
+ * 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.text;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.commons.ensure.Assert;
+import org.apache.isis.core.commons.exceptions.IsisException;
+import org.apache.isis.core.commons.lang.ToString;
+import org.apache.isis.viewer.dnd.drawing.Location;
+
+public class TextContent {
+ private static final Logger LOG = Logger.getLogger(TextContent.class);
+ private static final Logger UI_LOG = Logger.getLogger("ui." + TextContent.class.getName());
+ public static final int NO_WRAPPING = 1;
+ public static final int WRAPPING = 0;
+ private final Vector blocks;
+ private final TextBlockTarget target;
+ private int displayFromLine;
+ private int availableDisplayLines;
+ private final boolean useEmptyLines;
+ private final int wrap;
+
+ public TextContent(final TextBlockTarget target, final int noLines, final int wrapStyle) {
+ this(target, noLines, wrapStyle, false);
+ }
+
+ public TextContent(final TextBlockTarget target, final int noLines, final int wrapStyle, final boolean useEmptyLines) {
+ this.target = target;
+ this.wrap = wrapStyle;
+ this.blocks = new Vector();
+ this.useEmptyLines = useEmptyLines;
+ availableDisplayLines = noLines;
+ displayFromLine = 0;
+ addBlock("");
+ alignDisplay(0);
+ }
+
+ private void addBlock(final String text) {
+ final TextBlock block = new TextBlock(target, text, wrap == TextContent.WRAPPING);
+ LOG.debug("add block " + block);
+ blocks.addElement(block);
+ }
+
+ /**
+ * Returns the number of lines that this field will display the content.
+ * This can be smaller than the actual number of lines of content, but will
+ * be at least one.
+ */
+ public int getNoDisplayLines() {
+ return availableDisplayLines;
+ }
+
+ /**
+ * Aligns the lines of content so that the specified line is within the
+ * array of lines returned by getDisplayLines().
+ *
+ * @see #getDisplayLines()
+ */
+ public void alignDisplay(final int line) {
+ final int noContentLines = getNoLinesOfContent();
+ final int lastLine = noContentLines - 1;
+
+ int displayToLine = Math.min(displayFromLine + availableDisplayLines, noContentLines);
+ if (noContentLines <= availableDisplayLines) {
+ displayFromLine = 0;
+ } else {
+ if (line >= displayToLine) {
+ displayToLine = line + 3;
+ displayToLine = Math.min(displayToLine, lastLine);
+
+ displayFromLine = displayToLine - availableDisplayLines + 1;
+ displayFromLine = Math.max(displayFromLine, 0);
+ }
+
+ if (line < displayFromLine) {
+ displayFromLine = line;
+ displayToLine = (displayFromLine + availableDisplayLines) - 1;
+
+ if (displayToLine >= noContentLines) {
+ displayToLine = lastLine;
+ displayFromLine = Math.max(0, displayToLine - availableDisplayLines);
+ }
+ }
+ }
+
+ LOG.debug("display line " + line + " " + displayFromLine + "~" + displayToLine);
+ }
+
+ public void breakBlock(final CursorPosition cursorAt) {
+ final BlockToLineMapping mapping = findBlockFor(cursorAt.getLine());
+ final TextBlock newBlock = mapping.textBlock.splitAt(mapping.line, cursorAt.getCharacter());
+ blocks.insertElementAt(newBlock, mapping.index + 1);
+ }
+
+ /**
+ * deletes the selected text
+ */
+ public void delete(final TextSelection selection) {
+ final CursorPosition from = selection.from();
+ final CursorPosition to = selection.to();
+
+ final BlockToLineMapping fromMapping = findBlockFor(from.getLine());
+ final int fromBlock = fromMapping.index;
+ final int fromLine = fromMapping.line;
+ final int fromCharacter = from.getCharacter();
+
+ final BlockToLineMapping toMapping = findBlockFor(to.getLine());
+ final int toBlock = toMapping.index;
+ final int toLine = toMapping.line;
+ final int toCharacter = to.getCharacter();
+
+ if (fromBlock == toBlock) {
+ final TextBlock block = (TextBlock) blocks.elementAt(fromBlock);
+ block.delete(fromLine, fromCharacter, toLine, toCharacter);
+ } else {
+ TextBlock block = (TextBlock) blocks.elementAt(toBlock);
+ block.deleteTo(toLine, toCharacter);
+
+ block = (TextBlock) blocks.elementAt(fromBlock);
+ block.deleteFrom(fromLine, fromCharacter);
+
+ fromMapping.textBlock.join(toMapping.textBlock);
+ blocks.removeElementAt(toMapping.index);
+
+ for (int i = fromBlock + 1; i < toBlock; i++) {
+ blocks.removeElementAt(i);
+ }
+ }
+ }
+
+ public void deleteLeft(final CursorPosition cursorAt) {
+ final BlockToLineMapping mapping = findBlockFor(cursorAt.getLine());
+ if (mapping == null || mapping.textBlock == null) {
+ throw new IsisException("invalid block " + mapping + " for line " + cursorAt.getLine());
+ }
+ mapping.textBlock.deleteLeft(mapping.line, cursorAt.getCharacter());
+ }
+
+ public void deleteRight(final CursorPosition cursorAt) {
+ final BlockToLineMapping mapping = findBlockFor(cursorAt.getLine());
+ mapping.textBlock.deleteRight(mapping.line, cursorAt.getCharacter());
+ }
+
+ private BlockToLineMapping findBlockFor(final int line) {
+ if (line < 0) {
+ throw new IllegalArgumentException("Line must be greater than, or equal to, zero: " + line);
+ }
+
+ int lineWithinBlock = line;
+ for (int i = 0; i < blocks.size(); i++) {
+ final TextBlock block = (TextBlock) blocks.elementAt(i);
+ final int noLines = block.noLines();
+ if (lineWithinBlock < noLines) {
+ UI_LOG.debug("block " + i + ", line " + lineWithinBlock);
+ return new BlockToLineMapping(i, block, lineWithinBlock);
+ }
+ lineWithinBlock -= noLines;
+ }
+ return null;
+ // throw new IllegalArgumentException("line number not valid " + line);
+
+ }
+
+ /**
+ * returns the entire text of the content, with a newline between each block
+ * (but not after the final block.
+ */
+ public String getText() {
+ final StringBuffer content = new StringBuffer();
+ final Enumeration e = blocks.elements();
+ while (e.hasMoreElements()) {
+ final TextBlock block = (TextBlock) e.nextElement();
+ if (content.length() > 0) {
+ content.append("\n");
+ }
+ content.append(block.getText());
+ }
+ return content.toString();
+ }
+
+ /**
+ * returns the text on the specified line
+ */
+ public String getText(final int forLine) {
+ final BlockToLineMapping block = findBlockFor(forLine);
+ if (block == null) {
+ return null;
+ }
+ return block.textBlock.getLine(block.line);
+ }
+
+ /**
+ * returns only the text that is selected
+ */
+ public String getText(final TextSelection selection) {
+ final CursorPosition from = selection.from();
+ final CursorPosition to = selection.to();
+
+ final int line = from.getLine();
+ String text = getText(line);
+ if (from.getLine() == to.getLine()) {
+ return text.substring(from.getCharacter(), to.getCharacter());
+
+ } else {
+ final StringBuffer str = new StringBuffer();
+ str.append(text.substring(from.getCharacter()));
+ for (int i = line + 1; i < line + (to.getLine() - from.getLine()); i++) {
+ text = getText(i);
+ str.append(text);
+ }
+ text = getText(line + (to.getLine() - from.getLine()));
+ str.append(text.substring(0, to.getCharacter()));
+ return str.toString();
+ }
+ }
+
+ public void insert(final CursorPosition cursorAt, final String characters) {
+ Assert.assertNotNull(cursorAt);
+
+ final BlockToLineMapping block = findBlockFor(cursorAt.getLine());
+
+ Assert.assertNotNull("failed to get block for line " + cursorAt.getLine(), block);
+
+ block.textBlock.insert(block.line, cursorAt.getCharacter(), characters);
+ }
+
+ /**
+ * Returns the number of lines required to display the content text in it
+ * entirety.
+ */
+ public int getNoLinesOfContent() {
+ int lineCount = 0;
+ final Enumeration e = blocks.elements();
+ while (e.hasMoreElements()) {
+ lineCount += ((TextBlock) e.nextElement()).noLines();
+ }
+ return lineCount;
+ }
+
+ public void setText(final String text) {
+ blocks.removeAllElements();
+
+ if (text == null || text.equals("")) {
+ addBlock("");
+ } else {
+ final String[] tokens = text.split("\\n");
+ for (final String token : tokens) {
+ if (useEmptyLines || token.length() > 0) {
+ addBlock(token);
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ final ToString content = new ToString(this);
+ content.append("field", target);
+ content.append("lines", availableDisplayLines);
+ content.append("blocks=", blocks.size());
+ /*
+ * for (int i = 0; i < blocks.size(); i++) { content.append(i == 0 ? " "
+ * : "\n "); content.append(blocks.elementAt(i)); }
+ */
+ return content.toString();
+ }
+
+ public String[] getDisplayLines() {
+ final String[] lines = new String[availableDisplayLines];
+ for (int i = 0, j = displayFromLine; i < lines.length; i++, j++) {
+ final String line = getText(j);
+ lines[i] = line == null ? "" : line;
+ }
+ return lines;
+ }
+
+ public int getDisplayFromLine() {
+ return displayFromLine;
+ }
+
+ public void setNoDisplayLines(final int noDisplayLines) {
+ this.availableDisplayLines = noDisplayLines;
+ }
+
+ public void increaseDepth() {
+ availableDisplayLines++;
+ }
+
+ public boolean decreaseDepth() {
+ if (availableDisplayLines > 1) {
+ availableDisplayLines--;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private static class BlockToLineMapping {
+ TextBlock textBlock;
+ int index;
+ int line;
+
+ public BlockToLineMapping(final int blockIndex, final TextBlock block, final int line) {
+ this.index = blockIndex;
+ this.textBlock = block;
+ this.line = line;
+ }
+ }
+
+ int cursorAtLine(final Location atLocation) {
+ LOG.debug("pointer at " + atLocation);
+ final int y = atLocation.getY();
+ int lineIndex = displayFromLine + (y / target.getText().getLineHeight());
+ lineIndex = Math.max(lineIndex, 0);
+ return lineIndex;
+ }
+
+ int cursorAtCharacter(final Location atLocation, final int lineOffset) {
+ final String text = getText(lineOffset);
+ if (text == null) {
+ for (int i = lineOffset; i >= 0; i--) {
+ final String text2 = getText(i);
+ if (text2 != null) {
+ final int at = text2.length();
+ LOG.debug("character at " + at + " line " + lineOffset);
+ return at;
+ }
+ }
+ }
+
+ /*
+ * slightly offsetting mouse helps the user position the cursor between
+ * characters near the pointer rather than always after the pointer
+ */
+ final int x = atLocation.getX() - 3;
+
+ int at = 0;
+ final int endAt = text.length();
+
+ int width = 0;
+
+ while (at < endAt && x > width) {
+ width += target.getText().charWidth(text.charAt(at));
+ at++;
+ }
+
+ LOG.debug("character at " + at + " line " + lineOffset);
+ return at;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextSelection.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextSelection.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextSelection.java
new file mode 100644
index 0000000..ff78e7b
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextSelection.java
@@ -0,0 +1,99 @@
+/*
+ * 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.text;
+
+import org.apache.isis.viewer.dnd.drawing.Location;
+
+public class TextSelection {
+ private final CursorPosition cursor;
+ private final CursorPosition start;
+
+ public TextSelection(final TextContent content) {
+ this.cursor = new CursorPosition(content, 0, 0);
+ this.start = new CursorPosition(content, 0, 0);
+ }
+
+ /**
+ * Determine if the selection is back to front. Returns true if the cursor
+ * position is before the start position.
+ */
+ private boolean backwardSelection() {
+ return cursor.isBefore(start);
+ }
+
+ public void extendTo(final CursorPosition pos) {
+ cursor.asFor(pos);
+ }
+
+ /**
+ * extends the selection so the end point is the same as the cursor.
+ */
+ public void extendTo(final Location at) {
+ cursor.cursorAt(at);
+ }
+
+ public CursorPosition from() {
+ return backwardSelection() ? cursor : start;
+ }
+
+ // private CursorPosition end = new CursorPosition(0,0);
+
+ /**
+ * returns true is a selection exists - if the start and end locations are
+ * not the same
+ */
+ public boolean hasSelection() {
+ return !cursor.samePosition(start);
+ }
+
+ /**
+ * clears the selection so nothing is selected. The start and end points are
+ * set to the same values as the cursor.
+ */
+ public void resetTo(final CursorPosition pos) {
+ start.asFor(pos);
+ cursor.asFor(pos);
+ }
+
+ public void selectSentence() {
+ resetTo(cursor);
+ start.home();
+ cursor.end();
+ }
+
+ /**
+ * set the selection to be for the word marked by the current cursor
+ *
+ */
+ public void selectWord() {
+ resetTo(cursor);
+ start.wordLeft();
+ cursor.wordRight();
+ }
+
+ public CursorPosition to() {
+ return backwardSelection() ? start : cursor;
+ }
+
+ @Override
+ public String toString() {
+ return "Selection [from=" + start.getLine() + ":" + start.getCharacter() + ",to=" + cursor.getLine() + ":" + cursor.getCharacter() + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextUtils.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextUtils.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextUtils.java
new file mode 100644
index 0000000..be45cfd
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TextUtils.java
@@ -0,0 +1,63 @@
+/*
+ * 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.text;
+
+import org.apache.isis.viewer.dnd.drawing.Text;
+
+public class TextUtils {
+
+ private TextUtils() {
+ }
+
+ public static String limitText(final String xtext, final Text style, final int maxWidth) {
+ String text = xtext;
+ final int ellipsisWidth = style.stringWidth("...");
+ if (maxWidth > 0 && style.stringWidth(text) > maxWidth) {
+ int lastCharacterWithinAllowedWidth = 0;
+ for (int textWidth = ellipsisWidth; textWidth <= maxWidth;) {
+ final char character = text.charAt(lastCharacterWithinAllowedWidth);
+ textWidth += style.charWidth(character);
+ lastCharacterWithinAllowedWidth++;
+ }
+
+ int space = text.lastIndexOf(' ', lastCharacterWithinAllowedWidth - 1);
+ if (space > 0) {
+ while (space >= 0) {
+ final char character = text.charAt(space - 1);
+ if (Character.isLetterOrDigit(character)) {
+ break;
+ }
+ space--;
+ }
+
+ text = text.substring(0, space);
+ } else {
+ if (lastCharacterWithinAllowedWidth > 0) {
+ text = text.substring(0, lastCharacterWithinAllowedWidth - 1);
+ } else {
+ text = "";
+ }
+ }
+ text += "...";
+ }
+ return text;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TitleText.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TitleText.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TitleText.java
new file mode 100644
index 0000000..6f414a3
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/text/TitleText.java
@@ -0,0 +1,125 @@
+/*
+ * 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.text;
+
+import org.apache.isis.core.commons.lang.ToString;
+import org.apache.isis.core.metamodel.adapter.ResolveException;
+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.Size;
+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.ViewState;
+
+/**
+ * TitleText draws the text derived from the subclass within a view. The text is
+ * properly truncated if longer than the specified maximum width.
+ */
+public abstract class TitleText {
+ private static final int NO_MAX_WIDTH = -1;
+ private final Color color;
+ private final Text style;
+ private final View view;
+ private boolean resolveFailure;
+
+ public TitleText(final View view, final Text style, final Color color) {
+ this.view = view;
+ this.style = style;
+ this.color = color;
+ }
+
+ /**
+ * Draw this TitleText's text stating from the specified x coordination and
+ * on the specified baseline.
+ */
+ public void draw(final Canvas canvas, final int x, final int baseline) {
+ draw(canvas, x, baseline, NO_MAX_WIDTH);
+ }
+
+ /**
+ * Draw this TitleText's text stating from the specified x coordination and
+ * on the specified baseline. If a maximum width is specified (ie it is
+ * positive) then the text drawn will not extend past that width.
+ *
+ * @param maxWidth
+ * the maximum width to display the text within; if negative no
+ * limit is imposed
+ */
+ public void draw(final Canvas canvas, final int x, final int baseline, final int maxWidth) {
+ Color color;
+ final ViewState state = view.getState();
+ if (resolveFailure) {
+ color = Toolkit.getColor(ColorsAndFonts.COLOR_ERROR);
+ } else if (state.canDrop()) {
+ color = Toolkit.getColor(ColorsAndFonts.COLOR_VALID);
+ } else if (state.cantDrop()) {
+ color = Toolkit.getColor(ColorsAndFonts.COLOR_INVALID);
+ } else if (state.isObjectIdentified()) {
+ color = Toolkit.getColor(ColorsAndFonts.COLOR_IDENTIFIED);
+ } else {
+ color = this.color;
+ }
+
+ final String text = TextUtils.limitText(getTitle(), style, maxWidth);
+
+ final int xt = x;
+ final int yt = baseline;
+
+ if (Toolkit.debug) {
+ final int x2 = style.stringWidth(text);
+ canvas.drawDebugOutline(new Bounds(xt, yt - style.getAscent(), x2, style.getTextHeight()), baseline, Toolkit.getColor(ColorsAndFonts.COLOR_DEBUG_BOUNDS_DRAW));
+ }
+ canvas.drawText(text, xt, yt, color, style);
+ }
+
+ public Size getSize() {
+ final int height = style.getTextHeight();
+ final int width = style.stringWidth(getTitle());
+ return new Size(width, height);
+ }
+
+ private String getTitle() {
+ if (resolveFailure) {
+ return "Resolve Failure!";
+ }
+
+ String title;
+ try {
+ title = title();
+ } catch (final ResolveException e) {
+ resolveFailure = true;
+ title = "Resolve Failure!";
+ }
+ return title;
+ }
+
+ protected abstract String title();
+
+ @Override
+ public String toString() {
+ final ToString str = new ToString(this);
+ str.append("style", style);
+ str.append("color", color);
+ return str.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/undo/AssociateCommand.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/undo/AssociateCommand.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/undo/AssociateCommand.java
new file mode 100644
index 0000000..f1f163c
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/undo/AssociateCommand.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.view.undo;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.viewer.dnd.view.Command;
+
+public class AssociateCommand implements Command {
+ private final String description;
+ private final OneToOneAssociation field;
+ private final ObjectAdapter object;
+ private final ObjectAdapter associatedObject;
+ private final String name;
+
+ public AssociateCommand(final ObjectAdapter object, final ObjectAdapter associatedObject, final OneToOneAssociation field) {
+ this.description = "Clear association of " + associatedObject.titleString();
+ this.name = "associate " + associatedObject.titleString();
+ this.object = object;
+ this.associatedObject = associatedObject;
+ this.field = field;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public void undo() {
+ field.clearAssociation(object);
+ }
+
+ @Override
+ public void execute() {
+ field.setAssociation(object, associatedObject);
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/undo/SetValueCommand.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/undo/SetValueCommand.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/undo/SetValueCommand.java
new file mode 100644
index 0000000..2b04f0f
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/undo/SetValueCommand.java
@@ -0,0 +1,86 @@
+/*
+ * 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.undo;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
+import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+import org.apache.isis.runtimes.dflt.runtime.system.persistence.AdapterManagerSpi;
+import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceSession;
+import org.apache.isis.viewer.dnd.view.Command;
+
+public class SetValueCommand implements Command {
+ private final String description;
+ private final OneToOneAssociation value;
+ private final ObjectAdapter object;
+ private final String oldValue;
+
+ public SetValueCommand(final ObjectAdapter object, final OneToOneAssociation value) {
+ final EncodableFacet facet = value.getFacet(EncodableFacet.class);
+ this.oldValue = facet.toEncodedString(object);
+ this.object = object;
+ this.value = value;
+
+ this.description = "reset the value to " + oldValue;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public void undo() {
+ final EncodableFacet facet = value.getFacet(EncodableFacet.class);
+ final Object obj = facet.fromEncodedString(oldValue);
+ final ObjectAdapter adapter = getAdapterManager().adapterFor(obj);
+ value.setAssociation(object, adapter);
+ // have commented this out because it isn't needed; the transaction
+ // manager will do this
+ // for us on endTransaction. Still, if I'm wrong and it is needed,
+ // hopefully this
+ // comment will help...
+ // IsisContext.getObjectPersistor().objectChangedAllDirty();
+ }
+
+ @Override
+ public void execute() {
+ }
+
+ @Override
+ public String getName() {
+ return "entry";
+ }
+
+ // //////////////////////////////////////////////////////////////////
+ // Dependencies (from context)
+ // //////////////////////////////////////////////////////////////////
+
+ private static PersistenceSession getPersistenceSession() {
+ return IsisContext.getPersistenceSession();
+ }
+
+ private static AdapterManager getAdapterManager() {
+ return getPersistenceSession().getAdapterManager();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/AbstractWindowBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/AbstractWindowBorder.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/AbstractWindowBorder.java
new file mode 100644
index 0000000..0ec0d67
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/AbstractWindowBorder.java
@@ -0,0 +1,211 @@
+/*
+ * 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.window;
+
+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.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.ViewDragImpl;
+import org.apache.isis.viewer.dnd.view.Click;
+import org.apache.isis.viewer.dnd.view.DragEvent;
+import org.apache.isis.viewer.dnd.view.DragStart;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewState;
+import org.apache.isis.viewer.dnd.view.Workspace;
+import org.apache.isis.viewer.dnd.view.base.AbstractBorder;
+import org.apache.isis.viewer.dnd.view.border.BorderDrawing;
+
+public abstract class AbstractWindowBorder extends AbstractBorder {
+ protected static BorderDrawing borderRender;
+ protected WindowControl controls[];
+ private WindowControl overControl;
+
+ public static void setBorderRenderer(final BorderDrawing borderRender) {
+ AbstractWindowBorder.borderRender = borderRender;
+ }
+
+ public AbstractWindowBorder(final View enclosedView) {
+ super(enclosedView);
+ left = borderRender.getLeft();
+ right = borderRender.getRight();
+ top = borderRender.getTop();
+ bottom = borderRender.getBottom();
+ }
+
+ @Override
+ public void debugDetails(final DebugBuilder debug) {
+ super.debugDetails(debug);
+ borderRender.debugDetails(debug);
+ if (controls.length > 0) {
+ debug.appendln("controls:-");
+ debug.indent();
+ for (final WindowControl control : controls) {
+ debug.append(control);
+ debug.appendln();
+ }
+ debug.unindent();
+ }
+ }
+
+ @Override
+ public DragEvent dragStart(final DragStart drag) {
+ if (overBorder(drag.getLocation())) {
+ final Location location = drag.getLocation();
+ final View dragOverlay = Toolkit.getViewFactory().createDragViewOutline(getView());
+ return new ViewDragImpl(this, new Offset(location.getX(), location.getY()), dragOverlay);
+ } else {
+ return super.dragStart(drag);
+ }
+ }
+
+ protected void setControls(final WindowControl[] controls) {
+ this.controls = controls;
+ }
+
+ @Override
+ public void setSize(final Size size) {
+ super.setSize(size);
+ layoutControls(size);
+ }
+
+ @Override
+ public void setBounds(final Bounds bounds) {
+ super.setBounds(bounds);
+ layoutControls(bounds.getSize());
+ }
+
+ private void layoutControls(final Size size) {
+ left = borderRender.getLeft();
+ right = borderRender.getRight();
+ top = borderRender.getTop();
+ bottom = borderRender.getBottom();
+
+ borderRender.layoutControls(size, controls);
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ // blank background
+ final Bounds bounds = getBounds();
+ final Color color = Toolkit.getColor(ColorsAndFonts.COLOR_WINDOW + "." + getSpecification().getName());
+ canvas.drawSolidRectangle(1, 1, bounds.getWidth() - 2, bounds.getHeight() - 2, color);
+
+ final boolean hasFocus = containsFocus();
+ final ViewState state = getState();
+ borderRender.draw(canvas, getSize(), hasFocus, state, controls, title() + " (" + getSpecification().getName() + ")");
+ // canvas.drawRectangle(0, 0, getSize().getWidth(),
+ // borderRender.getTop(), Toolkit.getColor(0xfff));
+
+ // controls
+ for (int i = 0; controls != null && i < controls.length; i++) {
+ final Canvas controlCanvas = canvas.createSubcanvas(controls[i].getBounds());
+ controls[i].draw(controlCanvas);
+ }
+
+ super.draw(canvas);
+ }
+
+ protected abstract String title();
+
+ @Override
+ public Size getRequiredSize(final Size maximumSize) {
+ left = borderRender.getLeft();
+ right = borderRender.getRight();
+ top = borderRender.getTop();
+ bottom = borderRender.getBottom();
+
+ final Size size = super.getRequiredSize(maximumSize);
+ borderRender.getRequiredSize(size, title(), controls);
+ return size;
+ }
+
+ @Override
+ public void secondClick(final Click click) {
+ final View control = overControl(click.getLocation());
+ if (control == null) {
+ super.secondClick(click);
+ }
+ }
+
+ @Override
+ public void thirdClick(final Click click) {
+ final View control = overControl(click.getLocation());
+ if (control == null) {
+ super.thirdClick(click);
+ }
+ }
+
+ @Override
+ public void firstClick(final Click click) {
+ final View control = overControl(click.getLocation());
+ if (control == null) {
+ if (overBorder(click.getLocation())) {
+ final Workspace workspace = getWorkspace();
+ if (workspace != null) {
+ if (click.button2()) {
+ workspace.lower(getView());
+ } else if (click.button1()) {
+ workspace.raise(getView());
+ }
+ }
+ } else {
+ super.firstClick(click);
+ }
+
+ } else {
+ control.firstClick(click);
+ }
+ }
+
+ @Override
+ public void mouseMoved(final Location at) {
+ final WindowControl control = (WindowControl) overControl(at);
+ if (control != null) {
+ if (control != overControl) {
+ control.entered();
+ overControl = control;
+ return;
+ }
+ } else {
+ if (control != overControl) {
+ overControl.exited();
+ overControl = null;
+ return;
+ }
+ }
+ super.mouseMoved(at);
+ }
+
+ private View overControl(final Location location) {
+ for (final WindowControl control : controls) {
+ if (control.getBounds().contains(location)) {
+ return control;
+ }
+ }
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/CloseWindowControl.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/CloseWindowControl.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/CloseWindowControl.java
new file mode 100644
index 0000000..d497a82
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/CloseWindowControl.java
@@ -0,0 +1,41 @@
+/*
+ * 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.window;
+
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.option.CloseViewOption;
+
+public class CloseWindowControl extends WindowControl {
+ private static CloseWindowRender render;
+
+ public static void setRender(final CloseWindowRender render) {
+ CloseWindowControl.render = render;
+ }
+
+ public CloseWindowControl(final View target) {
+ super(new CloseViewOption(), target);
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ render.draw(canvas, WIDTH, HEIGHT, action.disabled(this).isVetoed(), isOver(), isPressed());
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/CloseWindowRender.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/CloseWindowRender.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/CloseWindowRender.java
new file mode 100644
index 0000000..d19ba1f
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/CloseWindowRender.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.window;
+
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+
+public interface CloseWindowRender {
+
+ void draw(Canvas canvas, int width, int height, boolean isDisabled, boolean isOver, boolean isPressed);
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/DialogBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/DialogBorder.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/DialogBorder.java
new file mode 100644
index 0000000..4823b05
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/DialogBorder.java
@@ -0,0 +1,41 @@
+/*
+ * 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.window;
+
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.border.ScrollBorder;
+
+public class DialogBorder extends AbstractWindowBorder {
+
+ public DialogBorder(final View wrappedView, final boolean scrollable) {
+ super(scrollable ? new ScrollBorder(wrappedView) : wrappedView);
+ setControls(new WindowControl[] { new CloseWindowControl(this) });
+ }
+
+ @Override
+ protected String title() {
+ return getContent().windowTitle();
+ }
+
+ @Override
+ public String toString() {
+ return wrappedView.toString() + "/DialogBorder [" + 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/window/IconizeWindowControl.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/IconizeWindowControl.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/IconizeWindowControl.java
new file mode 100644
index 0000000..9c54db3
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/IconizeWindowControl.java
@@ -0,0 +1,42 @@
+/*
+ * 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.window;
+
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.option.IconizeViewOption;
+
+public class IconizeWindowControl extends WindowControl {
+ private static IconizeWindowRender render;
+
+ public static void setRender(final IconizeWindowRender render) {
+ IconizeWindowControl.render = render;
+ }
+
+ public IconizeWindowControl(final View target) {
+ super(new IconizeViewOption(), target);
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ render.draw(canvas, WIDTH, HEIGHT, false, isOver(), isPressed());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/IconizeWindowRender.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/IconizeWindowRender.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/IconizeWindowRender.java
new file mode 100644
index 0000000..82ccca8
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/IconizeWindowRender.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.window;
+
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+
+public interface IconizeWindowRender {
+
+ void draw(Canvas canvas, int width, int height, boolean isDisabled, boolean isOver, boolean isPressed);
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/ResizeWindowControl.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/ResizeWindowControl.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/ResizeWindowControl.java
new file mode 100644
index 0000000..47afae4
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/ResizeWindowControl.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.isis.viewer.dnd.view.window;
+
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.consent.Veto;
+import org.apache.isis.core.metamodel.spec.ActionType;
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.view.UserAction;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.Workspace;
+
+public class ResizeWindowControl extends WindowControl {
+ private static ResizeWindowRender render;
+
+ public static void setRender(final ResizeWindowRender render) {
+ ResizeWindowControl.render = render;
+ }
+
+ public ResizeWindowControl(final View target) {
+ super(new UserAction() {
+
+ @Override
+ public Consent disabled(final View view) {
+ return Veto.DEFAULT;
+ }
+
+ @Override
+ public void execute(final Workspace workspace, final View view, final Location at) {
+ }
+
+ @Override
+ public String getDescription(final View view) {
+ return "";
+ }
+
+ @Override
+ public String getHelp(final View view) {
+ return "";
+ }
+
+ @Override
+ public ActionType getType() {
+ return ActionType.USER;
+ }
+
+ @Override
+ public String getName(final View view) {
+ return "Resize";
+ }
+ }, target);
+
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ render.draw(canvas, WIDTH, HEIGHT, action.disabled(this).isVetoed(), isOver(), isPressed());
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/ResizeWindowRender.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/ResizeWindowRender.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/ResizeWindowRender.java
new file mode 100644
index 0000000..c18e93b
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/ResizeWindowRender.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.window;
+
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+
+public interface ResizeWindowRender {
+
+ void draw(Canvas canvas, int width, int height, boolean isDisabled, boolean isOver, boolean isPressed);
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/SubviewFocusManager.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/SubviewFocusManager.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/SubviewFocusManager.java
new file mode 100644
index 0000000..f0d738f
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/SubviewFocusManager.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.isis.viewer.dnd.view.window;
+
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.base.AbstractFocusManager;
+
+public class SubviewFocusManager extends AbstractFocusManager {
+ private final WindowBorder windowBorder;
+
+ public SubviewFocusManager(final WindowBorder container) {
+ super(container);
+ windowBorder = container;
+ }
+
+ public SubviewFocusManager(final View container) {
+ super(container);
+ windowBorder = null;
+ }
+
+ public SubviewFocusManager(final View container, final View initalFocus) {
+ super(container, initalFocus);
+ windowBorder = null;
+ }
+
+ @Override
+ protected View[] getChildViews() {
+ final View[] subviews = container.getSubviews();
+ final View[] buttons = windowBorder == null ? new View[0] : windowBorder.getButtons();
+
+ final View[] views = new View[subviews.length + buttons.length];
+ System.arraycopy(subviews, 0, views, 0, subviews.length);
+ System.arraycopy(buttons, 0, views, subviews.length, buttons.length);
+ return views;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/WindowBorder.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/WindowBorder.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/WindowBorder.java
new file mode 100644
index 0000000..5614a4f
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/view/window/WindowBorder.java
@@ -0,0 +1,132 @@
+/*
+ * 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.window;
+
+import java.util.Enumeration;
+
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+import org.apache.isis.viewer.dnd.view.Click;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.UserAction;
+import org.apache.isis.viewer.dnd.view.UserActionSet;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.border.ButtonBorder;
+import org.apache.isis.viewer.dnd.view.border.SaveTransientObjectBorder;
+import org.apache.isis.viewer.dnd.view.border.ScrollBorder;
+import org.apache.isis.viewer.dnd.view.option.CloseAllViewsForObjectOption;
+import org.apache.isis.viewer.dnd.view.option.CloseAllViewsOption;
+import org.apache.isis.viewer.dnd.view.option.CloseOtherViewsForObjectOption;
+import org.apache.isis.viewer.dnd.view.option.CloseViewOption;
+import org.apache.isis.viewer.dnd.view.option.IconizeViewOption;
+import org.apache.isis.viewer.dnd.view.option.ReplaceViewOption;
+
+public class WindowBorder extends AbstractWindowBorder {
+ private static final UserAction CLOSE_ALL_OPTION = new CloseAllViewsOption();
+ private static final UserAction CLOSE_OPTION = new CloseViewOption();
+ private static final UserAction CLOSE_VIEWS_FOR_OBJECT = new CloseAllViewsForObjectOption();
+ private static final UserAction CLOSE_OTHER_VIEWS_FOR_OBJECT = new CloseOtherViewsForObjectOption();
+ private static final IconizeViewOption iconizeOption = new IconizeViewOption();
+
+ public WindowBorder(final View wrappedView, final boolean scrollable) {
+ super(addTransientBorderIfNeccessary(scrollable ? new ScrollBorder(wrappedView) : wrappedView));
+
+ if (isTransient()) {
+ setControls(new WindowControl[] { new CloseWindowControl(this) });
+ } else {
+ setControls(new WindowControl[] { new IconizeWindowControl(this), new ResizeWindowControl(this), new CloseWindowControl(this) });
+ }
+ }
+
+ private static View addTransientBorderIfNeccessary(final View view) {
+ final Content content = view.getContent();
+ if (content.isPersistable() && content.isTransient()) {
+ return new SaveTransientObjectBorder(view);
+ } else {
+ return view;
+ }
+ }
+
+ /* TODO fix focus management and remove this hack */
+ public View[] getButtons() {
+ if (wrappedView instanceof ButtonBorder) {
+ return ((ButtonBorder) wrappedView).getButtons();
+ } else {
+ return new View[0];
+ }
+ }
+
+ @Override
+ public void draw(final Canvas canvas) {
+ super.draw(canvas);
+ if (isTransient()) {
+ borderRender.drawTransientMarker(canvas, getSize());
+ }
+ }
+
+ private boolean isTransient() {
+ final Content content = getContent();
+ return content.isPersistable() && content.isTransient();
+ }
+
+ @Override
+ public void viewMenuOptions(final UserActionSet menuOptions) {
+ menuOptions.add(iconizeOption);
+ menuOptions.add(CLOSE_OPTION);
+ menuOptions.add(CLOSE_ALL_OPTION);
+ menuOptions.add(CLOSE_VIEWS_FOR_OBJECT);
+ menuOptions.add(CLOSE_OTHER_VIEWS_FOR_OBJECT);
+
+ super.viewMenuOptions(menuOptions);
+
+ final Content content = getContent();
+ final UserActionSet suboptions = menuOptions.addNewActionSet("Replace with");
+ replaceOptions(Toolkit.getViewFactory().availableViews(new ViewRequirement(content, ViewRequirement.OPEN)), suboptions);
+ replaceOptions(Toolkit.getViewFactory().availableViews(new ViewRequirement(content, ViewRequirement.CLOSED)), 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));
+ }
+ }
+ }
+ }
+
+ @Override
+ public void secondClick(final Click click) {
+ if (overBorder(click.getLocation())) {
+ iconizeOption.execute(getWorkspace(), getView(), getAbsoluteLocation());
+ } else {
+ super.secondClick(click);
+ }
+ }
+
+ @Override
+ protected String title() {
+ return getContent().windowTitle();
+ }
+
+}