package com.baselet.element.old;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import javax.swing.JComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.baselet.control.HandlerElementMap;
import com.baselet.control.Main;
import com.baselet.control.SharedUtils;
import com.baselet.control.basics.Converter;
import com.baselet.control.basics.geom.Dimension;
import com.baselet.control.basics.geom.Line;
import com.baselet.control.basics.geom.Point;
import com.baselet.control.basics.geom.Rectangle;
import com.baselet.control.config.SharedConfig;
import com.baselet.control.constants.Constants;
import com.baselet.control.enums.AlignHorizontal;
import com.baselet.control.enums.Direction;
import com.baselet.control.enums.ElementId;
import com.baselet.control.enums.LineType;
import com.baselet.control.util.Utils;
import com.baselet.diagram.DiagramHandler;
import com.baselet.diagram.draw.DrawHandler;
import com.baselet.diagram.draw.helper.ColorOwn;
import com.baselet.diagram.draw.helper.ColorOwn.Transparency;
import com.baselet.element.ElementUtils;
import com.baselet.element.NewGridElement;
import com.baselet.element.UndoHistory;
import com.baselet.element.UndoInformation;
import com.baselet.element.facet.KeyValueFacet;
import com.baselet.element.facet.common.GroupFacet;
import com.baselet.element.facet.common.LayerFacet;
import com.baselet.element.interfaces.CursorOwn;
import com.baselet.element.interfaces.GridElement;
import com.baselet.element.interfaces.GridElementDeprecatedAddons;
import com.baselet.element.sticking.PointChange;
import com.baselet.element.sticking.Stickable;
import com.baselet.element.sticking.StickableMap;
import com.baselet.element.sticking.Stickables;
import com.baselet.element.sticking.StickingPolygon;
import com.baselet.gui.AutocompletionText;
public abstract class OldGridElement extends JComponent implements GridElement, com.baselet.element.interfaces.Component {
private static final long serialVersionUID = 1L;
protected static final Logger log = LoggerFactory.getLogger(OldGridElement.class);
public static final float ALPHA_MIDDLE_TRANSPARENCY = 0.5f;
public static final float ALPHA_FULL_TRANSPARENCY = 0.0f;
private boolean enabled;
private boolean autoresizeandmanualresizeenabled;
private List<String> panelAttributes = new ArrayList<String>();
// deselectedColor and fgColor must be stored separately because selection changes the actual fgColor but not the fgColorBase
/**
* contains the value of the fgColor of the element if not selected
*/
protected Color fgColorBase = Converter.convert(ColorOwn.BLACK);
/**
* contains the current fgColor of the element. Will be overwritten by selectioncolor if it's selected
*/
protected Color fgColor = fgColorBase;
private String fgColorString = "";
protected Color bgColor = Converter.convert(ColorOwn.WHITE);
private String bgColorString = "";
protected float alphaFactor;
protected final UndoHistory undoStack = new UndoHistory();
public OldGridElement() {
this.setSize(100, 100);
setVisible(true);
enabled = true;
autoresizeandmanualresizeenabled = false;
}
@Override
public void setEnabled(boolean en) {
super.setEnabled(en);
if (!en && enabled) {
removeMouseListener(getDiagramHandler().getEntityListener(this));
removeMouseMotionListener(getDiagramHandler().getEntityListener(this));
enabled = false;
}
else if (en && !enabled) {
addMouseListener(getDiagramHandler().getEntityListener(this));
addMouseMotionListener(getDiagramHandler().getEntityListener(this));
enabled = true;
}
}
public boolean isManualResized() {
autoresizeandmanualresizeenabled = true;
return isManResized();
}
private boolean isManResized() {
Vector<String> lines = Utils.decomposeStringsWithComments(getPanelAttributes());
for (String line : lines) {
if (line.startsWith("autoresize=false")) {
return true;
}
}
return false;
}
protected boolean isAutoResizeandManualResizeEnabled() {
return autoresizeandmanualresizeenabled;
}
public void setManualResized() {
if (autoresizeandmanualresizeenabled) {
if (!isManResized()) {
setPanelAttributes(getPanelAttributes() + Constants.NEWLINE + "autoresize=false");
if (equals(Main.getInstance().getEditedGridElement())) {
Main.getInstance().setPropertyPanelToGridElement(this);
}
}
}
}
// Some GridElements need additionalAttributes to be displayed correctly (eg: Relations need exact positions for edges)
@Override
public String getAdditionalAttributes() {
return "";
}
@Override
public void setAdditionalAttributes(String s) {}
@Override
public String getPanelAttributes() {
return SharedUtils.listToString("\n", panelAttributes);
}
@Override
public List<String> getPanelAttributesAsList() {
return panelAttributes;
}
@Override
public void setPanelAttributes(String panelAttributes) {
this.panelAttributes = Arrays.asList(panelAttributes.split("\n", -1)); // split with -1 to retain empty lines at the end
}
@Override
public String getSetting(String key) {
for (String line : getPanelAttributesAsList()) {
if (line.startsWith(key + KeyValueFacet.SEP)) {
String[] split = line.split(KeyValueFacet.SEP, 2);
if (split.length > 1) {
return split[1];
}
}
}
return null;
}
@Override
public void setProperty(String key, Object newValue) {
StringBuilder sb = new StringBuilder("");
for (String line : panelAttributes) {
if (!line.startsWith(key.toString())) {
sb.append(line).append("\n");
}
}
if (sb.length() > 0) {
sb.setLength(sb.length() - 1); // remove last linebreak
}
if (newValue != null && !newValue.toString().isEmpty()) {
sb.append("\n").append(key.toString()).append("=").append(newValue); // null will not be added as a value
}
setPanelAttributes(sb.toString());
}
public Composite[] colorize(Graphics2D g2) {
bgColorString = "";
fgColorString = "";
bgColor = getDefaultBackgroundColor();
fgColorBase = Converter.convert(ColorOwn.DEFAULT_FOREGROUND);
List<String> v = panelAttributes;
for (int i = 0; i < v.size(); i++) {
String line = v.get(i);
if (line.indexOf("bg=") >= 0) {
bgColorString = line.substring("bg=".length());
// OldGridElements apply transparency for background explicitly, therefore don't apply it here
bgColor = Converter.convert(ColorOwn.forStringOrNull(bgColorString, Transparency.FOREGROUND));
if (bgColor == null) {
bgColor = getDefaultBackgroundColor();
}
}
else if (line.indexOf("fg=") >= 0) {
fgColorString = line.substring("fg=".length());
fgColorBase = Converter.convert(ColorOwn.forStringOrNull(fgColorString, Transparency.FOREGROUND));
if (fgColorBase == null) {
fgColorBase = Converter.convert(ColorOwn.DEFAULT_FOREGROUND);
}
if (!getDiagramHandler().getDrawPanel().getSelector().isSelected(this)) {
fgColor = fgColorBase;
}
}
}
alphaFactor = ALPHA_MIDDLE_TRANSPARENCY;
if (bgColorString.equals("") || bgColorString.equals("default")) {
alphaFactor = ALPHA_FULL_TRANSPARENCY;
}
Composite old = g2.getComposite();
AlphaComposite alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alphaFactor);
Composite[] composites = { old, alpha };
return composites;
}
protected Color getDefaultBackgroundColor() {
return Converter.convert(ColorOwn.DEFAULT_BACKGROUND);
}
@Override
public Dimension getRealSize() {
return new Dimension(getRectangle().width / getDiagramHandler().getGridSize() * Constants.DEFAULTGRIDSIZE, getRectangle().height / getDiagramHandler().getGridSize() * Constants.DEFAULTGRIDSIZE);
}
@Override
public Set<Direction> getResizeArea(int x, int y) {
Set<Direction> returnSet = new HashSet<Direction>();
if (x <= 5 && x >= 0) {
returnSet.add(Direction.LEFT);
}
else if (x <= getRectangle().width && x >= getRectangle().width - 5) {
returnSet.add(Direction.RIGHT);
}
if (y <= 5 && y >= 0) {
returnSet.add(Direction.UP);
}
else if (y <= getRectangle().height && y >= getRectangle().height - 5) {
returnSet.add(Direction.DOWN);
}
return returnSet;
}
@Override
public void setLocationDifference(int diffx, int diffy) {
this.setLocation(getRectangle().x + diffx, getRectangle().y + diffy);
}
/**
* Must be overwritten because Swing uses this method to tell if 2 elements are overlapping
* It's also used to determine which element gets selected if there are overlapping elements (the smallest one)
* IMPORTANT: on overlapping elements, contains is called for all elements until the first one returns true, then the others contain methods are not called
*/
@Override
public boolean contains(java.awt.Point p) {
return this.contains(p.x, p.y);
}
/**
* Must be overwritten because Swing sometimes uses this method instead of contains(Point)
*/
@Override
public boolean contains(int x, int y) {
return ElementUtils.checkForOverlap(this, new Point(x, y));
}
@Override
public boolean isInRange(Rectangle rect1) {
return rect1.contains(getRectangle());
}
public void setInProgress(Graphics g, boolean flag) {
if (flag) {
Graphics2D g2 = (Graphics2D) g;
g2.setFont(getDiagramHandler().getFontHandler().getFont());
g2.setColor(Color.red);
getDiagramHandler().getFontHandler().writeText(g2, "in progress...", getRectangle().width / 2 - 40, getRectangle().height / 2 + (int) getDiagramHandler().getFontHandler().getFontSize() / 2, AlignHorizontal.LEFT);
}
else {
repaint();
}
}
public GridElement cloneFromMe() {
try {
java.lang.Class<? extends GridElement> cx = this.getClass(); // get class of dynamic object
GridElement c = cx.newInstance();
c.setPanelAttributes(getPanelAttributes()); // copy states
c.setRectangle(getRectangle());
getDiagramHandler().setHandlerAndInitListeners(c);
return c;
} catch (Exception e) {
log.error("Error at calling CloneFromMe() on entity", e);
}
return null;
}
private DiagramHandler getDiagramHandler() {
return HandlerElementMap.getHandlerForElement(this);
}
@Override
public StickingPolygon generateStickingBorder(Rectangle rect) {
return generateStickingBorder(rect.x, rect.y, rect.width, rect.height);
}
public StickingPolygon generateStickingBorder(int x, int y, int width, int height) {
StickingPolygon p = new StickingPolygon(x, y);
p.addRectangle(0, 0, width, height);
return p;
}
public final void drawStickingPolygon(Graphics2D g2) {
Rectangle rect = new Rectangle(0, 0, getRectangle().width - 1, getRectangle().height - 1);
StickingPolygon poly = this.generateStickingBorder(rect);
if (poly != null) {
Color c = g2.getColor();
Stroke s = g2.getStroke();
g2.setColor(Converter.convert(ColorOwn.SELECTION_FG));
g2.setStroke(Utils.getStroke(LineType.DASHED, 1));
for (Line line : poly.getStickLines()) {
g2.drawLine(line.getStart().getX().intValue(), line.getStart().getY().intValue(), line.getEnd().getX().intValue(), line.getEnd().getY().intValue());
}
g2.setColor(c);
g2.setStroke(s);
}
}
private boolean translateForExport = false;
@Override
public void translateForExport() {
translateForExport = true;
}
public boolean isDeprecated() {
return true;
}
@Override
public final void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
if (translateForExport) {
g2.translate(Constants.EXPORT_DISPLACEMENT, Constants.EXPORT_DISPLACEMENT);
}
boolean selected = getDiagramHandler().getDrawPanel().getSelector().isSelected(this);
if (selected) {
if (SharedConfig.getInstance().isDev_mode()) {
Color oldColor = g2.getColor();
g2.setColor(Converter.convert(ColorOwn.BLACK));
String text = "Type: " + getClass().getName();
g2.drawString(text, getWidth() - (int) getDiagramHandler().getFontHandler().getTextWidth(text), getHeight() - 5);
g2.setColor(oldColor);
}
if (isDeprecated()) {
Color oldColor = g2.getColor();
g2.setColor(Converter.convert(ColorOwn.RED.transparency(Transparency.SELECTION_BACKGROUND)));
g2.fillRect(0, 0, getWidth(), getHeight());
g2.setColor(oldColor);
g2.setColor(Converter.convert(ColorOwn.RED.transparency(Transparency.DEPRECATED_WARNING)));
g2.drawString("DEPRECATED ELEMENT", 10, 15);
g2.drawString("WILL SOON BE REMOVED", 10, 30);
}
fgColor = Converter.convert(ColorOwn.SELECTION_FG);
if (SharedConfig.getInstance().isShow_stickingpolygon()) {
drawStickingPolygon(g2);
}
}
else {
fgColor = fgColorBase;
}
updateModelFromText();
paintEntity(g2);
}
public abstract void paintEntity(Graphics g);
@Override
public com.baselet.element.interfaces.Component getComponent() {
return this;
}
@Override
public List<AutocompletionText> getAutocompletionList() {
return new ArrayList<AutocompletionText>();
}
@Override
public void updateModelFromText() {
/* OldGridElement has no model but simply parses the properties text within every paint() call */
}
@Override
public Integer getLayer() {
return getLayerHelper(LayerFacet.DEFAULT_VALUE);
}
protected Integer getLayerHelper(Integer defaultLayer) {
try {
return Integer.valueOf(getSettingHelper(LayerFacet.KEY, defaultLayer.toString()));
} catch (NumberFormatException e) {/* default value applies */}
return defaultLayer;
}
private String getSettingHelper(String key, String defaultValue) {
key = key + KeyValueFacet.SEP;
for (String s : panelAttributes) {
if (s.startsWith(key)) {
String[] value = s.split(key);
if (value.length == 0) {
return "";
}
return value[1];
}
}
return defaultValue;
}
@Override
public Integer getGroup() {
try {
return Integer.valueOf(getSettingHelper(GroupFacet.KEY, null));
} catch (NumberFormatException e) {/* default value applies */}
return null;
}
@Override
public Rectangle getRectangle() {
return Converter.convert(getBounds());
}
@Override
public void setRectangle(Rectangle rect) {
setBounds(rect.x, rect.y, rect.width, rect.height);
}
@Override
public void setBoundsRect(Rectangle rect) {
setBounds(Converter.convert(rect));
}
@Override
public Rectangle getBoundsRect() {
return Converter.convert(getBounds());
}
@Override
public void repaintComponent() {
this.repaint();
}
@Override
public DrawHandler getDrawHandler() {
return null;
}
@Override
public DrawHandler getMetaDrawHandler() {
return null;
}
@Override
public ElementId getId() {
return null;
}
@Override
public void drag(Collection<Direction> resizeDirection, int diffX, int diffY, Point mousePosBeforeDrag, boolean isShiftKeyDown, boolean firstDrag, StickableMap stickables, boolean undoable) {
Rectangle oldRect = getRectangle();
StickingPolygon stickingPolygonBeforeLocationChange = generateStickingBorder();
String oldAddAttr = getAdditionalAttributes();
if (resizeDirection.isEmpty()) { // Move GridElement
setLocationDifference(diffX, diffY);
}
else { // Resize GridElement
Rectangle rect = getRectangle();
if (isShiftKeyDown && diagonalResize(resizeDirection)) { // Proportional Resize
boolean mouseToRight = diffX > 0 && diffX > diffY;
boolean mouseDown = diffY > 0 && diffY > diffX;
boolean mouseLeft = diffX < 0 && diffX < diffY;
boolean mouseUp = diffY < 0 && diffY < diffX;
if (mouseToRight || mouseLeft) {
diffY = diffX;
}
if (mouseDown || mouseUp) {
diffX = diffY;
}
}
if (resizeDirection.contains(Direction.LEFT) && resizeDirection.contains(Direction.RIGHT)) {
rect.setX(rect.getX() - diffX / 2);
rect.setWidth(Math.max(rect.getWidth() + diffX, minSize()));
}
else if (resizeDirection.contains(Direction.LEFT)) {
int newWidth = rect.getWidth() - diffX;
if (newWidth >= minSize()) {
rect.setX(rect.getX() + diffX);
rect.setWidth(newWidth);
}
}
else if (resizeDirection.contains(Direction.RIGHT)) {
rect.setWidth(Math.max(rect.getWidth() + diffX, minSize()));
}
if (resizeDirection.contains(Direction.UP)) {
int newHeight = rect.getHeight() - diffY;
if (newHeight >= minSize()) {
rect.setY(rect.getY() + diffY);
rect.setHeight(newHeight);
}
}
if (resizeDirection.contains(Direction.DOWN)) {
rect.setHeight(Math.max(rect.getHeight() + diffY, minSize()));
}
setRectangle(rect);
updateModelFromText();
}
moveStickables(stickables, undoable, oldRect, stickingPolygonBeforeLocationChange, oldAddAttr);
}
private void moveStickables(StickableMap stickables, boolean undoable, Rectangle oldRect, StickingPolygon stickingPolygonBeforeLocationChange, String oldAddAttr) {
Map<Stickable, List<PointChange>> stickableChanges = Stickables.moveStickPointsBasedOnPolygonChanges(stickingPolygonBeforeLocationChange, generateStickingBorder(), stickables, getGridSize());
if (undoable) {
undoStack.add(new UndoInformation(getRectangle(), oldRect, stickableChanges, getGridSize(), oldAddAttr, getAdditionalAttributes()));
}
}
public int getGridSize() {
return getDiagramHandler().getGridSize();
}
@Override
public void afterModelUpdate() {}
@Override
public boolean isSelectableOn(Point point) {
return getRectangle().contains(point);
}
private boolean diagonalResize(Collection<Direction> resizeDirection) {
return resizeDirection.contains(Direction.UP) && resizeDirection.contains(Direction.RIGHT) ||
resizeDirection.contains(Direction.UP) && resizeDirection.contains(Direction.LEFT) ||
resizeDirection.contains(Direction.DOWN) && resizeDirection.contains(Direction.LEFT) ||
resizeDirection.contains(Direction.DOWN) && resizeDirection.contains(Direction.RIGHT);
}
@Override
public void dragEnd() {
// only used by some specific elements like Relations
}
@Override
public void setRectangleDifference(int diffx, int diffy, int diffw, int diffh, boolean firstDrag, StickableMap stickables, boolean undoable) {
Rectangle oldRect = getRectangle();
StickingPolygon stickingPolygonBeforeLocationChange = generateStickingBorder();
String oldAddAttr = getAdditionalAttributes();
setRectangle(new Rectangle(oldRect.x + diffx, oldRect.y + diffy, oldRect.getWidth() + diffw, oldRect.getHeight() + diffh));
moveStickables(stickables, undoable, oldRect, stickingPolygonBeforeLocationChange, oldAddAttr);
}
@Override
public StickingPolygon generateStickingBorder() {
return generateStickingBorder(getRectangle());
}
private int minSize() {
return getDiagramHandler().getGridSize() * 2;
}
@Override
public void undoDrag() {
execUndoInformation(true);
}
private void execUndoInformation(boolean undo) {
UndoInformation undoInfo = undoStack.get(undo);
if (undoInfo != null) {
setRectangle(getRectangle().add(undoInfo.getDiffRectangle(getGridSize(), undo)));
Stickables.applyChanges(undoInfo.getStickableMoves(undo), null);
// setAdditionalAttributes(undoInfo.getAdditionalAttributes(undo)); //Issue 217: of all OldGridElements only the Relation uses additional attributes and in that case they must not be set because of zooming errors (see Issue 217)
}
}
@Override
public void redoDrag() {
execUndoInformation(false);
}
@Override
public void mergeUndoDrag() {
UndoInformation undoInfoA = undoStack.remove();
UndoInformation undoInfoB = undoStack.remove();
undoStack.add(undoInfoA.merge(undoInfoB));
}
@Override
public GridElementDeprecatedAddons getDeprecatedAddons() {
return GridElementDeprecatedAddons.NONE;
}
@Override
public CursorOwn getCursor(Point point, Set<Direction> resizeDirections) {
return NewGridElement.getCursorStatic(this, point, resizeDirections);
}
}