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 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
// ------------------------------------------
String paintEdge(CpsEdge con, String maxCap) {
if (con!=null && con.getA() != null && con.getB() != null && con.getA().getId() != model.getSelectedObjectID() && con.getB().getId() != model.getSelectedObjectID()
&& con != edgeHighlight) {
if (con.getConnected() == CpsEdge.CON_UPPER_NODE
|| con.getConnected() == CpsEdge.CON_UPPER_NODE_AND_INSIDE) {
setEdgeState(con);
} else {
g2.setColor(Color.DARK_GRAY);
g2.setStroke(new BasicStroke(2));
}
g2.drawLine(con.getA().getPosition().x, con.getA().getPosition().y, con.getB().getPosition().x,
con.getB().getPosition().y);
maxCap = setCapacityString(con, maxCap);
if (showedInformation[0]) {
if (con.getConnected() == CpsEdge.CON_UPPER_NODE
|| con.getConnected() == CpsEdge.CON_UPPER_NODE_AND_INSIDE) {
g2.drawString(con.getFlow() + "/" + maxCap,
(con.getA().getPosition().x + con.getB().getPosition().x) / 2,
(con.getA().getPosition().y + con.getB().getPosition().y) / 2);
} else {
g2.drawString("not connected", (con.getA().getPosition().x + con.getB().getPosition().x) / 2,
(con.getA().getPosition().y + con.getB().getPosition().y) / 2);
}
}
}
return maxCap;
}
void setEdgeState(CpsEdge con) {
if (con.isWorking()) {
g2.setColor(Color.GREEN);
if (con.getCapacity() != CpsEdge.CAPACITY_INFINITE) {
g2.setStroke(new BasicStroke(Math.min(((con.getFlow() / con.getCapacity() * 3) + 1), 4)));
}
} else {
g2.setColor(Color.RED);
g2.setStroke(new BasicStroke(2));
}
}
String setCapacityString(CpsEdge con, String maxCap) {
if (con.getCapacity() == -1) {
maxCap = Character.toString('\u221e');
} else if (con.getCapacity() == -2) {
maxCap = "???";
} else {
maxCap = String.valueOf(con.getCapacity());
}
return maxCap;
}
String drawEdgeLine(CpsEdge con, String maxCap) {
if (con.getA().getId() == model.getSelectedObjectID() || model.getSelectedCpsObjects().contains(con.getA())
|| tempSelected.contains(con.getA()) || con.getB().getId() == model.getSelectedObjectID()
|| model.getSelectedCpsObjects().contains(con.getB())
|| tempSelected.contains(con.getB()) && con != edgeHighlight) {
g2.drawLine(con.getA().getPosition().x, con.getA().getPosition().y, con.getB().getPosition().x,
con.getB().getPosition().y);
maxCap = setCapacityString(con, maxCap);
if (showedInformation[0]) {
if (con.getConnected() == CpsEdge.CON_UPPER_NODE
|| con.getConnected() == CpsEdge.CON_UPPER_NODE_AND_INSIDE) {
g2.drawString(con.getFlow() + "/" + maxCap,
(con.getA().getPosition().x + con.getB().getPosition().x) / 2,
(con.getA().getPosition().y + con.getB().getPosition().y) / 2);
} else {
g2.drawString("not connected", (con.getA().getPosition().x + con.getB().getPosition().x) / 2,
(con.getA().getPosition().y + con.getB().getPosition().y) / 2);
}
}
}
return maxCap;
}
/**
* Paints the SupplyBar for the given cps object on the canvas
*
* @param g
* Graphics used
* @param cps
* cpsObject which the supplyBar should be drawn for
*/
protected void paintSupplyBar(Graphics g, AbstractCpsObject cps) {
g2.setColor(Color.black);
/**
* draw and fill the supply Bar
*/
if (model.getShowSupplyBars() && (cps instanceof HolonObject || cps instanceof HolonBattery))
{
// set Color & Percentage
/**
* percentage the SupplyBar is representing
*/
float percentage = 0;
/**
* color of the SupplyBar
*/
Color paintColor = Color.WHITE;
if(cps instanceof HolonObject)
{
HolonObject hl = (HolonObject) cps;
if (hl == null || !(hl.getState() == HolonObject.NOT_SUPPLIED
|| hl.getState() == HolonObject.PARTIALLY_SUPPLIED || hl.getState() == HolonObject.OVER_SUPPLIED)) {
/**
* don't show Bars for unsupplied oder fully supplied Objects
*/
return;
}
/**
* calculate Percentage & set Color
*/
percentage = hl.getSuppliedPercentage();
paintColor = hl.getColor();
}
else if (cps instanceof HolonBattery){
HolonBattery hB = (HolonBattery) cps;
if(hB == null || hB.getCapacity() == 0){
return;
}
/**
* get percentage filled
*/
percentage = hB.getStateOfChargeAtTimeStep(model.getCurIteration()-1) / hB.getCapacity();
/**
* calculate the Color (Red->Yellow->Green)
*/
Color color1 = Color.RED;
Color color2 = Color.GREEN;
//
float colorPercentage;
if(percentage < 0.5f){
colorPercentage = percentage * 2;
color1 = Color.RED;
color2 = Color.YELLOW;
}
else {
colorPercentage = (percentage - 0.5f) * 2;
color1 = Color.YELLOW;
color2 = Color.GREEN;
}
final int dRed = color2.getRed() - color1.getRed();
final int dGreen = color2.getGreen() - color1.getGreen();
final int dBlue = color2.getBlue() - color1.getBlue();
int resultRed = color1.getRed() + (int)(colorPercentage * dRed);
int resultGreen = color1.getGreen() + (int)(colorPercentage * dGreen);
int resultBlue = color1.getBlue() + (int)( colorPercentage * dBlue);
paintColor = new Color( resultRed,resultGreen,resultBlue);
}
/**
* Starting position x of the bar
*/
int barX = (int) (cps.getPosition().x - controller.getScaleDiv2() - scalediv20);
/**
* Starting position y of the bar
*/
int barY = (int) (cps.getPosition().y - controller.getScaleDiv2() + controller.getScale() + 1);
/**
* if object should be replaced -> move bar below the ReplacementIndicator
*/
if(mayBeReplaced==cps) barY += 3;
/**
* Width of the bar
*/
int barWidth = (int) (controller.getScale() + ((scalediv20) * 2) - 1);
/**
* Height of the bar
*/
int barHeight = (int) (controller.getScale() / 5);
/**
* draw Rectangle below the image
*/
g2.setStroke(new BasicStroke(1));
g2.drawRect(barX, barY, barWidth, barHeight);
g2.setColor(paintColor);
/** fill it accordingly if filled partially */
if (percentage < 1)
g2.fillRect(barX + 1, barY + 1, (int) ((barWidth - 1) * percentage), barHeight - 1);
else /** over supplied / supplied bar should be fully filled */
g2.fillRect(barX + 1, barY + 1, (int) (barWidth - 1), barHeight - 1);
/** write percentage */
if(percentage>1)
g2.setColor(Color.WHITE);
else
g2.setColor(Color.BLACK);
/** original font */
Font oldFont = g2.getFont();
g.setFont(new Font("TimesRoman", Font.PLAIN, (int) (barHeight * 1.5) - 2));
String percentageString = (Math.round((percentage * 100))) + "%";
int stringWidth = (int) g2.getFontMetrics().getStringBounds(percentageString, g2).getWidth();
g2.drawString(percentageString, barX + barWidth / 2 + 1 - stringWidth / 2, barY + barHeight);
/** recover Font and Color */
g2.setFont(oldFont);
g2.setColor(Color.BLACK);
}
}
void setEdgePictureAndHighlighting(AbstractCpsObject cps) {
// node image
if (cps instanceof CpsNode && (cps == tempCps || model.getSelectedCpsObject() == cps
|| model.getSelectedCpsObjects().contains(cps) || tempSelected.contains(cps))) {
img = Util.loadImage(this, "/Images/node_selected.png");
} else {
if (cps instanceof HolonSwitch) {//TODO: What the hell are thes ecalls doing here?
if (((HolonSwitch) cps).getState(model.getCurIteration())) {
((HolonSwitch) cps).setAutoState(true);
} else {
((HolonSwitch) cps).setAutoState(false);
}
}
// Highlighting
if ((cps == tempCps && model.getSelectedCpsObjects().size() == 0 && tempSelected.size() == 0)
|| model.getSelectedCpsObjects().contains(cps) || tempSelected.contains(cps)) {
/**
* draw selected Blue Frame
*/
g2.setColor(Color.BLUE);
g2.fillRect((int) (cps.getPosition().x - controller.getScaleDiv2() - scalediv20),
(int) (cps.getPosition().y - controller.getScaleDiv2() - scalediv20),
(int) (controller.getScale() + (scalediv20 * 2)),
(int) (controller.getScale() + (scalediv20 * 2)));
/**
*
*/
if(cps instanceof HolonObject){
g2.setColor(((HolonObject)cps).getColor());
g2.fillRect((int) (cps.getPosition().x - controller.getScaleDiv2() - scalediv20+3),
(int) (cps.getPosition().y - controller.getScaleDiv2() - scalediv20+3),
(int) (controller.getScale() + (scalediv20 * 2)-6),
(int) (controller.getScale() + (scalediv20 * 2)-6));
}
if (showedInformation[1] && cps instanceof HolonObject) {
g2.setColor(Color.BLACK);
float totalEnergy = ((HolonObject) cps).getCurrentEnergyAtTimeStep(model.getCurIteration());
g2.drawString(Float.toString(totalEnergy), cps.getPosition().x - controller.getScaleDiv2(),
cps.getPosition().y - controller.getScaleDiv2() - 10);
}else if (showedInformation[1] && cps instanceof HolonBattery)
{
g2.setColor(Color.BLACK);
g2.drawString(((HolonBattery) cps).getCanvasBatteryString(model.getCurIteration()), cps.getPosition().x - controller.getScaleDiv2(),
cps.getPosition().y - controller.getScaleDiv2() - 10);
}
} else if (cps instanceof HolonObject) {
g2.setColor(((HolonObject) cps).getColor());
g2.fillRect((int) (cps.getPosition().x - controller.getScaleDiv2() - scalediv20),
(int) (cps.getPosition().y - controller.getScaleDiv2() - scalediv20),
(int) (controller.getScale() + (scalediv20 * 2)),
(int) (controller.getScale() + (scalediv20 * 2)));
if (showedInformation[1]) {
g2.setColor(Color.BLACK);
float totalEnergy = ((HolonObject) cps).getCurrentEnergyAtTimeStep(model.getCurIteration());
g2.drawString(Float.toString(totalEnergy), cps.getPosition().x - controller.getScaleDiv2(),
cps.getPosition().y - controller.getScaleDiv2() - 10);
}
}else if (cps instanceof HolonBattery) {
if (showedInformation[1]) {
g2.setColor(Color.BLACK);
g2.drawString(((HolonBattery) cps).getCanvasBatteryString(model.getCurIteration()), cps.getPosition().x - controller.getScaleDiv2(),
cps.getPosition().y - controller.getScaleDiv2() - 10);
}
}
// set image
if(cps instanceof HolonBattery)
{
img = Util.loadImage(this, ((HolonBattery) cps).getImageBattery());
}else
{
img = Util.loadImage(this, cps.getImage());
}
}
}
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);
}
}
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 setConsoleTextAfterSelect(AbstractCpsObject cps) {
if (model.getShowConsoleLog()) {
controller.addTextToConsole("Selected: ", Color.BLACK, 12, false, false, false);
controller.addTextToConsole("" + cps.getName(), Color.BLUE, 12, true, false, false);
controller.addTextToConsole(", ID:", Color.BLACK, 12, false, false, false);
controller.addTextToConsole("" + cps.getId(), Color.RED, 12, true, false, true);
}
}
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);
}