package com.vividsolutions.jump.workbench.ui.cursortool;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.util.Assert;
import com.vividsolutions.jump.I18N;
import com.vividsolutions.jump.feature.AttributeType;
import com.vividsolutions.jump.feature.BasicFeature;
import com.vividsolutions.jump.feature.Feature;
import com.vividsolutions.jump.feature.FeatureDataset;
import com.vividsolutions.jump.feature.FeatureSchema;
import com.vividsolutions.jump.workbench.model.FeatureEventType;
import com.vividsolutions.jump.workbench.model.Layer;
import com.vividsolutions.jump.workbench.model.LayerManager;
import com.vividsolutions.jump.workbench.model.StandardCategoryNames;
import com.vividsolutions.jump.workbench.model.UndoableCommand;
import com.vividsolutions.jump.workbench.ui.EditTransaction;
import com.vividsolutions.jump.workbench.ui.GUIUtil;
import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
import com.vividsolutions.jump.workbench.ui.Viewport;
import com.vividsolutions.jump.workbench.ui.images.IconLoader;
import com.vividsolutions.jump.workbench.ui.renderer.style.Style;
import com.vividsolutions.jump.workbench.ui.snap.SnapToFeaturesPolicy;
//Notes layer can now be saved with a task [LDB 12-15-2005]
public class NoteTool extends NClickTool {
public static final String NOTES = I18N.get("ui.cursortool.NoteTool.notes");
// Create DEFAULT_SCHEMA as a static attribute to be sure the schema is
// initialized from NoteStyle class with internationalized attributes
private static final FeatureSchema DEFAULT_SCHEMA = NoteStyle.createFeatureSchema();
private LayerViewPanel panel;
private abstract class Mode {
private Feature noteFeature;
public Mode(Feature noteFeature) {
this.noteFeature = noteFeature;
}
public Coordinate location() {
return noteFeature.getGeometry().getCoordinate();
}
public abstract void commit(String text);
protected Feature getNoteFeature() {
return noteFeature;
}
public abstract String initialText();
}
private class CreateMode extends Mode {
public CreateMode(final Coordinate location) {
super(new BasicFeature(layer().getFeatureCollectionWrapper()
.getFeatureSchema()) {
{
setAttribute(NoteStyle.CREATED, new Date());
setAttribute(NoteStyle.GEOMETRY, new GeometryFactory()
.createPoint(location));
}
});
}
public void commit(String text) {
if (text.length() > 0) {
disableAutomaticInitialZooming();
getNoteFeature().setAttribute(NoteStyle.MODIFIED, new Date());
getNoteFeature().setAttribute(NoteStyle.TEXT, text);
EditTransaction transaction = new EditTransaction(
Collections.EMPTY_LIST, getName(), layer(),
isRollingBackInvalidEdits(), true, getPanel());
transaction.createFeature(getNoteFeature());
transaction.commit();
}
}
public String initialText() {
return "";
}
}
private class EditMode extends Mode {
public EditMode(Feature noteFeature) {
super(noteFeature);
}
public void commit(final String text) {
final Date modifiedDate = new Date();
final Date oldModifiedDate = (Date) getNoteFeature().getAttribute(
NoteStyle.MODIFIED);
final String oldText = getNoteFeature().getString(NoteStyle.TEXT);
execute(new UndoableCommand(getName()) {
public void execute() {
update(getNoteFeature(), text, modifiedDate, layer());
}
public void unexecute() {
update(getNoteFeature(), oldText, oldModifiedDate, layer());
}
});
}
private void update(Feature noteFeature, String text,
Date modifiedDate, Layer layer) {
noteFeature.setAttribute(NoteStyle.MODIFIED, modifiedDate);
noteFeature.setAttribute(NoteStyle.TEXT, text);
layer.getLayerManager().fireFeaturesChanged(
Collections.singleton(noteFeature),
FeatureEventType.ATTRIBUTES_MODIFIED, layer);
}
public String initialText() {
return getNoteFeature().getString(NoteStyle.TEXT);
}
}
public NoteTool() {
super(1);
panel = getPanel();
getSnapManager().addPolicies(
Collections.singleton(new SnapToFeaturesPolicy()));
textArea = NoteStyle.createTextArea();
textArea.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent e) {
if (panelContainsTextArea())
{
boolean doit = (textArea.getText().trim().length() > 0);
if (doit) getPanel().getLayerManager().getUndoableEditReceiver().startReceiving();
removeTextAreaFromPanel();
if (doit) getPanel().getLayerManager().getUndoableEditReceiver().stopReceiving(); }
}
});
}
public String getName() {
return I18N.get("ui.cursortool.NoteTool");
}
private JTextArea textArea;
private Mode mode;
public void deactivate()
{
if (panelContainsTextArea())
{
boolean doit = (textArea.getText().trim().length() > 0);
if (doit) getPanel().getLayerManager().getUndoableEditReceiver().startReceiving();
removeTextAreaFromPanel();
if (doit) getPanel().getLayerManager().getUndoableEditReceiver().stopReceiving();
}
super.deactivate();
}
public Cursor getCursor() {
return cursor;
}
protected void gestureFinished() throws Exception {
reportNothingToUndoYet();
Feature noteFeatureAtClick = noteFeature(getModelDestination());
removeTextAreaFromPanel();
mode = mode(noteFeatureAtClick, getModelDestination());
//Since focusLost will be called after gestureFinished() is called
//any testArea added here would be removed.
//However the invokeLater will be called after any other calls to removeTextAreaFromPanel()
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
addTextAreaToPanel(mode.location());
} catch (NoninvertibleTransformException e) {
getPanel().getContext().handleThrowable(e);
}
}
});
}
private Mode mode(Feature noteFeatureAtClick, Coordinate click) {
return noteFeatureAtClick == null ? (Mode) new CreateMode(click)
: new EditMode(noteFeatureAtClick);
}
private Feature noteFeature(Coordinate click) {
return noteFeature(new Envelope(click, new Coordinate(click.x - NoteStyle.WIDTH
/ scale(), click.y + NoteStyle.HEIGHT / scale())));
}
private Feature noteFeature(Envelope envelope) {
return (Feature) firstOrNull(layer().getFeatureCollectionWrapper()
.query(envelope));
}
private Object firstOrNull(Collection items) {
return !items.isEmpty() ? items.iterator().next() : null;
}
private double scale() {
return getPanel().getViewport().getScale();
}
private void addTextAreaToPanel(Coordinate location)
throws NoninvertibleTransformException {
layer().setVisible(true);
if (getPanel().getLayout() != null) {
getPanel().setLayout(null);
}
textArea.setText(mode.initialText());
textArea.setBackground(layer().getBasicStyle().getFillColor());
getPanel().add(textArea);
textArea.addCaretListener(new CaretListener()
{
public void caretUpdate(CaretEvent e)
{
JTextArea textArea = (JTextArea)e.getSource();
int ht = textArea.getPreferredSize().height;
int wt = textArea.getPreferredSize().width;
if (ht < NoteStyle.HEIGHT) ht = NoteStyle.HEIGHT;
if (wt < NoteStyle.WIDTH) wt = NoteStyle.WIDTH;
int x = textArea.getBounds().x;
int y = textArea.getBounds().y;
textArea.setBounds(x, y, wt, ht);
}
});
int ht = textArea.getPreferredSize().height;
int wt = textArea.getPreferredSize().width;
if (ht < NoteStyle.HEIGHT) ht = NoteStyle.HEIGHT;
if (wt < NoteStyle.WIDTH) wt = NoteStyle.WIDTH;
textArea.setBounds((int) getPanel().getViewport().toViewPoint(location)
.getX(), (int) getPanel().getViewport().toViewPoint(location)
.getY(), wt, ht);
//textArea.setBounds((int) getPanel().getViewport().toViewPoint(location)
// .getX(), (int) getPanel().getViewport().toViewPoint(location)
// .getY(), NoteStyle.WIDTH, NoteStyle.HEIGHT);
textArea.requestFocus();
}
private boolean panelContainsTextArea() {
return Arrays.asList(getPanel().getComponents()).contains(textArea);
}
private void removeTextAreaFromPanel() {
if (!panelContainsTextArea()) {return;}
mode.commit(textArea.getText().trim());
getPanel().remove(textArea);
getPanel().superRepaint();
}
private void disableAutomaticInitialZooming() {
getPanel().setViewportInitialized(true);
}
public Layer layer() {
LayerManager layerManager = getPanel().getLayerManager();
if (layerManager.getLayer(NOTES) != null) {
return layerManager.getLayer(NOTES);
}
Layer noteLayer = new Layer(NOTES, Color.yellow.brighter().brighter(),
//new FeatureDataset(NoteStyle.createFeatureSchema()), layerManager);
new FeatureDataset(DEFAULT_SCHEMA), layerManager);
boolean firingEvents = layerManager.isFiringEvents();
layerManager.setFiringEvents(false);
try {
noteLayer.addStyle(new NoteStyle());
} finally {
layerManager.setFiringEvents(firingEvents);
}
layerManager.addLayer(StandardCategoryNames.SYSTEM, noteLayer);
return noteLayer;
}
public Icon getIcon() {
return icon;
}
protected Shape getShape() throws NoninvertibleTransformException {
return null;
}
private ImageIcon icon = IconLoader.icon("sticky.png");
private Cursor cursor = GUIUtil.createCursorFromIcon(icon.getImage());
}