package at.bestsolution.efxclipse.runtime.example.photoedit.ui;
import static at.bestsolution.efxclipse.runtime.example.photoedit.model.photoedit.PhotoeditPackage.Literals.PHOTO_AREA__HEIGHT;
import static at.bestsolution.efxclipse.runtime.example.photoedit.model.photoedit.PhotoeditPackage.Literals.PHOTO_AREA__WIDTH;
import static at.bestsolution.efxclipse.runtime.example.photoedit.model.photoedit.PhotoeditPackage.Literals.PHOTO_AREA__X;
import static at.bestsolution.efxclipse.runtime.example.photoedit.model.photoedit.PhotoeditPackage.Literals.PHOTO_AREA__Y;
import static at.bestsolution.efxclipse.runtime.example.photoedit.model.photoedit.PhotoeditPackage.Literals.PHOTO__AREAS;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicReference;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.core.databinding.observable.list.IListChangeListener;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.ui.di.Focus;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.impl.ApplicationPackageImpl;
import org.eclipse.e4.ui.model.application.ui.basic.MInputPart;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.databinding.EMFDataBindingContext;
import org.eclipse.emf.databinding.EMFProperties;
import org.eclipse.emf.databinding.IEMFValueProperty;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EContentAdapter;
import at.bestsolution.efxclipse.runtime.databinding.IJFXBeanValueProperty;
import at.bestsolution.efxclipse.runtime.databinding.JFXBeanProperties;
import at.bestsolution.efxclipse.runtime.example.photoedit.core.ResourceStore;
import at.bestsolution.efxclipse.runtime.example.photoedit.model.photoedit.Album;
import at.bestsolution.efxclipse.runtime.example.photoedit.model.photoedit.Media;
import at.bestsolution.efxclipse.runtime.example.photoedit.model.photoedit.Photo;
import at.bestsolution.efxclipse.runtime.example.photoedit.model.photoedit.PhotoArea;
import at.bestsolution.efxclipse.runtime.example.photoedit.model.photoedit.PhotoeditFactory;
@SuppressWarnings("restriction")
public class MediaEditor {
@Inject
ResourceStore store;
@Inject
MInputPart input;
@Inject
MApplication application;
private Photo photo;
private String tool;
private PhotoArea sizedArea;
private PhotoArea moveArea;
// private static final String KEY_ACTIVE_TOOL = "activetool";
private static final String KEY_TRANSLATE_X = "translateX";
private static final String KEY_TRANSLATE_Y = "translateY";
private static final String KEY_SCALE_FACTOR = "scale";
private AnchorPane transformStack;
private boolean isSWT = false;
@Inject
public MediaEditor(IEclipseContext context) {
isSWT = context.get("org.eclipse.swt.widgets.Composite") != null;
}
@PostConstruct
void init(BorderPane pane) {
String uuid = input.getInputURI().substring("cdo-object://".length());
for( Album a : store.getPhotoEditApp().getAlbums() ) {
for( Media m : a.getMedia() ) {
if( m instanceof Photo && m.getUuid().equals(uuid) ) {
initPhoto(pane, (Photo) m);
this.photo = (Photo) m;
break;
}
}
}
((EObject)input).eAdapters().add(new EContentAdapter() {
@SuppressWarnings("unchecked")
@Override
public void notifyChanged(Notification msg) {
super.notifyChanged(msg);
if( msg.getFeature() == ApplicationPackageImpl.Literals.APPLICATION_ELEMENT__PERSISTED_STATE ) {
processChange((Entry<String, String>) msg.getNewValue());
} else if( msg.getFeature() == ApplicationPackageImpl.Literals.STRING_TO_STRING_MAP__VALUE ) {
processChange((Entry<String, String>) msg.getNotifier());
}
}
});
if( input.getPersistedState().get(KEY_TRANSLATE_X) != null ) {
transformStack.setTranslateX(Double.parseDouble(input.getPersistedState().get(KEY_TRANSLATE_X))+(isSWT?1100:0));
}
if( input.getPersistedState().get(KEY_TRANSLATE_Y) != null ) {
transformStack.setTranslateY(Double.parseDouble(input.getPersistedState().get(KEY_TRANSLATE_Y))+(isSWT?750:0));
}
if( input.getPersistedState().get(KEY_SCALE_FACTOR) != null ) {
double v = Double.parseDouble(input.getPersistedState().get(KEY_SCALE_FACTOR));
transformStack.setScaleX(v);
transformStack.setScaleY(v);
}
}
@Inject
void setTool(@Named("activetool") @Optional String tool){
this.tool = tool;
}
private void processChange(Entry<String, String> e) {
if( KEY_TRANSLATE_X.equals(e.getKey()) ) {
transformStack.setTranslateX(Double.parseDouble(e.getValue())+(isSWT?1100:0));
} else if( KEY_TRANSLATE_Y.equals(e.getKey()) ) {
transformStack.setTranslateY(Double.parseDouble(e.getValue())+(isSWT?750:0));
} else if( KEY_SCALE_FACTOR.equals(e.getKey()) ) {
double v = Double.parseDouble(e.getValue());
transformStack.setScaleX(v);
transformStack.setScaleY(v);
}
}
@Focus
void setFocus() {
application.getContext().set(Media.class, photo);
}
private void initPhoto(final BorderPane pane, final Photo photo) {
transformStack = new AnchorPane();
Image img = new Image((photo.getSource().getObjectStream()));
ImageView v = new ImageView(img);
transformStack.getChildren().add(v);
final EMFDataBindingContext dbc = new EMFDataBindingContext();
final IEMFValueProperty exProp = EMFProperties.value(PHOTO_AREA__X);
final IEMFValueProperty eyProp = EMFProperties.value(PHOTO_AREA__Y);
final IEMFValueProperty ewidthProp = EMFProperties.value(PHOTO_AREA__WIDTH);
final IEMFValueProperty eheightProp = EMFProperties.value(PHOTO_AREA__HEIGHT);
final IJFXBeanValueProperty fxProp = JFXBeanProperties.value("x");
final IJFXBeanValueProperty fyProp = JFXBeanProperties.value("y");
final IJFXBeanValueProperty fwidthProp = JFXBeanProperties.value("width");
final IJFXBeanValueProperty fheightProp = JFXBeanProperties.value("height");
for( PhotoArea a: photo.getAreas() ) {
Rectangle r = new Rectangle();
r.setFill(Color.TRANSPARENT);
r.setStroke(Color.RED);
r.setStrokeWidth(5);
r.setUserData(a);
r.setCursor(Cursor.MOVE);
dbc.bindValue(fxProp.observe(r), exProp.observe(a));
dbc.bindValue(fyProp.observe(r), eyProp.observe(a));
dbc.bindValue(fwidthProp.observe(r), ewidthProp.observe(a));
dbc.bindValue(fheightProp.observe(r), eheightProp.observe(a));
transformStack.getChildren().add(r);
}
pane.setCenter(transformStack);
IObservableList l = EMFProperties.list(PHOTO__AREAS).observe(photo);
l.addListChangeListener(new IListChangeListener() {
@Override
public void handleListChange(ListChangeEvent event) {
event.diff.accept(new ListDiffVisitor() {
@Override
public void handleRemove(int index, Object element) {
Iterator<Node> it = transformStack.getChildren().iterator();
while( it.hasNext() ) {
Node n = it.next();
if( n.getUserData() == element ) {
it.remove();
break;
}
}
}
@Override
public void handleAdd(int index, Object element) {
Rectangle r = new Rectangle();
r.setFill(Color.TRANSPARENT);
r.setStroke(Color.RED);
r.setStrokeWidth(5);
r.setUserData(element);
r.setCursor(Cursor.MOVE);
dbc.bindValue(fxProp.observe(r), exProp.observe(element));
dbc.bindValue(fyProp.observe(r), eyProp.observe(element));
dbc.bindValue(fwidthProp.observe(r), ewidthProp.observe(element));
dbc.bindValue(fheightProp.observe(r), eheightProp.observe(element));
transformStack.getChildren().add(r);
}
});
}
});
final AtomicReference<MouseEvent> deltaEvent = new AtomicReference<MouseEvent>();
pane.setOnScroll(new EventHandler<ScrollEvent>() {
@Override
public void handle(ScrollEvent event) {
int direction = event.getDeltaY() < 0 || event.isShiftDown() ? -1 : 1;
double v = Math.max(transformStack.getScaleX() + 0.05 * direction,0.1);
input.getPersistedState().put(KEY_SCALE_FACTOR, Double.toString(v));
}
});
pane.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if( !"area".equals(tool) ) {
deltaEvent.set(event);
}
}
});
transformStack.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if( "area".equals(tool) ) {
for( PhotoArea a : photo.getAreas() ) {
if( a.contains(event.getX(), event.getY()) ) {
moveArea = a;
application.getContext().set(PhotoArea.class, moveArea);
deltaEvent.set(event);
return;
}
}
PhotoArea p = PhotoeditFactory.eINSTANCE.createPhotoArea();
p.setX(event.getX());
p.setY(event.getY());
p.setDescription("New Area");
photo.getAreas().add(p);
sizedArea = p;
application.getContext().set(PhotoArea.class, sizedArea);
}
}
});
transformStack.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if( "area".equals(tool) ) {
if( sizedArea != null ) {
double width = event.getX() - sizedArea.getX();
double height = event.getY() - sizedArea.getY();
sizedArea.setWidth(width);
sizedArea.setHeight(height);
} else if( moveArea != null ) {
double deltaX = event.getX() - deltaEvent.get().getX();
double deltaY = event.getY() - deltaEvent.get().getY();
moveArea.setX(moveArea.getX() + deltaX);
moveArea.setY(moveArea.getY() + deltaY);
deltaEvent.set(event);
}
}
}
});
transformStack.setOnMouseReleased(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if( sizedArea != null ) {
if( sizedArea.getWidth() < 10 || sizedArea.getHeight() < 10 ) {
photo.getAreas().remove(sizedArea);
}
}
sizedArea = null;
moveArea = null;
}
});
pane.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if( !"area".equals(tool) ) {
double deltaX = event.getX() - deltaEvent.get().getX();
double deltaY = event.getY() - deltaEvent.get().getY();
double targetX = transformStack.getTranslateX() + deltaX;
double targetY = transformStack.getTranslateY() + deltaY;
if( ! isSWT ) {
input.getPersistedState().put(KEY_TRANSLATE_X, Double.toString(targetX-(isSWT?750:0)));
input.getPersistedState().put(KEY_TRANSLATE_Y, Double.toString(targetY-(isSWT?650:0)));
}
deltaEvent.set(event);
}
}
});
}
}