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

[12/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/CheckboxField.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/CheckboxField.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/CheckboxField.java
new file mode 100644
index 0000000..dd44730
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/CheckboxField.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.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.progmodel.facets.value.booleans.BooleanValueFacet;
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+import org.apache.isis.viewer.dnd.drawing.Color;
+import org.apache.isis.viewer.dnd.drawing.ColorsAndFonts;
+import org.apache.isis.viewer.dnd.drawing.Image;
+import org.apache.isis.viewer.dnd.drawing.ImageFactory;
+import org.apache.isis.viewer.dnd.drawing.Size;
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Click;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.KeyboardAction;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewConstants;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.base.AbstractFieldSpecification;
+import org.apache.isis.viewer.dnd.view.content.TextParseableContent;
+
+/*
+ * TODO this class does not set the underlying business object  via its boolean adapter.  Need
+ * to create an content type for flags.
+ */
+public class CheckboxField extends AbstractField {
+    private static final int size = Toolkit.getText(ColorsAndFonts.TEXT_NORMAL).getTextHeight();
+
+    public static class Specification extends AbstractFieldSpecification {
+        @Override
+        public boolean canDisplay(final ViewRequirement requirement) {
+            return requirement.isTextParseable() && requirement.isForValueType(BooleanValueFacet.class);
+        }
+
+        @Override
+        public View createView(final Content content, final Axes axes, final int sequence) {
+            return new CheckboxField(content, this);
+        }
+
+        @Override
+        public String getName() {
+            return "Checkbox";
+        }
+    }
+
+    public CheckboxField(final Content content, final ViewSpecification specification) {
+        super(content, specification);
+    }
+
+    @Override
+    public void draw(final Canvas canvas) {
+        Color color;
+        color = getIdentified() ? Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY2) : null;
+        color = hasFocus() ? Toolkit.getColor(ColorsAndFonts.COLOR_IDENTIFIED) : color;
+
+        final int top = ViewConstants.VPADDING;
+        final int left = ViewConstants.HPADDING;
+        if (color != null) {
+            canvas.drawRectangle(left - 2, top - 2, size + 4, size + 4, color);
+        }
+
+        color = Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY1);
+        canvas.drawRectangle(left, top, size, size, color);
+        if (isSet()) {
+            final Image image = ImageFactory.getInstance().loadImage("check-mark");
+            canvas.drawImage(image, 3, 3, size, size);
+        }
+    }
+
+    @Override
+    public void firstClick(final Click click) {
+        toggle();
+    }
+
+    @Override
+    public void secondClick(final Click click) {
+        // ignore
+    }
+
+    @Override
+    public void thirdClick(final Click click) {
+        // ignore
+    }
+
+    @Override
+    public void keyTyped(final KeyboardAction action) {
+        if (action.getKeyCode() == ' ') {
+            toggle();
+        } else {
+            super.keyTyped(action);
+        }
+    }
+
+    private void toggle() {
+        if (canChangeValue().isAllowed()) {
+            initiateSave(false);
+        }
+    }
+
+    @Override
+    public Consent canChangeValue() {
+        final TextParseableContent cont = (TextParseableContent) getContent();
+        return cont.isEditable();
+    }
+
+    @Override
+    public int getBaseline() {
+        return ViewConstants.VPADDING + Toolkit.getText(ColorsAndFonts.TEXT_NORMAL).getAscent();
+    }
+
+    @Override
+    public Size getRequiredSize(final Size availableSpace) {
+        return new Size(ViewConstants.HPADDING + size + ViewConstants.HPADDING, ViewConstants.VPADDING + size + ViewConstants.VPADDING);
+    }
+
+    private boolean isSet() {
+        final BooleanValueFacet booleanValueFacet = getContent().getSpecification().getFacet(BooleanValueFacet.class);
+        return booleanValueFacet.isSet(getContent().getAdapter());
+    }
+
+    @Override
+    protected void save() {
+        final BooleanValueFacet booleanValueFacet = getContent().getSpecification().getFacet(BooleanValueFacet.class);
+        final ObjectAdapter adapter = getContent().getAdapter();
+        if (adapter == null) {
+            ((TextParseableContent) getContent()).parseTextEntry("true");
+        } else {
+            booleanValueFacet.toggle(adapter);
+        }
+
+        // return parsed != null ? PersistorUtil.createAdapter(parsed) : null;
+
+        markDamaged();
+        ((TextParseableContent) getContent()).entryComplete();
+        getParent().invalidateContent();
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ClearValueOption.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ClearValueOption.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ClearValueOption.java
new file mode 100644
index 0000000..3daa53f
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ClearValueOption.java
@@ -0,0 +1,69 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.field;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.consent.Veto;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.Workspace;
+
+public class ClearValueOption extends AbstractValueOption {
+
+    public ClearValueOption(final AbstractField field) {
+        super(field, "Clear");
+    }
+
+    @Override
+    public String getDescription(final View view) {
+        return "Clear field";
+    }
+
+    @Override
+    public Consent disabled(final View view) {
+        final ObjectAdapter value = getValue(view);
+        final Consent consent = view.canChangeValue();
+        if (consent.isVetoed()) {
+            return consent;
+        }
+        final Consent canClear = field.canClear();
+        if (canClear.isVetoed()) {
+            // TODO: move logic into Facets.
+            return new Veto(String.format("Can't clear %s values", value.getSpecification().getShortIdentifier()));
+        }
+        if (value == null || isEmpty(view)) {
+            // TODO: move logic into Facets.
+            return new Veto("Field is already empty");
+        }
+        // TODO: move logic into Facets.
+        return consent.setDescription(String.format("Clear value ", value.titleString()));
+    }
+
+    @Override
+    public void execute(final Workspace frame, final View view, final Location at) {
+        field.clear();
+    }
+
+    @Override
+    public String toString() {
+        return "ClearValueOption";
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ColorField.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ColorField.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ColorField.java
new file mode 100644
index 0000000..986ef0f
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ColorField.java
@@ -0,0 +1,144 @@
+/*
+ *  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.NotYetImplementedException;
+import org.apache.isis.core.metamodel.facets.object.parseable.InvalidEntryException;
+import org.apache.isis.core.progmodel.facets.value.color.ColorValueFacet;
+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.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewConstants;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.base.AbstractFieldSpecification;
+import org.apache.isis.viewer.dnd.view.content.TextParseableContent;
+import org.apache.isis.viewer.dnd.view.lookup.OpenDropDownBorder;
+import org.apache.isis.viewer.dnd.view.lookup.OptionContent;
+
+public class ColorField extends TextParseableFieldAbstract {
+    public static class Specification extends AbstractFieldSpecification {
+
+        @Override
+        public boolean canDisplay(final ViewRequirement requirement) {
+            return requirement.isTextParseable() && requirement.isForValueType(ColorValueFacet.class);
+        }
+
+        @Override
+        public View createView(final Content content, final Axes axes, final int sequence) {
+            final ColorField field = new ColorField(content, this);
+            return new OpenDropDownBorder(field) {
+                @Override
+                protected View createDropDownView() {
+                    return new ColorFieldOverlay(field);
+                }
+
+                @Override
+                protected void setSelection(final OptionContent selectedContent) {
+                }
+            };
+        }
+
+        @Override
+        public String getName() {
+            return "Color";
+        }
+    }
+
+    private int color;
+
+    public ColorField(final Content content, final ViewSpecification specification) {
+        super(content, specification);
+    }
+
+    @Override
+    public void draw(final Canvas canvas) {
+        Color color;
+
+        if (hasFocus()) {
+            color = Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY1);
+        } else if (getParent().getState().isObjectIdentified()) {
+            color = Toolkit.getColor(ColorsAndFonts.COLOR_IDENTIFIED);
+        } else if (getParent().getState().isRootViewIdentified()) {
+            color = Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY2);
+        } else {
+            color = Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY1);
+        }
+
+        int top = 0;
+        int left = 0;
+
+        final Size size = getSize();
+        int w = size.getWidth() - 1;
+        int h = size.getHeight() - 1;
+        canvas.drawRectangle(left, top, w, h, color);
+        left++;
+        top++;
+        w -= 1;
+        h -= 1;
+        canvas.drawSolidRectangle(left, top, w, h, Toolkit.getColor(getColor()));
+    }
+
+    /*
+     * @Override public void firstClick(final Click click) { if
+     * (((TextParseableContent) getContent()).isEditable().isAllowed()) { final
+     * View overlay = new DisposeOverlay(new ColorFieldOverlay(this), new
+     * ValueDropDownAxis((TextParseableContent) getContent(), getView())); final
+     * Location location = this.getAbsoluteLocation(); // Location location =
+     * click.getLocationWithinViewer(); // TODO offset by constant amount //
+     * location.move(10, 10); overlay.setLocation(location); //
+     * overlay.setSize(overlay.getRequiredSize(new Size())); //
+     * overlay.markDamaged(); getViewManager().setOverlayView(overlay); } }
+     */
+    @Override
+    public int getBaseline() {
+        return ViewConstants.VPADDING + Toolkit.getText(ColorsAndFonts.TEXT_NORMAL).getAscent();
+    }
+
+    int getColor() {
+        final TextParseableContent content = ((TextParseableContent) getContent());
+        final ColorValueFacet col = content.getSpecification().getFacet(ColorValueFacet.class);
+        return col.colorValue(content.getAdapter());
+    }
+
+    @Override
+    public Size getRequiredSize(final Size availableSpace) {
+        return new Size(45, 15);
+    }
+
+    @Override
+    protected void save() {
+        try {
+            parseEntry("" + color);
+        } catch (final InvalidEntryException e) {
+            throw new NotYetImplementedException();
+        }
+    }
+
+    void setColor(final int color) {
+        this.color = color;
+        initiateSave(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/ColorFieldOverlay.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ColorFieldOverlay.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ColorFieldOverlay.java
new file mode 100644
index 0000000..ab27820
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ColorFieldOverlay.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.field;
+
+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.Click;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.base.AbstractView;
+
+class ColorFieldOverlay extends AbstractView {
+    private static final int colors[] = new int[] { 0xffffff, 0x0, 0x666666, 0xcccccc, // white,
+                                                                                       // black,
+                                                                                       // dark
+            // gray, light gray
+            0x000099, 0x0066cc, 0x0033ff, 0x99ccff, // blues
+            0x990000, 0xff0033, 0xcc0066, 0xff66ff, // reds
+            0x003300, 0x00ff33, 0x669933, 0xccff66 // greens
+    };
+    private static final int COLUMNS = 4;
+    private static final int ROWS = 4;
+    private static final int ROW_HEIGHT = 18;
+    private static final int COLUMN_WIDTH = 23;
+
+    private final ColorField field;
+
+    public ColorFieldOverlay(final ColorField field) {
+        super(field.getContent());
+
+        this.field = field;
+    }
+
+    @Override
+    public Size getRequiredSize(final Size availableSpace) {
+        return new Size(COLUMNS * COLUMN_WIDTH, ROWS * ROW_HEIGHT);
+    }
+
+    @Override
+    public void draw(final Canvas canvas) {
+        canvas.drawSolidRectangle(0, 0, COLUMNS * COLUMN_WIDTH - 1, ROWS * ROW_HEIGHT - 1, Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY3));
+        for (int i = 0; i < colors.length; i++) {
+            final Color color = Toolkit.getColor(colors[i]);
+            final int y = i / COLUMNS * ROW_HEIGHT;
+            final int x = i % COLUMNS * COLUMN_WIDTH;
+            canvas.drawSolidRectangle(x, y, COLUMN_WIDTH - 1, ROW_HEIGHT - 1, color);
+        }
+        canvas.drawRectangle(0, 0, COLUMNS * COLUMN_WIDTH - 1, ROWS * ROW_HEIGHT - 1, Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY2));
+    }
+
+    @Override
+    public void firstClick(final Click click) {
+        final int x = click.getLocation().getX();
+        final int y = click.getLocation().getY();
+        final int color = colors[y / ROW_HEIGHT * COLUMNS + x / COLUMN_WIDTH];
+        field.setColor(color);
+        getViewManager().clearOverlayView();
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/CopyValueOption.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/CopyValueOption.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/CopyValueOption.java
new file mode 100644
index 0000000..b9219af
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/CopyValueOption.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.field;
+
+import org.apache.isis.core.metamodel.consent.Allow;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.consent.Veto;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.Workspace;
+
+public class CopyValueOption extends AbstractValueOption {
+
+    public CopyValueOption(final AbstractField field) {
+        super(field, "Copy");
+    }
+
+    @Override
+    public Consent disabled(final View view) {
+        if (isEmpty(view)) {
+            // TODO: move logic into Facets
+            return new Veto("Field is empty");
+        }
+        // TODO: move logic into Facets
+        return Allow.DEFAULT;
+        // return new Allow(String.format("Copy value '%s' to clipboard",
+        // field.getSelectedText()));
+    }
+
+    @Override
+    public void execute(final Workspace frame, final View view, final Location at) {
+        field.copyToClipboard();
+    }
+
+    @Override
+    public String toString() {
+        return "CopyValueOption";
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/DateFieldSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/DateFieldSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/DateFieldSpecification.java
new file mode 100644
index 0000000..5a8cc12
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/DateFieldSpecification.java
@@ -0,0 +1,62 @@
+/*
+ *  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.progmodel.facets.value.date.DateValueFacet;
+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.OpenDropDownBorder;
+import org.apache.isis.viewer.dnd.view.lookup.OptionContent;
+
+/**
+ * Creates a single line text field with the base line drawn.
+ */
+public class DateFieldSpecification extends AbstractFieldSpecification {
+    @Override
+    public boolean canDisplay(final ViewRequirement requirement) {
+        return requirement.isTextParseable() && requirement.isForValueType(DateValueFacet.class);
+    }
+
+    @Override
+    public View createView(final Content content, final Axes axes, final int sequence) {
+        final SingleLineTextField textField = new SingleLineTextField((TextParseableContent) content, this, true);
+        final View field = new TextFieldResizeBorder(textField);
+        return new OpenDropDownBorder(field) {
+            @Override
+            protected View createDropDownView() {
+                return DatePickerControl.getPicker(content);
+            }
+
+            @Override
+            protected void setSelection(final OptionContent selectedContent) {
+            }
+        };
+    }
+
+    @Override
+    public String getName() {
+        return "Date Field";
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/DatePicker.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/DatePicker.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/DatePicker.java
new file mode 100644
index 0000000..2153fb5
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/DatePicker.java
@@ -0,0 +1,30 @@
+/*
+ *  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.Size;
+
+public interface DatePicker {
+
+    public abstract Size getRequiredSize(Size availableSpace);
+
+    public abstract void draw(final Canvas 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/DatePickerControl.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/DatePickerControl.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/DatePickerControl.java
new file mode 100644
index 0000000..99f1675
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/DatePickerControl.java
@@ -0,0 +1,40 @@
+/*
+ *  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.Content;
+import org.apache.isis.viewer.dnd.view.Look;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.look.LookFactory;
+import org.apache.isis.viewer.dnd.view.look.linux.LinuxDatePicker;
+import org.apache.isis.viewer.dnd.view.look.linux.LinuxLook;
+
+public class DatePickerControl {
+
+    private static final Look LINUX_LOOK = new LinuxLook();
+
+    public static View getPicker(final Content content) {
+        final Look look = LookFactory.getInstalledLook();
+        if (look.getClass().isInstance(LINUX_LOOK)) {
+            return new LinuxDatePicker(content);
+        }
+        return new SimpleDatePicker(content);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/EmptyField.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/EmptyField.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/EmptyField.java
new file mode 100644
index 0000000..1f5449a
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/EmptyField.java
@@ -0,0 +1,208 @@
+/*
+ *  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.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+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.icon.IconSpecification;
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.ContentDrag;
+import org.apache.isis.viewer.dnd.view.ObjectContent;
+import org.apache.isis.viewer.dnd.view.Placement;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.ViewConstants;
+import org.apache.isis.viewer.dnd.view.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.action.ObjectParameter;
+import org.apache.isis.viewer.dnd.view.base.AbstractView;
+import org.apache.isis.viewer.dnd.view.base.IconGraphic;
+import org.apache.isis.viewer.dnd.view.border.ObjectBorder;
+import org.apache.isis.viewer.dnd.view.field.OneToOneField;
+import org.apache.isis.viewer.dnd.view.lookup.OpenObjectDropDownBorder;
+import org.apache.isis.viewer.dnd.view.text.TitleText;
+
+public class EmptyField extends AbstractView {
+
+    public static class Specification implements ViewSpecification {
+        @Override
+        public boolean canDisplay(final ViewRequirement requirement) {
+            return requirement.isObject() && requirement.isOpen() && !requirement.isTextParseable() && !requirement.hasReference();
+        }
+
+        @Override
+        public View createView(final Content content, final Axes axes, final int sequence) {
+            final EmptyField emptyField = new EmptyField(content, this, Toolkit.getText(ColorsAndFonts.TEXT_NORMAL));
+            if ((content instanceof OneToOneField && ((OneToOneField) content).isEditable().isAllowed()) || content instanceof ObjectParameter) {
+                if (content.isOptionEnabled()) {
+                    return new ObjectBorder(new OpenObjectDropDownBorder(emptyField, new IconSpecification()));
+                } else {
+                    return new ObjectBorder(emptyField);
+                }
+            } else {
+                return emptyField;
+            }
+        }
+
+        @Override
+        public String getName() {
+            return "empty field";
+        }
+
+        @Override
+        public boolean isAligned() {
+            return false;
+        }
+
+        @Override
+        public boolean isOpen() {
+            return false;
+        }
+
+        @Override
+        public boolean isReplaceable() {
+            return true;
+        }
+
+        @Override
+        public boolean isResizeable() {
+            return false;
+        }
+
+        @Override
+        public boolean isSubView() {
+            return true;
+        }
+    }
+
+    private final IconGraphic icon;
+    private final TitleText text;
+
+    public EmptyField(final Content content, final ViewSpecification specification, final Text style) {
+        super(content, specification);
+        if (((ObjectContent) content).getObject() != null) {
+            throw new IllegalArgumentException("Content for EmptyField must be null: " + content);
+        }
+        final ObjectAdapter object = ((ObjectContent) getContent()).getObject();
+        if (object != null) {
+            throw new IllegalArgumentException("Content for EmptyField must be null: " + object);
+        }
+        icon = new IconGraphic(this, style);
+        text = new EmptyFieldTitleText(this, style);
+    }
+
+    @Override
+    public void draw(final Canvas canvas) {
+        super.draw(canvas);
+        int x = 0;
+        final int y = icon.getBaseline();
+        icon.draw(canvas, x, y);
+        x += icon.getSize().getWidth();
+        x += ViewConstants.HPADDING;
+
+        text.draw(canvas, x, y);
+    }
+
+    @Override
+    public int getBaseline() {
+        return icon.getBaseline();
+    }
+
+    @Override
+    public Size getRequiredSize(final Size availableSpace) {
+        final Size size = icon.getSize();
+        size.extendWidth(ViewConstants.HPADDING);
+        size.extendWidth(text.getSize().getWidth());
+        return size;
+    }
+
+    private Consent canDrop(final ObjectAdapter dragSource) {
+        final ObjectContent content = (ObjectContent) getContent();
+        return content.canSet(dragSource);
+    }
+
+    @Override
+    public void dragIn(final ContentDrag drag) {
+        final Content sourceContent = drag.getSourceContent();
+        if (sourceContent instanceof ObjectContent) {
+            final ObjectAdapter source = ((ObjectContent) sourceContent).getObject();
+            final Consent canDrop = canDrop(source);
+            if (canDrop.isAllowed()) {
+                getState().setCanDrop();
+            } else {
+                getState().setCantDrop();
+            }
+            final String actionText = canDrop.isVetoed() ? canDrop.getReason() : "Set to " + sourceContent.title();
+            getFeedbackManager().setAction(actionText);
+        } else {
+            getState().setCantDrop();
+        }
+
+        markDamaged();
+    }
+
+    @Override
+    public void dragOut(final ContentDrag drag) {
+        getState().clearObjectIdentified();
+        markDamaged();
+    }
+
+    @Override
+    public void drop(final ContentDrag drag) {
+        getState().clearViewIdentified();
+        markDamaged();
+        final ObjectAdapter target = ((ObjectContent) getParent().getContent()).getObject();
+        final Content sourceContent = drag.getSourceContent();
+        if (sourceContent instanceof ObjectContent) {
+            final ObjectAdapter source = ((ObjectContent) sourceContent).getObject();
+            setField(target, source);
+        }
+    }
+
+    /**
+     * Objects returned by menus are used to set this field before passing the
+     * call on to the parent.
+     */
+    @Override
+    public void objectActionResult(final ObjectAdapter result, final Placement placement) {
+        final ObjectAdapter target = ((ObjectContent) getParent().getContent()).getObject();
+        if (result instanceof ObjectAdapter) {
+            setField(target, result);
+        }
+        super.objectActionResult(result, placement);
+    }
+
+    private void setField(final ObjectAdapter parent, final ObjectAdapter object) {
+        if (canDrop(object).isAllowed()) {
+            ((ObjectContent) getContent()).setObject(object);
+            getParent().invalidateContent();
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "EmptyField" + getId();
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/EmptyFieldTitleText.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/EmptyFieldTitleText.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/EmptyFieldTitleText.java
new file mode 100644
index 0000000..4cbc524
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/EmptyFieldTitleText.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.field;
+
+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.ObjectContent;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.text.TitleText;
+
+class EmptyFieldTitleText extends TitleText {
+    private final Content content;
+
+    public EmptyFieldTitleText(final View view, final Text style) {
+        super(view, style, Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY2));
+        content = view.getContent();
+    }
+
+    @Override
+    protected String title() {
+        return ((ObjectContent) content).getSpecification().getSingularName();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/FieldOfSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/FieldOfSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/FieldOfSpecification.java
new file mode 100644
index 0000000..362405f
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/FieldOfSpecification.java
@@ -0,0 +1,139 @@
+/*
+ *  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.metamodel.adapter.ObjectAdapter;
+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.form.InternalFormSpecification;
+import org.apache.isis.viewer.dnd.list.SimpleListSpecification;
+import org.apache.isis.viewer.dnd.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+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.IconBorder;
+import org.apache.isis.viewer.dnd.view.composite.CompositeView;
+import org.apache.isis.viewer.dnd.view.content.FieldContent;
+import org.apache.isis.viewer.dnd.view.text.TitleText;
+
+public class FieldOfSpecification implements ViewSpecification {
+
+    @Override
+    public boolean canDisplay(final ViewRequirement requirement) {
+        return requirement.isOpen() && !requirement.isSubview() && requirement.getContent() instanceof FieldContent;
+    }
+
+    @Override
+    public String getName() {
+        return "Field Of";
+    }
+
+    @Override
+    public boolean isAligned() {
+        return false;
+    }
+
+    @Override
+    public boolean isOpen() {
+        return false;
+    }
+
+    @Override
+    public boolean isReplaceable() {
+        return false;
+    }
+
+    @Override
+    public boolean isResizeable() {
+        return false;
+    }
+
+    @Override
+    public boolean isSubView() {
+        return false;
+    }
+
+    @Override
+    public View createView(final Content content, final Axes axes, final int sequence) {
+        final FieldContent fieldContent = (FieldContent) content;
+        final ObjectAdapter parent = fieldContent.getParent();
+        final Content parentContent = Toolkit.getContentFactory().createRootContent(parent);
+        View view = new InternalFieldView(parentContent, fieldContent, axes, this);
+        view = addBorder(parentContent, fieldContent, view);
+        return view;
+    }
+
+    private View addBorder(final Content parentContent, final FieldContent fieldContent, View view) {
+        final Text textStyle = Toolkit.getText(ColorsAndFonts.TEXT_TITLE);
+        final Color colorStyle = Toolkit.getColor(ColorsAndFonts.COLOR_BLACK);
+        final TitleText titleText = new TitleText(view, textStyle, colorStyle) {
+            @Override
+            protected String title() {
+                return parentContent.title() + "/" + fieldContent.getFieldName();
+            }
+        };
+        view = new IconBorder(view, titleText, null, textStyle);
+        return view;
+    }
+
+}
+
+class InternalFieldView extends CompositeView {
+    // final View[] subviews = new View[1];
+
+    private final Content fieldContent;
+
+    public InternalFieldView(final Content content, final Content fieldContent, final Axes axes, final ViewSpecification specification) {
+        super(content, specification);
+        this.fieldContent = fieldContent;
+    }
+
+    /*
+     * public void draw(Canvas canvas) { subviews[0].draw(canvas); }
+     * 
+     * public View[] getSubviews() { return subviews; }
+     */
+    @Override
+    public Size requiredSize(final Size availableSpace) {
+        return getSubviews()[0].getRequiredSize(availableSpace);
+    }
+
+    @Override
+    protected void doLayout(final Size maximumSize) {
+        final View view = getSubviews()[0];
+        view.setSize(view.getRequiredSize(maximumSize));
+        view.layout();
+    }
+
+    @Override
+    protected void buildView() {
+        ViewSpecification internalSpecification;
+        if (fieldContent.isCollection()) {
+            internalSpecification = new SimpleListSpecification();
+        } else {
+            internalSpecification = new InternalFormSpecification();
+        }
+        addView(internalSpecification.createView(fieldContent, new Axes(), 0));
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ImageField.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ImageField.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ImageField.java
new file mode 100644
index 0000000..cfbce6c
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/ImageField.java
@@ -0,0 +1,263 @@
+/*
+ *  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.Image;
+import java.awt.MediaTracker;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.io.File;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.commons.exceptions.IsisException;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.progmodel.facets.value.image.ImageValueFacet;
+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.view.Axes;
+import org.apache.isis.viewer.dnd.view.Content;
+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.ViewRequirement;
+import org.apache.isis.viewer.dnd.view.ViewSpecification;
+import org.apache.isis.viewer.dnd.view.Workspace;
+import org.apache.isis.viewer.dnd.view.base.AbstractFieldSpecification;
+import org.apache.isis.viewer.dnd.view.base.AwtImage;
+import org.apache.isis.viewer.dnd.view.content.FieldContent;
+import org.apache.isis.viewer.dnd.view.field.OneToOneField;
+import org.apache.isis.viewer.dnd.view.option.UserActionAbstract;
+
+public class ImageField extends AbstractField {
+    public static class Specification extends AbstractFieldSpecification {
+        @Override
+        public boolean canDisplay(final ViewRequirement requirement) {
+            return requirement.isForValueType(ImageValueFacet.class);
+        }
+
+        @Override
+        public View createView(final Content content, final Axes axes, final int sequence) {
+            return new ImageField(content, this);
+        }
+
+        @Override
+        public String getName() {
+            return "Image";
+        }
+    }
+
+    private static final Logger LOG = Logger.getLogger(ImageField.class);
+    private static final MediaTracker mt = new MediaTracker(new java.awt.Canvas());
+
+    public ImageField(final Content content, final ViewSpecification specification) {
+        super(content, specification);
+    }
+
+    @Override
+    public boolean canFocus() {
+        return true;
+    }
+
+    @Override
+    public void contentMenuOptions(final UserActionSet options) {
+        super.contentMenuOptions(options);
+
+        options.add(new UserActionAbstract("Load image from file...") {
+            @Override
+            public void execute(final Workspace workspace, final View view, final Location at) {
+                final String file = getViewManager().selectFilePath("Load image", ".");
+                if (new File(file).exists()) {
+                    loadImageFromFile(file);
+                }
+            }
+        });
+    }
+
+    private void copy() {
+    }
+
+    @Override
+    public void draw(final Canvas canvas) {
+        Color color;
+
+        if (hasFocus()) {
+            color = Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY1);
+        } else if (getParent().getState().isObjectIdentified()) {
+            color = Toolkit.getColor(ColorsAndFonts.COLOR_IDENTIFIED);
+        } else if (getParent().getState().isRootViewIdentified()) {
+            color = Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY2);
+        } else {
+            color = Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY1);
+        }
+
+        int top = 0;
+        int left = 0;
+
+        final Size size = getSize();
+        int w = size.getWidth() - 1;
+        int h = size.getHeight() - 1;
+        canvas.drawRectangle(left, top, w, h, color);
+        left++;
+        top++;
+        w -= 1;
+        h -= 1;
+
+        final ObjectAdapter value = getContent().getAdapter();
+        if (value != null) {
+            final ImageValueFacet facet = value.getSpecification().getFacet(ImageValueFacet.class);
+            final java.awt.Image image = facet.getImage(value);
+            if (image != null) {
+                final Size imageSize = new Size(facet.getWidth(value), facet.getHeight(value));
+                if (imageSize.getWidth() <= w && imageSize.getHeight() <= h) {
+                    canvas.drawImage(new AwtImage(image), left, top);
+                } else {
+                    canvas.drawImage(new AwtImage(image), left, top, w, h);
+                }
+            }
+        }
+    }
+
+    @Override
+    public int getBaseline() {
+        return ViewConstants.VPADDING + Toolkit.getText(ColorsAndFonts.TEXT_NORMAL).getAscent();
+    }
+
+    @Override
+    public Size getRequiredSize(final Size availableSpace) {
+        final ObjectAdapter value = getContent().getAdapter();
+        if (value == null) {
+            return super.getRequiredSize(availableSpace);
+        } else {
+            final ImageValueFacet facet = value.getSpecification().getFacet(ImageValueFacet.class);
+            final int width = Math.min(120, Math.max(32, facet.getWidth(value)));
+            final int height = Math.min(120, Math.max(32, facet.getHeight(value)));
+            return new Size(width, height);
+        }
+    }
+
+    @Override
+    public void keyPressed(final KeyboardAction key) {
+        if (canChangeValue().isVetoed()) {
+            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();
+        final boolean ctrl = (modifiers & InputEvent.CTRL_MASK) > 0;
+
+        switch (keyCode) {
+        case KeyEvent.VK_V:
+            if (ctrl) {
+                key.consume();
+                pasteFromClipboard();
+            }
+            break;
+        case KeyEvent.VK_C:
+            if (ctrl) {
+                key.consume();
+                copy();
+            }
+            break;
+        }
+    }
+
+    private void loadImage(final Image image) {
+        mt.addImage(image, 1);
+        try {
+            mt.waitForAll();
+        } catch (final InterruptedException e) {
+            throw new IsisException(e);
+        }
+
+        // final ObjectAdapter value = getContent().getAdapter();
+        final ImageValueFacet facet = ((FieldContent) getContent()).getSpecification().getFacet(ImageValueFacet.class);
+        final ObjectAdapter object = facet.createValue(image);
+        ((OneToOneField) getContent()).setObject(object);
+        // ((TextParseableField) getContent()).entryComplete();
+        invalidateLayout();
+    }
+
+    /*
+     * private void loadImageFromURL(final String filename) { try { final URL
+     * url = new URL("file://" + filename); final Image image =
+     * java.awt.Toolkit.getDefaultToolkit().getImage(url); loadImage(image); }
+     * catch (final MalformedURLException e) { throw new
+     * IsisException("Failed to load image from " + filename); } }
+     */
+    private void loadImageFromFile(final String filename) {
+        final Image image = java.awt.Toolkit.getDefaultToolkit().getImage(filename);
+        loadImage(image);
+    }
+
+    @Override
+    protected void pasteFromClipboard() {
+        final Clipboard cb = java.awt.Toolkit.getDefaultToolkit().getSystemClipboard();
+        final Transferable content = cb.getContents(this);
+
+        try {
+            if (content.isDataFlavorSupported(DataFlavor.stringFlavor)) {
+                // treat a string as a file
+                final String filename = (String) content.getTransferData(DataFlavor.stringFlavor);
+                LOG.debug("pasted image from " + filename);
+                loadImageFromFile("file://" + filename);
+
+            } else {
+                LOG.info("unsupported paste operation " + content);
+
+                // note java does not support transferring images from the
+                // clipboard
+                // although it has an image flavor for it !!?
+                /*
+                 * DataFlavor[] transferDataFlavors =
+                 * content.getTransferDataFlavors(); for (int i = 0; i <
+                 * transferDataFlavors.length; i++) {
+                 * LOG.debug("data transfer as " +
+                 * transferDataFlavors[i].getMimeType()); }
+                 * 
+                 * Image image = (Image)
+                 * content.getTransferData(DataFlavor.imageFlavor);
+                 * LOG.debug("pasted " + image);
+                 */
+
+            }
+
+        } catch (final Throwable e) {
+            LOG.error("invalid paste operation " + e);
+        }
+
+    }
+
+    @Override
+    protected void save() {
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/PasswordField.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/PasswordField.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/PasswordField.java
new file mode 100644
index 0000000..fefd02f
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/PasswordField.java
@@ -0,0 +1,188 @@
+/*
+ *  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.runtimes.dflt.runtime.system.context.IsisContext;
+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.util.Properties;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.UserActionSet;
+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;
+
+public class PasswordField extends TextField {
+    protected static final Text style = Toolkit.getText(ColorsAndFonts.TEXT_NORMAL);
+    private int maxTextWidth;
+    private char echoCharacter;
+
+    public PasswordField(final Content content, final ViewSpecification design) {
+        super((TextParseableContent) content, design, true, TextContent.NO_WRAPPING);
+        setMaxTextWidth(TEXT_WIDTH);
+        final String echoCharacterSetting = IsisContext.getConfiguration().getString(Properties.PROPERTY_BASE + "echo");
+        if (echoCharacterSetting == null || echoCharacterSetting.equals(" ")) {
+            echoCharacter = '*';
+        } else {
+            echoCharacter = echoCharacterSetting.charAt(0);
+        }
+    }
+
+    @Override
+    public void contentMenuOptions(final UserActionSet options) {
+        options.add(new ClearValueOption(this));
+        options.setColor(Toolkit.getColor(ColorsAndFonts.COLOR_MENU_VALUE));
+    }
+
+    @Override
+    protected boolean provideClearCopyPaste() {
+        return false;
+    }
+
+    /**
+     * Only allow deletion of last character, ie don;t allow editing of the
+     * internals of the password.
+     */
+    @Override
+    public void delete() {
+        textContent.deleteLeft(cursor);
+        cursor.left();
+        selection.resetTo(cursor);
+        changeMade();
+    }
+
+    /**
+     * disable left key.
+     */
+    @Override
+    protected void left(final boolean alt, final boolean shift) {
+    }
+
+    /**
+     * disable right key.
+     */
+    @Override
+    protected void right(final boolean alt, final boolean shift) {
+    }
+
+    /**
+     * disable home key.
+     */
+    @Override
+    protected void home(final boolean alt, final boolean shift) {
+    }
+
+    /**
+     * disable end key.
+     */
+    @Override
+    protected void end(final boolean alt, final boolean shift) {
+    }
+
+    /**
+     * disable page down key.
+     */
+    @Override
+    protected void pageDown(final boolean shift, final boolean ctrl) {
+    }
+
+    /**
+     * disable page up key.
+     */
+    @Override
+    protected void pageUp(final boolean shift, final boolean ctrl) {
+    }
+
+    private String echoPassword(final String password) {
+        final int length = password.length();
+        String echoedPassword = "";
+        for (int i = 0; i < length; i++) {
+            echoedPassword += echoCharacter;
+        }
+        return echoedPassword;
+    }
+
+    @Override
+    public Size getRequiredSize(final Size availableSpace) {
+        final int width = ViewConstants.HPADDING + maxTextWidth + ViewConstants.HPADDING;
+        int height = style.getTextHeight() + ViewConstants.VPADDING;
+        height = Math.max(height, Toolkit.defaultFieldHeight());
+
+        return new Size(width, height);
+    }
+
+    /**
+     * Set the maximum width of the field, as a number of characters
+     */
+    private void setMaxTextWidth(final int noCharacters) {
+        maxTextWidth = style.charWidth('o') * noCharacters;
+    }
+
+    @Override
+    protected void align() {
+    }
+
+    @Override
+    protected void drawHighlight(final Canvas canvas, final int maxWidth) {
+    }
+
+    @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("Password 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 IsisException();
+        }
+
+        final int baseline = getBaseline();
+        final String echoPassword = echoPassword(chars);
+
+        // draw cursor
+        if (hasFocus() && canChangeValue().isAllowed()) {
+            final int pos = style.stringWidth(echoPassword) - ViewConstants.HPADDING;
+            final Color color = Toolkit.getColor(ColorsAndFonts.COLOR_TEXT_CURSOR);
+            canvas.drawLine(pos, (baseline + style.getDescent()), pos, baseline - style.getAscent(), color);
+        }
+
+        // draw text
+        canvas.drawText(echoPassword, ViewConstants.HPADDING, baseline, textColor, style);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/PasswordFieldSpecification.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/PasswordFieldSpecification.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/PasswordFieldSpecification.java
new file mode 100644
index 0000000..a51dfeb
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/PasswordFieldSpecification.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.core.progmodel.facets.value.password.PasswordValueFacet;
+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;
+
+public class PasswordFieldSpecification extends AbstractFieldSpecification {
+    @Override
+    public boolean canDisplay(final ViewRequirement requirement) {
+        return requirement.isTextParseable() && requirement.isForValueType(PasswordValueFacet.class);
+    }
+
+    @Override
+    public View createView(final Content content, final Axes axes, final int sequence) {
+        return new PasswordField(content, this);
+    }
+
+    @Override
+    public String getName() {
+        return "Password Field";
+    }
+
+    @Override
+    public boolean isAligned() {
+        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/field/PasteValueOption.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/PasteValueOption.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/PasteValueOption.java
new file mode 100644
index 0000000..138ae26
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/PasteValueOption.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.dnd.field;
+
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.Workspace;
+
+public class PasteValueOption extends AbstractValueOption {
+
+    public PasteValueOption(final AbstractField field) {
+        super(field, "Paste");
+    }
+
+    @Override
+    public Consent disabled(final View view) {
+        final Consent changable = view.canChangeValue();
+        if (changable.isVetoed()) {
+            return changable;
+        } else {
+            return changable.setDescription(String.format("Replace field content with '%s' from clipboard", getClipboard(view)));
+        }
+    }
+
+    @Override
+    public void execute(final Workspace workspace, final View view, final Location at) {
+        field.pasteFromClipboard();
+    }
+
+    private String getClipboard(final View view) {
+        return (String) view.getViewManager().getClipboard(String.class);
+    }
+
+    @Override
+    public String toString() {
+        return "PasteValueOption";
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/RevertFieldOption.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/RevertFieldOption.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/RevertFieldOption.java
new file mode 100644
index 0000000..ba739de
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/RevertFieldOption.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.field;
+
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.consent.ConsentAbstract;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.view.View;
+import org.apache.isis.viewer.dnd.view.Workspace;
+import org.apache.isis.viewer.dnd.view.option.UserActionAbstract;
+
+public class RevertFieldOption extends UserActionAbstract {
+    private final TextField field;
+
+    public RevertFieldOption(final TextField field) {
+        super("Revert");
+        this.field = field;
+    }
+
+    @Override
+    public String getDescription(final View view) {
+        return "Revert the field to it original state";
+    }
+
+    @Override
+    public Consent disabled(final View view) {
+        return ConsentAbstract.allowIf(field.hasInvalidEntry());
+    }
+
+    @Override
+    public void execute(final Workspace workspace, final View view, final Location at) {
+        field.revertInvalidEntry();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/eb613703/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/SimpleDatePicker.java
----------------------------------------------------------------------
diff --git a/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/SimpleDatePicker.java b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/SimpleDatePicker.java
new file mode 100644
index 0000000..46f6408
--- /dev/null
+++ b/component/viewer/dnd/impl/src/main/java/org/apache/isis/viewer/dnd/field/SimpleDatePicker.java
@@ -0,0 +1,392 @@
+/*
+ *  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.KeyEvent;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.progmodel.facets.value.date.DateValueFacet;
+import org.apache.isis.viewer.dnd.drawing.Canvas;
+import org.apache.isis.viewer.dnd.drawing.Color;
+import org.apache.isis.viewer.dnd.drawing.ColorsAndFonts;
+import org.apache.isis.viewer.dnd.drawing.Location;
+import org.apache.isis.viewer.dnd.drawing.Shape;
+import org.apache.isis.viewer.dnd.drawing.Size;
+import org.apache.isis.viewer.dnd.drawing.Text;
+import org.apache.isis.viewer.dnd.view.Click;
+import org.apache.isis.viewer.dnd.view.Content;
+import org.apache.isis.viewer.dnd.view.KeyboardAction;
+import org.apache.isis.viewer.dnd.view.Toolkit;
+import org.apache.isis.viewer.dnd.view.base.AbstractView;
+import org.apache.isis.viewer.dnd.view.content.FieldContent;
+import org.apache.isis.viewer.dnd.view.content.TextParseableContent;
+
+public class SimpleDatePicker extends AbstractView implements DatePicker {
+    private static class Button {
+        private final char key;
+        private final int period;
+        private final int increment;
+
+        public Button(final char key, final int period, final int increment) {
+            this.key = key;
+            this.period = period;
+            this.increment = increment;
+        }
+
+        public void adjustDate(final Calendar date) {
+            date.add(period, increment);
+        }
+
+        public String getLabel() {
+            return "" + key;
+        }
+    }
+
+    private static final Button[] buttons = new Button[] { new Button('W', Calendar.WEEK_OF_YEAR, 1), new Button('F', Calendar.WEEK_OF_YEAR, 2), new Button('M', Calendar.MONTH, 1), new Button('Q', Calendar.MONTH, 3), new Button('Y', Calendar.YEAR, 1), new Button('D', Calendar.YEAR, 10),
+
+    new Button('w', Calendar.WEEK_OF_YEAR, -1), new Button('f', Calendar.WEEK_OF_YEAR, -2), new Button('m', Calendar.MONTH, -1), new Button('q', Calendar.MONTH, -3), new Button('y', Calendar.YEAR, -1), new Button('d', Calendar.YEAR, -10) };
+    private static final int ROWS = 7;
+    private static final int COLUMNS = 7;
+    private static final int PADDING = 5;
+    private final Calendar date;
+    private final Text style = Toolkit.getText(ColorsAndFonts.TEXT_NORMAL);
+    private final int labelWidth = style.stringWidth("XXX 0000") * 4 / 3;
+    private final int cellWidth = style.stringWidth("00") * 8 / 5;
+    private final int cellHeight = style.getLineHeight() * 4 / 3;
+    private final int headerHeight = style.getLineHeight() + PADDING * 2;
+    private final int firstRowBaseline = headerHeight + style.getLineHeight();
+    private final int calendarHeight = cellHeight * ROWS;
+    private final int controlWidth = style.charWidth('W') + 4;
+    private final int controlHeight = style.getLineHeight() * 11 / 10 + controlWidth + 4 + PADDING * 2;
+    private int mouseOverButton = -1;
+    private int mouseOverRow = -1;
+    private int mouseOverColumn;
+    private Calendar currentDate;
+    private final Calendar today;
+    private final DateFormat monthFormat = new SimpleDateFormat("MMM");
+    private final DateFormat dayFormat = new SimpleDateFormat("EEE");
+    private final boolean isEditable;
+
+    public SimpleDatePicker(final Content content) {
+        super(content);
+
+        isEditable = content instanceof FieldContent && ((FieldContent) content).isEditable().isAllowed();
+
+        today = Calendar.getInstance();
+        clearTime(today);
+
+        date = Calendar.getInstance();
+        final ObjectAdapter dateAdapter = ((TextParseableContent) getContent()).getAdapter();
+        if (dateAdapter != null) {
+            final DateValueFacet facet = dateAdapter.getSpecification().getFacet(DateValueFacet.class);
+            currentDate = Calendar.getInstance();
+            final Date dateValue = facet.dateValue(dateAdapter);
+            currentDate.setTime(dateValue);
+            clearTime(currentDate);
+            date.setTime(dateValue);
+        }
+        clearTime(date);
+        date.add(Calendar.DAY_OF_MONTH, -21);
+        roundDownDate();
+    }
+
+    private void roundDownDate() {
+        date.add(Calendar.DAY_OF_MONTH, date.getFirstDayOfWeek() - date.get(Calendar.DAY_OF_WEEK));
+    }
+
+    private void clearTime(final Calendar date) {
+        date.clear(Calendar.AM_PM);
+        date.clear(Calendar.HOUR);
+        date.clear(Calendar.HOUR_OF_DAY);
+        date.clear(Calendar.MINUTE);
+        date.clear(Calendar.SECOND);
+        date.clear(Calendar.MILLISECOND);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.nakedobjects.plugins.dnd.field.DatePicker#getRequiredSize(org.
+     * nakedobjects.plugins.dnd.drawing.Size)
+     */
+    @Override
+    public Size getRequiredSize(final Size availableSpace) {
+        return new Size(labelWidth + COLUMNS * cellWidth + 2, headerHeight + calendarHeight + controlHeight + 2);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.nakedobjects.plugins.dnd.field.DatePicker#draw(org.nakedobjects.plugins
+     * .dnd.drawing.Canvas)
+     */
+    @Override
+    public void draw(final Canvas canvas) {
+        final int width = getSize().getWidth();
+        final int height = getSize().getHeight();
+
+        final Color secondaryTextColor = Toolkit.getColor(ColorsAndFonts.COLOR_WHITE);
+        final Color mainTextColor = Toolkit.getColor(ColorsAndFonts.COLOR_BLACK);
+
+        drawBackground(canvas, width, height);
+        drawDaysOfWeek(canvas, secondaryTextColor);
+        if (isEditable) {
+            drawDayMarker(canvas);
+        }
+        drawMonthsAndWeeks(canvas, secondaryTextColor);
+        drawDays(canvas, mainTextColor);
+        drawControls(canvas, width);
+    }
+
+    private void drawBackground(final Canvas canvas, final int width, final int height) {
+        canvas.drawSolidRectangle(0, 0, width - 1, headerHeight, Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY1));
+        canvas.drawSolidRectangle(0, 0, labelWidth, height - 1, Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY1));
+        canvas.drawSolidRectangle(labelWidth, headerHeight, width - labelWidth - 1, height - cellHeight - 1, Toolkit.getColor(ColorsAndFonts.COLOR_WINDOW));
+        canvas.drawRectangle(0, 0, width - 1, height - 1, Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY1));
+    }
+
+    private void drawDaysOfWeek(final Canvas canvas, final Color textColor) {
+        final Calendar d = Calendar.getInstance();
+        d.setTime(date.getTime());
+        int x = labelWidth + cellWidth / 2;
+        final int y = PADDING + style.getAscent();
+        for (int column = 0; column < 7; column++) {
+            final String day = dayFormat.format(d.getTime()).substring(0, 1);
+            canvas.drawText(day, x - style.stringWidth(day) / 2, y, textColor, style);
+            x += cellWidth;
+            d.add(Calendar.DAY_OF_MONTH, 1);
+        }
+    }
+
+    private void drawDayMarker(final Canvas canvas) {
+        if (mouseOverColumn >= 0 && mouseOverColumn < COLUMNS && mouseOverRow >= 0 && mouseOverRow < ROWS) {
+            canvas.drawRectangle(labelWidth + mouseOverColumn * cellWidth, headerHeight + mouseOverRow * cellHeight, cellWidth, cellHeight, Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY3));
+        }
+    }
+
+    private int drawMonthsAndWeeks(final Canvas canvas, final Color color) {
+        final Calendar d = Calendar.getInstance();
+        d.setTime(date.getTime());
+        int y = firstRowBaseline;
+        String lastMonth = "";
+        for (int row = 0; row < ROWS; row++) {
+            final int x = labelWidth;
+            final String month = monthFormat.format(d.getTime()) + " " + d.get(Calendar.YEAR);
+            if (!month.equals(lastMonth)) {
+                canvas.drawText(month, x - style.stringWidth(month) - PADDING, y, color, style);
+                lastMonth = month;
+            } else {
+                final String week = "wk " + (d.get(Calendar.WEEK_OF_YEAR));
+                canvas.drawText(week == null ? "*" : week, x - style.stringWidth(week) - PADDING, y, color, style);
+            }
+            d.add(Calendar.DAY_OF_MONTH, 7);
+            y += cellHeight;
+        }
+        return y;
+    }
+
+    private int drawDays(final Canvas canvas, final Color mainTextColor) {
+        final Calendar d = Calendar.getInstance();
+        d.setTime(date.getTime());
+        int y = firstRowBaseline;
+        for (int row = 0; row < ROWS; row++) {
+            int x = labelWidth;
+            for (int column = 0; column < COLUMNS; column++) {
+                final String day = "" + d.get(Calendar.DAY_OF_MONTH);
+                if (currentDate != null && currentDate.equals(d)) {
+                    canvas.drawSolidRectangle(x, headerHeight + row * cellHeight, cellWidth, cellHeight, Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY2));
+                }
+                if (today.getTime().equals(d.getTime())) {
+                    canvas.drawRectangle(x, headerHeight + row * cellHeight, cellWidth, cellHeight, Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY2));
+                }
+                canvas.drawText(day, x - PADDING / 2 + cellWidth - style.stringWidth(day), y, mainTextColor, style);
+                x += cellWidth;
+                d.add(Calendar.DAY_OF_MONTH, 1);
+            }
+            y += cellHeight;
+        }
+        return y;
+    }
+
+    private void drawControls(final Canvas canvas, final int width) {
+        int x = labelWidth + PADDING;
+        final int y = headerHeight + calendarHeight + PADDING;
+
+        final int spaceTaken = width - labelWidth - 2 * PADDING - 6 * controlWidth;
+        final int spaceBetween = spaceTaken / 5;
+        final int adjustment = spaceTaken - 5 * spaceBetween - 2;
+
+        for (int i = 0; i < buttons.length / 2; i++) {
+            drawControl(canvas, x, y, controlWidth, controlHeight - PADDING * 2, buttons[i].getLabel(), i);
+            x += controlWidth + spaceBetween + (i == 3 ? adjustment : 0);
+        }
+    }
+
+    private void drawControl(final Canvas canvas, final int x, final int y, final int width, final int height, final String label, final int over) {
+        if (Toolkit.debug) {
+            canvas.drawRectangle(x - 2, y, width + 4, height, Toolkit.getColor(ColorsAndFonts.COLOR_DEBUG_BOUNDS_VIEW));
+        }
+        final Color color = Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY2);
+
+        final int arrowHeight = width / 2;
+        final Shape upArrow = new Shape(0, arrowHeight);
+        upArrow.addVector(arrowHeight, -arrowHeight);
+        upArrow.addVector(arrowHeight, arrowHeight);
+        if (mouseOverButton == over + 6) {
+            canvas.drawSolidShape(upArrow, x, y + 2, color);
+        } else {
+            canvas.drawShape(upArrow, x, y + 2, color);
+        }
+
+        final Shape downArrow = new Shape(0, 0);
+        downArrow.addVector(arrowHeight, arrowHeight);
+        downArrow.addVector(arrowHeight, -arrowHeight);
+        if (mouseOverButton == over) {
+            canvas.drawSolidShape(downArrow, x, y + height - 4 - arrowHeight, color);
+        } else {
+            canvas.drawShape(downArrow, x, y + height - 4 - arrowHeight, color);
+        }
+        final int charWidth = style.stringWidth(label);
+        canvas.drawText(label, x + width / 2 - charWidth / 2, y + 2 + arrowHeight + style.getAscent(), color, style);
+
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.nakedobjects.plugins.dnd.field.DatePicker#mouseMoved(org.nakedobjects
+     * .plugins.dnd.drawing.Location)
+     */
+    @Override
+    public void mouseMoved(final Location location) {
+        final int over = overButton(location);
+        if (over == -1) {
+            mouseOverButton = -1;
+            final int col = column(location);
+            final int row = row(location);
+            if (col != mouseOverColumn || row != mouseOverRow) {
+                if (isEditable) {
+                    mouseOverColumn = col;
+                    mouseOverRow = row;
+                }
+                markDamaged();
+            }
+        } else if (over != mouseOverButton) {
+            mouseOverButton = over;
+            markDamaged();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.nakedobjects.plugins.dnd.field.DatePicker#exited()
+     */
+    @Override
+    public void exited() {
+        mouseOverButton = -1;
+        super.exited();
+    }
+
+    private int overButton(final Location location) {
+        final int x = location.getX();
+        final int y = location.getY();
+        final int verticalBoundary = headerHeight + calendarHeight + PADDING;
+        final int height = controlHeight - PADDING * 2;
+        if (x > labelWidth && y > verticalBoundary && y < verticalBoundary + height) {
+            int column = (x - labelWidth) / ((getSize().getWidth() - labelWidth) / 6);
+            if (y <= verticalBoundary + height / 2) {
+                column += 6;
+            }
+            return column;
+        } else {
+            return -1;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.nakedobjects.plugins.dnd.field.DatePicker#firstClick(org.nakedobjects
+     * .plugins.dnd.view.Click)
+     */
+    @Override
+    public void firstClick(final Click click) {
+        if (mouseOverButton != -1) {
+            buttons[mouseOverButton].adjustDate(date);
+            roundDownDate();
+            markDamaged();
+            return;
+        }
+
+        if (isEditable) {
+            final Location location = click.getLocation();
+            final int col = column(location);
+            final int row = row(location);
+            if (col >= 0 && col < COLUMNS && row >= 0 && row < ROWS) {
+                date.add(Calendar.DAY_OF_MONTH, row * 7 + col);
+                final Content content = getContent();
+                final DateValueFacet facet = content.getSpecification().getFacet(DateValueFacet.class);
+                final ObjectAdapter value = facet.createValue(date.getTime());
+                ((TextParseableContent) content).parseTextEntry(value.titleString());
+                ((TextParseableContent) content).entryComplete();
+                /*
+                 * if (content.isObject()) { ((ObjectContent)
+                 * content).setObject(value); }
+                 */
+                // content.
+                getView().refresh();
+
+                // content.
+            }
+        }
+        getViewManager().clearOverlayView();
+    }
+
+    private int row(final Location location) {
+        return (location.getY() - headerHeight) / cellHeight;
+    }
+
+    private int column(final Location location) {
+        return (location.getX() - labelWidth) / cellWidth;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.nakedobjects.plugins.dnd.field.DatePicker#keyPressed(org.nakedobjects
+     * .plugins.dnd.view.KeyboardAction)
+     */
+    @Override
+    public void keyPressed(final KeyboardAction key) {
+        if (isEditable && key.getKeyCode() == KeyEvent.VK_ESCAPE) {
+            getViewManager().clearOverlayView();
+        } else {
+            super.keyPressed(key);
+        }
+    }
+}