package com.iambookmaster.client.editor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import com.allen_sauer.gwt.dnd.client.DragController;
import com.allen_sauer.gwt.dnd.client.DragEndEvent;
import com.allen_sauer.gwt.dnd.client.DragHandler;
import com.allen_sauer.gwt.dnd.client.DragStartEvent;
import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.allen_sauer.gwt.dnd.client.VetoDragException;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.iambookmaster.client.Images;
import com.iambookmaster.client.Styles;
import com.iambookmaster.client.common.ScrollContainer;
public abstract class MapEditor extends ScrollContainer {
private static final int SOURCE_X = 6;
private static final int SOURCE_Y = 6;
public static final int STYLE_NORMAL=0;
public static final int STYLE_CONDITIONAL=1;
private AbsolutePanel mainPanel;
private Map<MapWidget,ArrayList<MapConnector>> connections = new HashMap<MapWidget,ArrayList<MapConnector>>();
private DragController dragController;
private MapConnector selectedConnection;
private MapWidget selectedMapWidget;
private TitleTextBox titleTextBox;
public int getSelectedPositionY() {
if (selectedMapWidget == null) {
return 0;
} else {
return getPositionY(selectedMapWidget);
}
}
public int getSelectedPositionX() {
if (selectedMapWidget == null) {
return 0;
} else {
return getPositionX(selectedMapWidget);
}
}
public void clear() {
mainPanel.clear();
connections = new HashMap<MapWidget, ArrayList<MapConnector>>();
selectedConnection = null;
}
protected void applyMapSize(int maxDimensionX, int maxDimensionY) {
mainPanel.setSize(String.valueOf(maxDimensionX)+"px", String.valueOf(maxDimensionY)+"px");
}
public MapEditor(int maxX,int maxY) {
setSize("100%", "100%");
mainPanel = new AbsolutePanel();
mainPanel.setStyleName("editor_panel");
applyMapSize(maxX,maxY);
// Create a DragController for each logical area where a set of draggable
// widgets and drop targets will be allowed to interact with one another.
dragController = new PickupDragController(mainPanel, true);
dragController.addDragHandler(new DragHandler(){
private int dragConnectionPosition;
private MapWidget gragMapWidget;
private MapConnector dragConnectionWidget;
public void onDragEnd(DragEndEvent event) {
Widget draggable = event.getContext().draggable;
if (event.getContext().vetoException==null) {
if (draggable instanceof MapWidget) {
updateConnectionsOfWidget((MapWidget) draggable);
}
} else if (gragMapWidget != null) {
//new connection
gragMapWidget.onDragEnd(event.getContext().desiredDraggableX, event.getContext().desiredDraggableY);
} else if (dragConnectionWidget != null) {
//correct connection location
if (draggable instanceof MapConnector) {
//vertical correction
MapConnector connector = (MapConnector)draggable;
dragConnectionWidget.correctionX = dragConnectionWidget.correctionX + event.getContext().desiredDraggableX-dragConnectionPosition;
dragConnectionWidget.updateCorrection(dragConnectionWidget.correctionX,dragConnectionWidget.correctionY);
connector.update();
} else {
//horizontal correction
MapConnectorHorizontral connector = (MapConnectorHorizontral)draggable;
dragConnectionWidget.correctionY = dragConnectionWidget.correctionY + event.getContext().desiredDraggableY-dragConnectionPosition;
dragConnectionWidget.updateCorrection(dragConnectionWidget.correctionX,dragConnectionWidget.correctionY);
connector.connector.update();
}
}
}
public void onDragStart(DragStartEvent event) {
gragMapWidget = null;
dragConnectionWidget = null;
Widget draggable = event.getContext().draggable;
if (draggable instanceof Image) {
if (draggable.getParent() instanceof MapWidget) {
gragMapWidget = (MapWidget)draggable.getParent();
}
} else if (draggable instanceof MapConnector) {
dragConnectionWidget = (MapConnector)draggable;
dragConnectionPosition = event.getContext().mouseX;
} else if (draggable instanceof MapConnectorHorizontral) {
dragConnectionWidget = ((MapConnectorHorizontral)draggable).connector;
dragConnectionPosition = event.getContext().mouseY;
}
}
public void onPreviewDragEnd(DragEndEvent event) throws VetoDragException {
Widget draggable = event.getContext().draggable;
if (draggable instanceof MapWidget) {
} else {
throw new VetoDragException();
}
}
public void onPreviewDragStart(DragStartEvent event) throws VetoDragException {
}
});
setScrollWidget(mainPanel);
titleTextBox = new TitleTextBox();
}
private void updateConnectionsOfWidget(final MapWidget mapWidget) {
DeferredCommand.addCommand(new Command(){
public void execute() {
ArrayList<MapConnector> list = connections.get(mapWidget);
if (list != null && list.size()>0) {
//update all connections
for (int i = 0; i < list.size(); i++) {
MapConnector connector = list.get(i);
connector.update();
}
}
int x = getPositionX(mapWidget);
int y = getPositionY(mapWidget);
if (x>0 || y>0) {
mapWidget.updatePosition(x,y);
}
}
});
}
// private final PopupPanel dndPopupLabel = new PopupPanel(){
// {
// this.setStyleName("map_editor_dnd");
// this.setWidget(new Image(Images.ADD_CONNECTION));
// }
// };
protected void selectWidget(MapWidget mapWidget, MapConnector connector) {
if (mapWidget == null) {
if (selectedMapWidget != null) {
//clear selected widget
selectedMapWidget.unselect();
selectedMapWidget.hightlight(false);
selectedMapWidget = null;
}
if (connector==null) {
//nothing
} else {
if (selectedConnection==connector) {
// connector.hightlight(false);
// connector.unselect();
// dragController.makeNotDraggable(selectedConnection);
// selectedConnection = null;
} else {
connector.hightlight(true);
if (selectedConnection != null) {
selectedConnection.hightlight(false);
selectedConnection.unselect();
try {
dragController.makeNotDraggable(selectedConnection);
} catch (Exception e) {
e.printStackTrace();
}
try {
dragController.makeNotDraggable(selectedConnection.horizontralConnection);
} catch (Exception e) {
e.printStackTrace();
}
}
selectedConnection = connector;
connector.select();
dragController.makeDraggable(selectedConnection);
dragController.makeDraggable(selectedConnection.horizontralConnection);
}
}
} else {
if (selectedConnection != null) {
//clear selected widget
selectedConnection.hightlight(false);
selectedConnection.unselect();
try {
dragController.makeNotDraggable(selectedConnection);
} catch (Exception e) {
}
try {
dragController.makeNotDraggable(selectedConnection.horizontralConnection);
} catch (Exception e) {
}
selectedConnection = null;
}
if (selectedMapWidget!=mapWidget) {
if (selectedMapWidget != null) {
selectedMapWidget.unselect();
selectedMapWidget.hightlight(false);
}
selectedMapWidget = mapWidget;
selectedMapWidget.select();
selectedMapWidget.hightlight(true);
}
}
}
public abstract class MapWidget extends HorizontalPanel implements MouseDownHandler {
private Label nameLabel;
private Image connectorImage;
public MapWidget() {
DOM.setStyleAttribute(getElement(), "zIndex", "100");
nameLabel = new Label();
nameLabel.setWordWrap(false);
nameLabel.addMouseDownHandler(this);
add(nameLabel);
}
public void draw(int x,int y) {
nameLabel.setText(getFullName());
//image for connections
Widget widget = getQuickWidget();
if (widget != null) {
add(widget);
}
connectorImage = new Image(Images.ADD_CONNECTION);
connectorImage.setStyleName(Styles.MOVE);
add(connectorImage);
mainPanel.add(this, x, y);
dragController.makeDraggable(this,nameLabel);
dragController.makeDraggable(connectorImage);
hightlight(false);
}
public void delete() {
ArrayList<MapConnector> list = connections.get(this);
if (list != null) {
connections.remove(this);
for (int i = 0; i < list.size(); i++) {
MapConnector connector = list.get(i);
connector.removeWidget();
}
}
dragController.makeNotDraggable(this);
dragController.makeNotDraggable(connectorImage);
mainPanel.remove(this);
}
public void hightlight(boolean highlight) {
if (highlight) {
setStyleName("map_widget_selected");
} else {
setStyleName("map_widget");
}
}
public abstract void unselect();
public abstract void select();
public abstract Widget getQuickWidget();
protected abstract void updatePosition(int x, int y);
public abstract String getName();
public abstract String getFullName();
public void onMouseDown(MouseDownEvent event) {
if (event.getSource()==nameLabel) {
selectWidget(MapWidget.this,null);
}
}
public void onDragEnd(int absX,int absY) {
int l = mainPanel.getWidgetCount();
for (int i = 0; i < l; i++) {
Widget widget = mainPanel.getWidget(i);
if (widget instanceof MapWidget) {
MapWidget mapWidget = (MapWidget) widget;
int x1 = mapWidget.getAbsoluteLeft();
int x2 = x1+mapWidget.getOffsetWidth();
int y1 = mapWidget.getAbsoluteTop();
int y2 = y1+mapWidget.getOffsetHeight();
if (x1<=absX && x2>=absX && y1 <=absY && y2 >=absY) {
//this
connectTo(mapWidget);
break;
}
}
}
}
public abstract void connectTo(MapWidget mapWidget);
public abstract void onNameChanged(String name);
public void redraw(int x, int y) {
if (x==0 && y==0) {
x=0;
}
mainPanel.setWidgetPosition(this, x, y);
nameLabel.setText(getFullName());
updateConnectionsOfWidget(this);
}
public void editTitle() {
titleTextBox.show(this);
}
public void setTitleStyle(String style) {
nameLabel.setStyleName(style);
nameLabel.addStyleName(Styles.MOVE);
}
}
public class TitleTextBox extends TextBox implements BlurHandler,ChangeHandler, KeyPressHandler{
private MapWidget mapWidget;
private Command focusCommand = new Command() {
public void execute() {
setFocus(false);
}
};
public TitleTextBox() {
addBlurHandler(this);
addChangeHandler(this);
addKeyPressHandler(this);
DOM.setStyleAttribute(getElement(), "zIndex", "10000");
}
public void show(MapWidget widget) {
mapWidget = widget;
setText(widget.getName());
mainPanel.add(this,getPositionX(widget),getPositionY(widget));
setSize(String.valueOf(widget.getOffsetWidth())+"px",String.valueOf(widget.getOffsetHeight())+"px");
DeferredCommand.addCommand(focusCommand);
}
public void onBlur(BlurEvent event) {
mainPanel.remove(this);
}
public void onChange(ChangeEvent event) {
mapWidget.onNameChanged(this.getText());
}
public void onKeyPress(KeyPressEvent event) {
if (event.getCharCode()==KeyCodes.KEY_ENTER) {
mapWidget.onNameChanged(this.getText());
mainPanel.remove(this);
}
if (event.getCharCode()==KeyCodes.KEY_ESCAPE) {
//no changes in this case
setText(mapWidget.getName());
mainPanel.remove(this);
}
}
}
private int getPositionY(Widget widget) {
return DOM.getElementPropertyInt(widget.getElement(), "offsetTop");
}
private int getPositionX(Widget widget1) {
return DOM.getElementPropertyInt(widget1.getElement(), "offsetLeft");
}
public final class MapConnectorHorizontral extends HTML {
private MapConnector connector;
public MapConnectorHorizontral(MapConnector connector) {
super(" ");
DOM.setStyleAttribute(getElement(), "zIndex", "1");
DOM.setStyleAttribute(getElement(), "fontSize", "1px");
this.connector=connector;
}
}
public abstract class MapConnector extends HTML implements MouseDownHandler {
protected int correctionY;
protected int correctionX;
private static final String BORDER_ATTRIBUTE_HOR = "borderLeft";
private static final String BORDER_ATTRIBUTE_VER = "borderTop";
private MapWidget widget1;
private MapWidget widget2;
private boolean hightlight;
private Image source;
private int style;
private String color;
private MapConnectorHorizontral horizontralConnection;
public void remove() {
ArrayList<MapConnector> list = connections.get(widget1);
if (list != null) {
list.remove(this);
}
list = connections.get(widget2);
if (list != null) {
list.remove(this);
}
removeWidget();
}
private void removeWidget() {
mainPanel.remove(this);
mainPanel.remove(horizontralConnection);
if (source != null) {
mainPanel.remove(source);
}
if (selectedConnection==this) {
selectedConnection = null;
}
}
public MapConnector(MapWidget widget1,MapWidget widget2, boolean bothDirections, int style, String color,int corrX,int corrY) {
super(" ");
horizontralConnection = new MapConnectorHorizontral(this);
correctionX = corrX;
correctionY = corrY;
if (bothDirections) {
setWidth("6px");
horizontralConnection.setHeight("6px");
} else {
setWidth("6px");
horizontralConnection.setHeight("6px");
}
DOM.setStyleAttribute(getElement(), "fontSize", "1px");
DOM.setStyleAttribute(getElement(), "zIndex", "1");
this.style = style;
this.color = color;
if (bothDirections==false) {
source = new Image(Images.SOURCE);
mainPanel.add(source, 0, 0);
}
addMouseDownHandler(this);
horizontralConnection.addMouseDownHandler(this);
this.widget1 = widget1;
this.widget2 = widget2;
addConnection(widget1);
addConnection(widget2);
mainPanel.add(this, 0, 0);
mainPanel.add(horizontralConnection, 0, 0);
if (bothDirections==false) {
mainPanel.add(source, 0, 0);
}
}
public int getStyle() {
return style;
}
public void setStyle(int style) {
this.style = style;
}
public void update() {
int ya1 = getPositionY(widget1);
int xa1 = getPositionX(widget1);
int yb1 = getPositionY(widget2);
int xb1 = getPositionX(widget2);
if (ya1<=yb1) {
_update(xa1,ya1,xa1+widget1.getOffsetWidth(),ya1+widget1.getOffsetHeight(),xb1,yb1,xb1+widget2.getOffsetWidth(),yb1+widget2.getOffsetHeight(),true);
} else {
//mirror
_update(xb1,yb1,xb1+widget2.getOffsetWidth(),yb1+widget2.getOffsetHeight(),xa1,ya1,xa1+widget1.getOffsetWidth(),ya1+widget1.getOffsetHeight(),false);
}
}
protected Image getSourceImage() {
return source;
}
private void _update(int xa1, int ya1, int xa2, int ya2, int xb1, int yb1, int xb2, int yb2, boolean normalDirection) {
boolean top=false;
boolean left=false;
int x;
int y;
int h;
int w;
if (ya2<yb1) {
//all widget is above
if (xa2<xb1) {
//widget 1 on the left
top = true;
x = xa2;
if (correctionY == 0) {
y = ya2;
} else {
y = ya2+correctionY;
if (y>ya2) {
y = ya2;
correctionY = 0;
updateCorrection(correctionX,correctionY);
} else if (y<ya1) {
y = ya1;
correctionY = ya1-ya2;
updateCorrection(correctionX,correctionY);
}
}
w = xb1 - x;
if (correctionX > 0) {
if (xb2-xb1<correctionX) {
w = xb2 - x;
correctionX = xb2-xb1;
updateCorrection(correctionX,correctionY);
} else {
w = w+correctionX;
}
} else if (correctionX<0){
correctionX = 0;
updateCorrection(correctionX,correctionY);
}
h = yb1 - y;
if (h<1) {
h=1;
}
if (w<1) {
w = 1;
}
if (source != null) {
if (normalDirection) {
mainPanel.setWidgetPosition(source, x-SOURCE_X, y-SOURCE_Y+4);
} else {
mainPanel.setWidgetPosition(source, x+w-SOURCE_X, yb1-SOURCE_Y);
}
}
} else if (xa1 > xb2){
//widget 1 on the right
top = true;
left = true;
if (correctionY == 0) {
y = ya2;
} else {
y = ya2+correctionY;
if (y<ya1) {
y = ya1;
correctionY = ya1-ya2;
updateCorrection(correctionX,correctionY);
} else if (y>ya2) {
y = ya2;
correctionY = 0;
updateCorrection(correctionX,correctionY);
}
}
if (correctionX < 0) {
x = xb2+correctionX;
if (x<xb1) {
correctionX = xb1-xb2;
updateCorrection(correctionX,correctionY);
x=xb1;
}
} else {
if (correctionX>0){
correctionX=0;
updateCorrection(correctionX,correctionY);
}
x = xb2;
}
w = xa1 - x;
h = yb1 - y;
if (h<1) {
h=1;
}
if (w<1) {
w = 1;
}
if (source != null) {
if (normalDirection) {
mainPanel.setWidgetPosition(source, xa1-SOURCE_X, y-SOURCE_Y+4);
} else {
mainPanel.setWidgetPosition(source, x-SOURCE_X, yb1-SOURCE_Y);
}
}
} else if (xa1<xb1){
//widget 1 a bit left
left = true;
if (correctionX > 0) {
x = xb1+correctionX;
if (x > xb2) {
correctionX = xb2-xb1;
updateCorrection(correctionX,correctionY);
x=xb2;
}
if (x>xa2) {
correctionX = xa2-xb1;
updateCorrection(correctionX,correctionY);
x=xa2;
}
} else {
if (correctionX<0){
correctionX=0;
updateCorrection(correctionX,correctionY);
}
x = xb1;
}
y = ya2;
w = 2;
h = yb1 - y;
if (h<1) {
h=1;
}
if (source != null) {
if (normalDirection) {
mainPanel.setWidgetPosition(source, x-SOURCE_X, y-SOURCE_Y+4);
} else {
mainPanel.setWidgetPosition(source, x+w-SOURCE_X, yb1-SOURCE_Y);
}
}
} else {
//widget 1 a bit right
if (correctionX > 0) {
x = xa1+correctionX;
if (x > xa2) {
correctionX = xa2-xa1;
updateCorrection(correctionX,correctionY);
x=xa2;
}
if (x > xb2) {
x = xb2;
correctionX = xb2-xa1;
updateCorrection(correctionX,correctionY);
}
} else {
if (correctionX<0){
correctionX=0;
updateCorrection(correctionX,correctionY);
}
x = xa1;
}
y = ya2;
w = 2;
h = yb1 - y;
if (h<1) {
h=1;
}
if (source != null) {
if (normalDirection) {
mainPanel.setWidgetPosition(source, x-1, y-SOURCE_Y+4);
} else {
mainPanel.setWidgetPosition(source, x+w-3, yb1-SOURCE_Y);
}
}
}
} else if (xa2<xb1) {
//y intersection, left
top = true;
x = xa2;
if (correctionY>0) {
y = ya1+correctionY;
if (y>ya2) {
correctionY = ya2-ya1;
updateCorrection(correctionX,correctionY);
y=ya2;
}
if (y>yb2) {
correctionY = yb2-ya1;
updateCorrection(correctionX,correctionY);
y=yb2;
}
} else {
if (correctionY<0){
correctionY=0;
updateCorrection(correctionX,correctionY);
}
y = ya1;
}
w = xb1 - xa2;
h = yb1 - y;
if (h<1) {
h=1;
}
if (w<1) {
w = 1;
}
if (source != null) {
if (normalDirection) {
mainPanel.setWidgetPosition(source, x-SOURCE_X, y-SOURCE_Y+4);
} else {
mainPanel.setWidgetPosition(source, xb1-SOURCE_X, y-SOURCE_Y);
}
}
} else {
//y intersection, right
top = true;
left = true;
x = xb2;
if (correctionY>0) {
y = ya1+correctionY;
if (y>ya2) {
correctionY = ya2-ya1;
updateCorrection(correctionX,correctionY);
y=ya2;
}
if (y>yb2) {
correctionY = yb2-ya1;
updateCorrection(correctionX,correctionY);
y=yb2;
}
} else {
if (correctionY<0){
correctionY=0;
updateCorrection(correctionX,correctionY);
}
y = ya1;
}
w = xa1 - xb2;
h = yb1 - y;
if (h<1) {
h=1;
}
if (w<1) {
w = 1;
}
if (source != null) {
if (normalDirection) {
mainPanel.setWidgetPosition(source, xa1-SOURCE_X, y-SOURCE_Y+4);
} else {
mainPanel.setWidgetPosition(source, x-SOURCE_X, y+h-SOURCE_Y);
}
}
}
// if (source==null) {
// x=x-2;
// w=w+4;
// y=y-4;
// h=h+8;
// }
if (left) {
mainPanel.setWidgetPosition(this, x, y);
} else {
//right or middle
mainPanel.setWidgetPosition(this, x+w, y);
}
setHeight(String.valueOf(h));
if (top) {
mainPanel.setWidgetPosition(horizontralConnection, x, y);
} else {
//bottom or middle
mainPanel.setWidgetPosition(horizontralConnection, x, y+h);
}
horizontralConnection.setWidth(String.valueOf(w));
apply(hightlight);
}
private void addConnection(MapWidget widget) {
ArrayList<MapConnector> list = connections.get(widget);
if (list==null) {
list = new ArrayList<MapConnector>();
connections.put(widget,list);
}
list.add(this);
}
public void onMouseDown(MouseDownEvent event) {
selectConnection(this);
}
public void hightlight(boolean hightlight) {
this.hightlight = hightlight;
apply(hightlight);
}
private void apply(boolean highlight) {
String value;
if (style==STYLE_NORMAL) {
if (source==null) {
value = highlight ? "6px double "+color:"3px double "+color;
} else {
value = highlight ? "2px solid "+color:"1px solid "+color;
}
} else {
if (source==null) {
value = highlight ? "2px dashed "+color:"1px dashed "+color;
} else {
value = highlight ? "4px dotted "+color:"1px dotted "+color;
}
}
DOM.setStyleAttribute(getElement(), BORDER_ATTRIBUTE_HOR, value);
DOM.setStyleAttribute(horizontralConnection.getElement(), BORDER_ATTRIBUTE_VER, value);
}
private void selectConnection(MapConnector connector) {
selectWidget(null, this);
}
public abstract void unselect();
public abstract void select();
public abstract void updateCorrection(int x,int y);
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
}