package com.horstmann.violet.web.workspace.editorpart;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.Rectangle2D;
import com.horstmann.violet.web.util.jwt.CustomWebGraphics2D;
import com.horstmann.violet.workspace.editorpart.IEditorPart;
import com.horstmann.violet.workspace.editorpart.IEditorPartBehaviorManager;
import com.horstmann.violet.workspace.editorpart.IGrid;
import eu.webtoolkit.jwt.Coordinates;
import eu.webtoolkit.jwt.KeyboardModifier;
import eu.webtoolkit.jwt.Signal1;
import eu.webtoolkit.jwt.WFont;
import eu.webtoolkit.jwt.WLength;
import eu.webtoolkit.jwt.WLength.Unit;
import eu.webtoolkit.jwt.WMouseEvent;
import eu.webtoolkit.jwt.WMouseEvent.Button;
import eu.webtoolkit.jwt.WPaintDevice;
import eu.webtoolkit.jwt.WPaintedWidget;
import eu.webtoolkit.jwt.WPainter;
public class EditorPartWidget extends WPaintedWidget {
private IEditorPart editorPart;
private int mouseDragGapX = 0;
private int mouseDragGapY = 0;
private MouseEvent lastMouseEvent;
private boolean isDragDetected = false; // Used to avoid 'clicked' event after 'dragged" event
public EditorPartWidget(IEditorPart editorPart) {
setWidth(new WLength(100, Unit.Percentage));
setHeight(new WLength(100, Unit.Percentage));
this.editorPart = editorPart;
final IGrid grid = editorPart.getGrid();
grid.setVisible(false);
this.editorPart.getGraph().setGridSticker(grid.getGridSticker());
final IEditorPartBehaviorManager behaviorManager = editorPart.getBehaviorManager();
mouseDragged().addListener(this, new Signal1.Listener<WMouseEvent>() {
@Override
public void trigger(WMouseEvent event) {
MouseEvent mouseEvent = convertMouseEvent(event, MouseEvent.MOUSE_DRAGGED, 0, EditorPartWidget.this.editorPart.getSwingComponent());
if (lastMouseEvent != null && isSameEvent(lastMouseEvent, mouseEvent)) {
return;
}
int deltaX = Math.abs(event.getDragDelta().x);
int deltaY = Math.abs(event.getDragDelta().y);
if (Math.abs(deltaX - mouseDragGapX) >= grid.getSnappingWidth() || Math.abs(deltaY - mouseDragGapY) >= grid.getSnappingHeight()) {
behaviorManager.fireOnMouseDragged(mouseEvent);
fixEditorSize(event);
update();
mouseDragGapX = deltaX;
mouseDragGapY = deltaY;
}
lastMouseEvent = mouseEvent;
isDragDetected = true;
}
});
mouseWentDown().addListener(this, new Signal1.Listener<WMouseEvent>() {
@Override
public void trigger(WMouseEvent event) {
MouseEvent mouseEvent = convertMouseEvent(event, MouseEvent.MOUSE_PRESSED, 0, EditorPartWidget.this.editorPart.getSwingComponent());
if (lastMouseEvent != null && isSameEvent(lastMouseEvent, mouseEvent)) {
return;
}
behaviorManager.fireOnMousePressed(mouseEvent);
// No need to call update() that will be done on drag on button
// release;
lastMouseEvent = mouseEvent;
}
});
mouseWentUp().addListener(this, new Signal1.Listener<WMouseEvent>() {
@Override
public void trigger(WMouseEvent event) {
MouseEvent mouseEvent = convertMouseEvent(event, MouseEvent.MOUSE_RELEASED, 0, EditorPartWidget.this.editorPart.getSwingComponent());
if (lastMouseEvent != null && isSameEvent(lastMouseEvent, mouseEvent)) {
return;
}
behaviorManager.fireOnMouseReleased(mouseEvent);
update();
lastMouseEvent = mouseEvent;
fixEditorSize(event);
}
});
clicked().addListener(this, new Signal1.Listener<WMouseEvent>() {
@Override
public void trigger(WMouseEvent event) {
if (isDragDetected) {
isDragDetected = false;
return;
}
MouseEvent mouseEvent = convertMouseEvent(event, MouseEvent.MOUSE_CLICKED, 1, EditorPartWidget.this.editorPart.getSwingComponent());
if (lastMouseEvent != null && isSameEvent(lastMouseEvent, mouseEvent)) {
return;
}
behaviorManager.fireOnMouseClicked(mouseEvent);
lastMouseEvent = mouseEvent;
fixEditorSize(event);
update();
}
});
doubleClicked().addListener(this, new Signal1.Listener<WMouseEvent>() {
@Override
public void trigger(WMouseEvent event) {
MouseEvent mouseEvent = convertMouseEvent(event, MouseEvent.MOUSE_CLICKED, 2, EditorPartWidget.this.editorPart.getSwingComponent());
if (lastMouseEvent != null && isSameEvent(lastMouseEvent, mouseEvent)) {
return;
}
behaviorManager.fireOnMouseClicked(mouseEvent);
lastMouseEvent = mouseEvent;
fixEditorSize(event);
update();
}
});
mouseWheel().addListener(this, new Signal1.Listener<WMouseEvent>() {
@Override
public void trigger(WMouseEvent event) {
MouseWheelEvent wheelEvent = convertMouseWheelEvent(event, MouseEvent.MOUSE_WHEEL, EditorPartWidget.this.editorPart.getSwingComponent());
behaviorManager.fireOnMouseWheelMoved(wheelEvent);
update();
}
});
}
private MouseEvent convertMouseEvent(WMouseEvent event, int type, int clickCount, Component c) {
int modifiers = 0;
if (event.getModifiers().contains(KeyboardModifier.AltModifier))
modifiers |= MouseEvent.ALT_DOWN_MASK;
if (event.getModifiers().contains(KeyboardModifier.ShiftModifier))
modifiers |= MouseEvent.SHIFT_DOWN_MASK;
if (event.getModifiers().contains(KeyboardModifier.ControlModifier))
modifiers |= MouseEvent.CTRL_DOWN_MASK;
if (event.getModifiers().contains(KeyboardModifier.MetaModifier))
modifiers |= MouseEvent.META_DOWN_MASK;
int button = 0;
if (event.getButton() == Button.LeftButton) {
modifiers |= MouseEvent.BUTTON1_DOWN_MASK;
button = MouseEvent.BUTTON1;
} else if (event.getButton() == Button.MiddleButton) {
modifiers |= MouseEvent.BUTTON2_DOWN_MASK;
button = MouseEvent.BUTTON2;
} else if (event.getButton() == Button.RightButton) {
modifiers |= MouseEvent.BUTTON3_DOWN_MASK;
button = MouseEvent.BUTTON3;
}
MouseEvent newMouseEvent = new MouseEvent(c, type, System.currentTimeMillis(), modifiers, event.getWidget().x, event.getWidget().y, clickCount, event.getButton() == Button.RightButton, button);
return newMouseEvent;
}
private MouseWheelEvent convertMouseWheelEvent(WMouseEvent event, int type, Component c) {
int modifiers = 0;
if (event.getModifiers().contains(KeyboardModifier.AltModifier))
modifiers |= MouseEvent.ALT_DOWN_MASK;
if (event.getModifiers().contains(KeyboardModifier.ShiftModifier))
modifiers |= MouseEvent.SHIFT_DOWN_MASK;
if (event.getModifiers().contains(KeyboardModifier.ControlModifier))
modifiers |= MouseEvent.CTRL_DOWN_MASK;
if (event.getModifiers().contains(KeyboardModifier.MetaModifier))
modifiers |= MouseEvent.META_DOWN_MASK;
int wheelDelta = event.getWheelDelta();
return new MouseWheelEvent(c, type, System.currentTimeMillis(), modifiers, event.getWidget().x, event.getWidget().y, 1, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, 1, -wheelDelta);
}
@Override
protected void paintEvent(WPaintDevice paintDevice) {
WPainter painter = new WPainter(paintDevice) {
@Override
public WFont getFont() {
WFont font = super.getFont();
font.setSize(new WLength(12, Unit.Pixel));
return font;
}
};
painter.setClipping(false);
paintDevice.init();
Graphics2D graphics = new CustomWebGraphics2D(painter);
this.editorPart.getSwingComponent().paint(graphics);
paintDevice.done();
}
private boolean isSameEvent(MouseEvent firstMouseEvent, MouseEvent secondMouseEvent) {
if (firstMouseEvent == null || secondMouseEvent == null) {
return false;
}
boolean isSameButton = (firstMouseEvent.getButton() == secondMouseEvent.getButton());
boolean isSameLocation = firstMouseEvent.getPoint().equals(secondMouseEvent.getPoint());
boolean isSameModifiers = (firstMouseEvent.getModifiers() == secondMouseEvent.getModifiers());
boolean isSameClickCount = (firstMouseEvent.getClickCount() == secondMouseEvent.getClickCount());
boolean isSameType = (firstMouseEvent.getID() == secondMouseEvent.getID());
boolean isSameEvent = isSameButton && isSameLocation && isSameModifiers && isSameClickCount && isSameType;
return isSameEvent;
}
public IEditorPart getEditorPart() {
return this.editorPart;
}
@Override
public void resize(int widthPixels, int heightPixels) {
this.editorPart.getSwingComponent().setSize(widthPixels, heightPixels);
super.resize(widthPixels, heightPixels);
}
private void fixEditorSize(WMouseEvent event) {
Dimension preferredSize = this.editorPart.getSwingComponent().getPreferredSize();
int gap = 10;
Coordinates mouseLocationRelativeToEditorPart = event.getWidget();
double graphWidth = Math.max(preferredSize.getWidth() + gap, mouseLocationRelativeToEditorPart.x + gap);
double graphHeight = Math.max(preferredSize.getHeight() + gap, mouseLocationRelativeToEditorPart.y + gap);
double editorPartWidth = getWidth().toPixels();
double editorPartHeight = getHeight().toPixels();
if (editorPartWidth != graphWidth || editorPartHeight != graphHeight) {
setMinimumSize(new WLength(100, Unit.Percentage), new WLength(100, Unit.Percentage));
resize((int) graphWidth, (int) graphHeight);
}
}
}