/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.fge.controller;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceContext;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;
import java.util.List;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import org.openflexo.fge.Drawing;
import org.openflexo.fge.DrawingGraphicalRepresentation;
import org.openflexo.fge.GraphicalRepresentation;
import org.openflexo.fge.ShapeGraphicalRepresentation.LocationConstraints;
import org.openflexo.fge.controller.PaletteElement.PaletteElementTransferable;
import org.openflexo.fge.controller.PaletteElement.TransferedPaletteElement;
import org.openflexo.fge.geom.FGEPoint;
import org.openflexo.fge.view.DrawingView;
import org.openflexo.fge.view.FGEView;
import org.openflexo.fge.view.listener.FocusRetriever;
import org.openflexo.fib.utils.FIBIconLibrary;
import org.openflexo.toolbox.ToolBox;
public class DrawingPalette {
private static final Logger logger = Logger.getLogger(DrawingPalette.class.getPackage().getName());
private static Image DROP_OK_IMAGE = FIBIconLibrary.DROP_OK_CURSOR.getImage();
private static Image DROP_KO_IMAGE = FIBIconLibrary.DROP_KO_CURSOR.getImage();
public static final Cursor dropOK = ToolBox.getPLATFORM() == ToolBox.MACOS ? Toolkit.getDefaultToolkit().createCustomCursor(
DROP_OK_IMAGE, new Point(16, 16), "Drop OK") : DragSource.DefaultMoveDrop;
public static final Cursor dropKO = ToolBox.getPLATFORM() == ToolBox.MACOS ? Toolkit.getDefaultToolkit().createCustomCursor(
DROP_KO_IMAGE, new Point(16, 16), "Drop KO") : DragSource.DefaultMoveNoDrop;
private DrawingController<?> _controller;
private final PaletteDrawing _paletteDrawing;
// This controller is the local controller for displaying the palette, NOT the controller
// Which this palette is associated to.
private DrawingController<PaletteDrawing> _paletteController;
protected Vector<PaletteElement> elements;
private DragSourceContext dragSourceContext;
private final int width;
private final int height;
private final String title;
public DrawingPalette(int width, int height, String title) {
this.width = width;
this.height = height;
this.title = title;
elements = new Vector<PaletteElement>();
_paletteDrawing = new PaletteDrawing();
if (logger.isLoggable(Level.FINE)) {
logger.fine("Build palette " + title + " " + Integer.toHexString(hashCode()) + " of " + getClass().getName());
}
}
public void delete() {
_paletteController.delete();
for (PaletteElement element : elements) {
element.getGraphicalRepresentation().delete();
}
_paletteDrawing.getDrawingGraphicalRepresentation().delete();
elements = null;
}
public String getTitle() {
return title;
}
public void addElement(PaletteElement element) {
elements.add(element);
// Try to perform some checks and initialization of
// expecting behaviour for a PaletteElement
element.getGraphicalRepresentation().setIsFocusable(false);
element.getGraphicalRepresentation().setIsSelectable(false);
element.getGraphicalRepresentation().setIsReadOnly(true);
element.getGraphicalRepresentation().setLocationConstraints(LocationConstraints.UNMOVABLE);
// element.getGraphicalRepresentation().addToMouseDragControls(mouseDragControl)
}
public void removeElement(PaletteElement element) {
elements.remove(element);
}
public DrawingView<PaletteDrawing> getPaletteView() {
if (_paletteController == null) {
makePalettePanel();
}
return _paletteController.getDrawingView();
}
private JScrollPane scrollPane;
public JScrollPane getPaletteViewInScrollPane() {
if (scrollPane == null) {
scrollPane = new JScrollPane(getPaletteView(), ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
return scrollPane;
}
public PaletteDrawing getPaletteDrawing() {
return _paletteDrawing;
}
protected void makePalettePanel() {
for (PaletteElement e : elements) {
e.getGraphicalRepresentation().setValidated(true);
}
_paletteController = new DrawingController<PaletteDrawing>(_paletteDrawing);
for (PaletteElement e : elements) {
e.getGraphicalRepresentation().notifyObjectHierarchyHasBeenUpdated();
}
}
public class PaletteDrawing implements Drawing<DrawingPalette> {
private final DrawingGraphicalRepresentation<DrawingPalette> gr;
private PaletteDrawing() {
gr = new DrawingGraphicalRepresentation<DrawingPalette>(this, false);
gr.setWidth(width);
gr.setHeight(height);
gr.setDrawWorkingArea(false);
}
@Override
public List<?> getContainedObjects(Object aDrawable) {
if (aDrawable == getModel()) {
return elements;
} else {
return null;
}
}
@Override
public Object getContainer(Object aDrawable) {
if (aDrawable instanceof PaletteElement) {
return getModel();
} else {
return null;
}
}
@Override
public DrawingGraphicalRepresentation<DrawingPalette> getDrawingGraphicalRepresentation() {
return gr;
}
@Override
@SuppressWarnings("unchecked")
public GraphicalRepresentation<?> getGraphicalRepresentation(Object aDrawable) {
if (aDrawable == getModel()) {
return getDrawingGraphicalRepresentation();
}
if (aDrawable instanceof PaletteElement) {
return ((PaletteElement) aDrawable).getGraphicalRepresentation();
}
return null;
}
@Override
public DrawingPalette getModel() {
return DrawingPalette.this;
}
@Override
public boolean isEditable() {
return false;
}
}
// Bout de code a rajouter dans les vues
/*
this.setDropTarget(new DropTarget(this, DnDConstants.ACTION_COPY, new WKFDTListener(this, controller), true){
@Override
public synchronized void dragOver(DropTargetDragEvent dtde) {
super.dragOver(dtde);
FlexoProcessView.this.getController().paintDraggedNode(FlexoProcessView.this, dtde);
}
});*/
public PaletteDropListener buildPaletteDropListener(JComponent dropContainer, DrawingController controller) {
return new PaletteDropListener(dropContainer, controller);
}
/**
* DTListener a listener that tracks the state of the operation
*
* @see java.awt.dnd.DropTargetListener
* @see java.awt.dnd.DropTarget
*/
public class PaletteDropListener implements DropTargetListener {
private final int acceptableActions = DnDConstants.ACTION_COPY;
private final JComponent _dropContainer;
private final DrawingController _controller;
public PaletteDropListener(JComponent dropContainer, DrawingController controller) {
super();
_dropContainer = dropContainer;
_controller = controller;
}
/**
* Called by isDragOk Checks to see if the flavor drag flavor is acceptable
*
* @param e
* the DropTargetDragEvent object
* @return whether the flavor is acceptable
*/
private boolean isDragFlavorSupported(DropTargetDragEvent e) {
boolean ok = false;
if (e.isDataFlavorSupported(PaletteElementTransferable.defaultFlavor())) {
ok = true;
}
return ok;
}
/**
* Called by drop Checks the flavors and operations
*
* @param e
* the DropTargetDropEvent object
* @return the chosen DataFlavor or null if none match
*/
private DataFlavor chooseDropFlavor(DropTargetDropEvent e) {
if (e.isLocalTransfer() == true && e.isDataFlavorSupported(PaletteElementTransferable.defaultFlavor())) {
return PaletteElementTransferable.defaultFlavor();
}
return null;
}
/**
* Called by dragEnter and dragOver Checks the flavors and operations
*
* @param e
* the event object
* @return whether the flavor and operation is ok
*/
private boolean isDragOk(DropTargetDragEvent e) {
if (isDragFlavorSupported(e) == false) {
return false;
}
int da = e.getDropAction();
// we're saying that these actions are necessary
if ((da & acceptableActions) == 0) {
return false;
}
try {
PaletteElement element = ((TransferedPaletteElement) e.getTransferable().getTransferData(
PaletteElementTransferable.defaultFlavor())).getPaletteElement();
if (element == null) {
return false;
}
GraphicalRepresentation<?> focused = getFocusedObject(e);
if (focused == null) {
return false;
}
return element.acceptDragging(focused);
} catch (UnsupportedFlavorException e1) {
logger.warning("Unexpected: " + e1);
e1.printStackTrace();
return false;
} catch (IOException e1) {
logger.warning("Unexpected: " + e1);
e1.printStackTrace();
return false;
} catch (Exception e1) {
logger.warning("Unexpected: " + e1);
e1.printStackTrace();
return false;
}
}
/**
* start "drag under" feedback on component invoke acceptDrag or rejectDrag based on isDragOk
*
* @param e
*/
@Override
public void dragEnter(DropTargetDragEvent e) {
if (!isDragOk(e)) {
// DropLabel.this.borderColor=Color.red;
// showBorder(true);
e.rejectDrag();
return;
}
e.acceptDrag(e.getDropAction());
}
/**
* continue "drag under" feedback on component invoke acceptDrag or rejectDrag based on isDragOk
*
* @param e
*/
@Override
public void dragOver(DropTargetDragEvent e) {
if (isDragFlavorSupported(e)) {
getController().getDrawingView().updateCapturedDraggedNodeImagePosition(e,
_controller.getDrawingView().getActivePalette().getPaletteView());
}
if (!isDragOk(e)) {
if (getDragSourceContext() == null) {
logger.warning("dragSourceContext should NOT be null for " + DrawingPalette.this.getTitle()
+ Integer.toHexString(DrawingPalette.this.hashCode()) + " of " + DrawingPalette.this.getClass().getName());
} else {
getDragSourceContext().setCursor(dropKO);
}
e.rejectDrag();
return;
}
if (getDragSourceContext() == null) {
logger.warning("dragSourceContext should NOT be null");
} else {
getDragSourceContext().setCursor(dropOK);
}
e.acceptDrag(e.getDropAction());
}
@Override
public void dropActionChanged(DropTargetDragEvent e) {
if (!isDragOk(e)) {
e.rejectDrag();
return;
}
e.acceptDrag(e.getDropAction());
}
@Override
public void dragExit(DropTargetEvent e) {
// interface method
getController().getDrawingView().resetCapturedNode();
}
/**
* perform action from getSourceActions on the transferrable invoke acceptDrop or rejectDrop invoke dropComplete if its a local
* (same JVM) transfer, use StringTransferable.localStringFlavor find a match for the flavor check the operation get the
* transferable according to the chosen flavor do the transfer
*
* @param e
*/
@Override
public void drop(DropTargetDropEvent e) {
try {
DataFlavor chosen = chooseDropFlavor(e);
if (chosen == null) {
e.rejectDrop();
return;
}
// the actions that the source has specified with DragGestureRecognizer
int sa = e.getSourceActions();
if ((sa & acceptableActions) == 0) {
e.rejectDrop();
return;
}
Object data = null;
try {
/*
* the source listener receives this action in dragDropEnd. if the
* action is DnDConstants.ACTION_COPY_OR_MOVE then the source
* receives MOVE!
*/
data = e.getTransferable().getTransferData(chosen);
if (logger.isLoggable(Level.FINE)) {
logger.fine("data is a " + data.getClass().getName());
}
if (data == null) {
throw new NullPointerException();
}
} catch (Throwable t) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Couldn't get transfer data: " + t.getMessage());
}
t.printStackTrace();
e.dropComplete(false);
return;
}
if (data instanceof TransferedPaletteElement) {
try {
PaletteElement element = ((TransferedPaletteElement) data).getPaletteElement();
if (element == null) {
e.rejectDrop();
return;
}
GraphicalRepresentation<?> focused = getFocusedObject(e);
if (focused == null) {
e.rejectDrop();
return;
}
// OK, let's got for the drop
if (element.acceptDragging(focused)) {
Component targetComponent = e.getDropTargetContext().getComponent();
Point pt = e.getLocation();
FGEPoint modelLocation = new FGEPoint();
if (targetComponent instanceof FGEView) {
pt = GraphicalRepresentation.convertPoint(((FGEView<?>) targetComponent).getGraphicalRepresentation(), pt,
focused, ((FGEView<?>) targetComponent).getScale());
modelLocation.x = pt.x / ((FGEView<?>) targetComponent).getScale();
modelLocation.y = pt.y / ((FGEView<?>) targetComponent).getScale();
modelLocation.x -= ((TransferedPaletteElement) data).getOffset().x;
modelLocation.y -= ((TransferedPaletteElement) data).getOffset().y;
} else {
modelLocation.x -= ((TransferedPaletteElement) data).getOffset().x;
modelLocation.y -= ((TransferedPaletteElement) data).getOffset().y;
}
if (element.elementDragged(focused, modelLocation)) {
e.acceptDrop(acceptableActions);
e.dropComplete(true);
// logger.info("OK, valid drop, proceed");
return;
} else {
e.rejectDrop();
e.dropComplete(false);
return;
}
}
} catch (Exception e1) {
logger.warning("Unexpected: " + e1);
e1.printStackTrace();
e.rejectDrop();
e.dropComplete(false);
return;
}
}
e.rejectDrop();
e.dropComplete(false);
return;
} finally {
// Resets the screenshot stored by the editable drawing view (not the palette drawing view).
getController().getDrawingView().resetCapturedNode();
}
}
private FocusRetriever getFocusRetriever() {
if (_dropContainer instanceof FGEView) {
return ((FGEView) _dropContainer).getDrawingView().getFocusRetriever();
}
return null;
}
private FGEView getFGEView() {
if (_dropContainer instanceof FGEView) {
return (FGEView) _dropContainer;
}
return null;
}
public GraphicalRepresentation<?> getFocusedObject(DropTargetDragEvent event) {
if (getFocusRetriever() != null) {
GraphicalRepresentation<?> returned = getFocusRetriever().getFocusedObject(event);
if (returned == null) {
// Since we are in a FGEView, a null value indicates that we are on the Drawing view
return getFGEView().getGraphicalRepresentation().getDrawingGraphicalRepresentation();
}
return returned;
}
// No focus retriever: we are not in a FGEView....
return null;
}
public GraphicalRepresentation<?> getFocusedObject(DropTargetDropEvent event) {
if (getFocusRetriever() != null) {
GraphicalRepresentation<?> returned = getFocusRetriever().getFocusedObject(event);
if (returned == null) {
// Since we are in a FGEView, a null value indicates that we are on the Drawing view
return getFGEView().getGraphicalRepresentation().getDrawingGraphicalRepresentation();
}
return returned;
}
// No focus retriever: we are not in a FGEView....
return null;
}
}
public DrawingController<?> getController() {
return _controller;
}
protected void registerController(DrawingController<?> controller) {
_controller = controller;
}
public void updatePalette() {
_paletteController.rebuildDrawingView();
}
public DragSourceContext getDragSourceContext() {
return dragSourceContext;
}
public void setDragSourceContext(DragSourceContext dragSourceContext) {
this.dragSourceContext = dragSourceContext;
}
}