package ui.view;
import classes.*;
import ui.controller.Control;
import ui.controller.UpdateController;
import ui.model.Model;
import javax.swing.*;
import javax.swing.table.JTableHeader;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.TimerTask;
/**
* Collection of methods and values needed in both MyCanvas
and
* UpperNodeCanvas
*
* Although Java works on references we chose to add explicit return values for
* clearer code understanding in most cases
*
* @author: I. Dix
*/
public abstract class AbstractCanvas extends JPanel {
/**
* Version
*/
private static final long serialVersionUID = 1L;
final JMenuItem itemDelete = new JMenuItem(Languages.getLanguage()[98]);
final JMenuItem itemCut = new JMenuItem(Languages.getLanguage()[95]);
final JMenuItem itemCopy = new JMenuItem(Languages.getLanguage()[96]);
final JMenuItem itemPaste = new JMenuItem(Languages.getLanguage()[97]);
final JMenuItem itemGroup = new JMenuItem(Languages.getLanguage()[99]);
final JMenuItem itemUngroup = new JMenuItem(Languages.getLanguage()[100]);
final JMenuItem itemTrack = new JMenuItem(Languages.getLanguage()[101]);
final JMenuItem itemUntrack = new JMenuItem(Languages.getLanguage()[102]);
final JMenuItem itemAlign = new JMenuItem("Align selected");
final JMenuItem itemCreateTemplate = new JMenuItem(Languages.getLanguage()[Languages.right_click_create_template]);
final int ANIMTIME = 500; // animation Time
private final int animFPS = 60;
final int animDelay = 1000 / animFPS; // animation Delay
protected Model model;
protected Control controller;
protected int x = 0;
protected int y = 0;
// Selection
AbstractCpsObject tempCps = null;
UpdateController updCon;
//Replacement
/**
* the CpsObject that might be replaced by drag&drop
*/
protected AbstractCpsObject mayBeReplaced = null;
// PopUpMenu
JPopupMenu popmenu = new JPopupMenu();
// Tooltip
boolean toolTip; // Tooltip on or off
Position toolTipPos = new Position(); // Tooltip Position
String toolTipText = "";
ArrayList dataSelected = new ArrayList<>();
ArrayList tempSelected = new ArrayList<>();
boolean[] showedInformation = new boolean[5];
boolean showConnectionInformation;
boolean dragging = false; // for dragging
boolean dragged = false; // if an object/objects was/were dragged
boolean drawEdge = false; // for drawing edges
boolean doMark = false; // for double click
CpsEdge edgeHighlight = null;
Point mousePosition = new Point(); // Mouse Position when
ArrayList savePos;
// edge Object Start Point
int cx, cy;
int sx, sy; // Mark Coords
Position unPos;
// Animation
Timer animT; // animation Timer
int animDuration = ANIMTIME; // animation Duration
int animSteps = animDuration / animDelay; // animation Steps;
ArrayList animCps = null;
// Graphics
Image img = null; // Contains the image to draw on the Canvas
Graphics2D g2; // For Painting
float scalediv20;
// Mouse
private boolean click = false;
// ------------------------------------------ METHODS
// ------------------------------------------
class ACpsHandle{
public AbstractCpsObject object;
ACpsHandle(AbstractCpsObject object){
this.object = object;
}
public String toString() {
return object.toString();
}
}
void drawMarker() {
if (sx > x && sy > y) {
g2.drawRect(x, y, sx - x, sy - y);
} else if (sx < x && sy < y) {
g2.drawRect(sx, sy, x - sx, y - sy);
} else if (sx >= x) {
g2.drawRect(x, sy, sx - x, y - sy);
} else if (sy >= y) {
g2.drawRect(sx, y, x - sx, sy - y);
}
}
/**
* @deprecated
* @param g
*/
void showTooltip(Graphics g) {
if (toolTip) {
g2.setColor(new Color(255, 225, 150));
g2.setStroke(new BasicStroke(1));
int textWidth = g.getFontMetrics().stringWidth(toolTipText) + 2; // Text
// width
// fixed x and y Position to the screen
int fixXPos = toolTipPos.x - (textWidth >> 1) + model.getScaleDiv2();
int fixYPos = toolTipPos.y;
if (fixXPos < 0) {
fixXPos = 0;
} else if (fixXPos + textWidth + 1 > this.getWidth()) {
fixXPos -= (fixXPos + textWidth + 1) - this.getWidth();
}
if (fixYPos + 16 > this.getHeight()) {
fixYPos -= (fixYPos + 16) - this.getHeight();
}
g2.fillRect(fixXPos, fixYPos, textWidth, 15);
g2.setColor(Color.BLACK);
g2.drawRect(fixXPos, fixYPos, textWidth, 15);
g2.drawString(toolTipText, fixXPos + 2, fixYPos + 12);
}
}
void setRightClickMenu(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
itemPaste.setEnabled(true);
if (tempCps != null) {
itemPaste.setEnabled(true);
itemDelete.setEnabled(true);
itemCut.setEnabled(true);
itemCopy.setEnabled(true);
itemAlign.setEnabled(true);
// tracking
if (tempCps != null) {
itemGroup.setEnabled(true);
itemTrack.setEnabled(true);
itemUntrack.setEnabled(true);
}
// ungrouping
if (tempCps instanceof CpsUpperNode)
itemUngroup.setEnabled(true);
else
itemUngroup.setEnabled(false);
if (model.getSelectedCpsObjects().size() == 0) {
controller.addSelectedObject(tempCps);
}
if (tempCps instanceof HolonObject) {
itemCreateTemplate.setEnabled(true);
} else {
itemCreateTemplate.setEnabled(false);
}
} else {
itemAlign.setEnabled(false);
itemCut.setEnabled(false);
itemCopy.setEnabled(false);
itemGroup.setEnabled(false);
itemUngroup.setEnabled(false);
itemTrack.setEnabled(false);
itemUntrack.setEnabled(false);
itemCreateTemplate.setEnabled(false);
if (edgeHighlight != null) {
itemDelete.setEnabled(true);
itemPaste.setEnabled(false);
} else {
itemDelete.setEnabled(false);
itemPaste.setEnabled(true);
}
}
mousePosition = this.getMousePosition();
popmenu.show(e.getComponent(), e.getX(), e.getY());
}
}
void markObjects() {
if (doMark) {
doMark = false;
for (AbstractCpsObject cps : tempSelected) {
if (!model.getSelectedCpsObjects().contains(cps)) {
controller.addSelectedObject(cps);
}
}
controller.getObjectsInDepth();
tempSelected.clear();
}
}
int[] determineMousePositionOnEdge(CpsEdge p) {
int lx, ly, hx, hy;
if (p.getA().getPosition().x > p.getB().getPosition().x) {
hx = p.getA().getPosition().x + model.getScaleDiv2() + 7;
lx = p.getB().getPosition().x + model.getScaleDiv2() - 7;
} else {
lx = p.getA().getPosition().x + model.getScaleDiv2() - 7;
hx = p.getB().getPosition().x + model.getScaleDiv2() + 7;
}
if (p.getA().getPosition().y > p.getB().getPosition().y) {
hy = p.getA().getPosition().y + model.getScaleDiv2() + 7;
ly = p.getB().getPosition().y + model.getScaleDiv2() - 7;
} else {
ly = p.getA().getPosition().y + model.getScaleDiv2() - 7;
hy = p.getB().getPosition().y + model.getScaleDiv2() + 7;
}
return new int[] { lx, ly, hx, hy };
}
/**
* Checks if a double click was made.
*
* @return true if doublecklick, false if not
*/
boolean doubleClick() {
if (click) {
click = false;
return true;
} else {
click = true;
java.util.Timer t = new java.util.Timer("doubleclickTimer", false);
t.schedule(new TimerTask() {
@Override
public void run() {
click = false;
}
}, 500);
}
return false;
}
boolean setToolTipInfoAndPosition(boolean on, AbstractCpsObject cps) {
if (x - controller.getScale() <= cx && y - controller.getScale() <= cy && x >= cx && y >= cy) {
on = true;
toolTipPos.x = cps.getPosition().x - controller.getScaleDiv2();
toolTipPos.y = cps.getPosition().y + controller.getScaleDiv2();
toolTipText = cps.getName() + ", " + cps.getId();
}
return on;
}
abstract void drawDeleteEdge();
void triggerUpdateController() {
updCon.paintProperties(tempCps);
updCon.refreshTableHolonElement(model.getMultiTable(), model.getSingleTable());
updCon.refreshTableProperties(model.getPropertyTable());
}
/**
* Checks if {@code draggedCps} or a new cpsObject at Position (x,y) could replace exactly one object
* in {@code objects}.
* Saves the object that would be replaced in {@link AbstractCanvas}.{@code MayBeReplaced}
* @param objects list of objects that could be replaced
* @param draggedCps Object that might replace
* @param x Position of the objects that might replace
* @param y Position of the objects that might replace
* @return true if exactly one Object could be replaced
*/
protected boolean checkForReplacement(ArrayList objects, AbstractCpsObject draggedCps, int x, int y){
/** distance treshold for replacement */
int treshhold = controller.getScale()/2;
/** number of Objects that might be replaced (should be 1) */
int replaceCounter = 0;
/** last object that could be replaced */
AbstractCpsObject toBeReplaced = null;
/** Position of object that might be replaced */
Position p;
/** for each cps on Canvas */
if(draggedCps == null || !(draggedCps instanceof CpsNode) && !(draggedCps instanceof CpsNode)){
for (AbstractCpsObject cps : objects){
/** same object -> ignore */
if(cps == draggedCps)continue;
/** set Position of object that might be replaced */
p = cps.getPosition();
/** if near enough */
if(Math.abs(x-p.x)1)break;
}
}
}
/**
* return true if exactly one obect would be replaced
*/
if( replaceCounter == 1 && toBeReplaced != null){
mayBeReplaced = toBeReplaced;
return true;
}else{
mayBeReplaced = null;
return false;
}
}
/**
* Checks if an inserted new Object could replace exactly one object on the canvas.
* Saves the object that would be replaced in {@link AbstractCanvas}.{@code MayBeReplaced}
* @param x Position of the objects that might replace
* @param y Position of the objects that might replace
* @return true if exactly one Object could be replaced
*/
public abstract boolean checkForReplacement(int x, int y);
/**
* highlights the object that mayBeReplaced
* @param g2
*/
protected void highlightMayBeReplaced(Graphics2D g2) {
if(mayBeReplaced != null){
g2.setColor(Color.RED);
g2.fillRect(
(int) (mayBeReplaced.getPosition().x
- controller.getScaleDiv2() - (scalediv20 + 3)),
(int) (mayBeReplaced.getPosition().y
- controller.getScaleDiv2() - (scalediv20 + 3)),
(int) (controller.getScale() + ((scalediv20 + 3) * 2)),
(int) (controller.getScale() + ((scalediv20 + 3) * 2)));
}
}
/**
* Align alle Objects on the Canvas to a Grid with objects every 10 pixels
*/
public abstract void tryToAlignObjects();
/**
* Aligns the Object the a grid
* @param cps Object that should be aligned
* @param distance distance between the AlignmentGrid Lines. (objects every 'distance' pixels
*/
protected void align(AbstractCpsObject cps, int distance) {
/** Position of the AbstractCpsObject which should be aligned */
Position p = cps.getPosition();
//calculate how many pixels the cps should be decreased to align
/** x offset relative to a grid with lines every distance pixels */
int x_off = cps.getPosition().x % distance;
/** y offset relative to a grid with lines every distance pixels */
int y_off = cps.getPosition().y % distance;
//align to the other Line, if it is nearer
if(x_off > distance/2)
x_off -= distance;
if(y_off > distance/2)
y_off -= distance;
/** set new Position */
cps.setPosition(p.x-x_off, p.y-y_off);
}
/**
* Stops Editing in HolonElementTable and PropertyTable
*/
protected void stopEditing() {
/**
* Stop Editing, if mouse exits the Table
*/
JTable holElem = model.getTableHolonElement();
CellEditor cellEditor = holElem.getCellEditor();
if (cellEditor != null) {
if (cellEditor.getCellEditorValue() != null) {
/** TODO: Maybe try to save current Data */
cellEditor.stopCellEditing();
} else {
cellEditor.cancelCellEditing();
}
}
JTable propertys = model.getTableProperties();
cellEditor = propertys.getCellEditor();
if (cellEditor != null) {
if (cellEditor.getCellEditorValue() != null) {
/** TODO: Maybe try to save current Data */
cellEditor.stopCellEditing();
} else {
cellEditor.cancelCellEditing();
}
}
}
/**
* Closes a tab of the UpperNode with ID upperNodeID
* @param upperNodeId
*/
public abstract void closeUpperNodeTab(int upperNodeId);
}