package ch.rakudave.NetMap;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.JTextField;
@Deprecated
public class NetCanvas extends Canvas {
protected Vector<NetItem> items;
protected Vector<NetLine2> lines;
protected Vector<BackgroundImage> backgroundImages;
protected Vector<NetNote> notes;
protected BufferedImage img;
protected Graphics2D g2;
protected boolean documentChanged, exportPaint;
protected int[] stats = new int[4];
protected int historyPosition, button, zoom;
protected Point displacement;
protected Preferences preferences;
protected NetMap parent;
protected Vector<NetItem> selected, prevSelected;
protected double zoomFactor = 1;
private Vector<HistoryItem> history;
private NetLine2 selectedLine;
private BackgroundImage selectedImage;
private NetNote selectedNote;
private Point linePosition, midWay, gridPoint, rectStart, rectStop;
private Font mono;
private Color blueish, whiteish, gridColor, yellowish;
private BasicStroke normal, legend, bold, highlight;
/**
* Creates a new canvas. It interacts with the user and displays all registered devices.
*
* Comment: This canvas doesn't repaint itself to reduce CPU load. It's not an animation and only needs to be
* repainted when editing and pinging.
*/
public NetCanvas(NetMap parent, final Preferences preferences) {
super();
// initialize variables, set defaults
this.parent = parent;
this.preferences = preferences;
img = new BufferedImage(800, 700, BufferedImage.TYPE_INT_RGB);
normal = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
legend = new BasicStroke(5.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
bold = new BasicStroke(5.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
highlight = new BasicStroke(15.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
g2 = img.createGraphics();
items = new Vector<NetItem>();
selected = new Vector<NetItem>();
prevSelected = new Vector<NetItem>();
lines = new Vector<NetLine2>();
notes = new Vector<NetNote>();
backgroundImages = new Vector<BackgroundImage>();
history = new Vector<HistoryItem>(preferences.undo-1);
historyPosition = -1;
midWay = new Point(); gridPoint = new Point(); displacement = new Point();
mono = new Font(Font.MONOSPACED, Font.BOLD, 12);
blueish = new Color(200, 200, 255, 150);
gridColor = new Color(200, 200, 200, 50);
whiteish = new Color(255, 255, 255, 175);
yellowish = new Color(241, 233, 104, 175);
// listen to mouse/key actions on the canvas
this.addMouseListener(new MouseListener() {
@Override public void mouseClicked(MouseEvent e) {clicked(e);}
@Override public void mouseEntered(MouseEvent e) {}
@Override public void mouseExited(MouseEvent e) {}
@Override public void mousePressed(MouseEvent e) {button = e.getButton();}
@Override public void mouseReleased(MouseEvent e) {released(e);}});
this.addMouseMotionListener(new MouseMotionListener() {
@Override public void mouseDragged(MouseEvent e) {dragged(e);}
@Override public void mouseMoved(MouseEvent e) {if (linePosition != null) {linePosition = e.getPoint();repaint();}}});
this.addKeyListener(new KeyListener() {
@Override public void keyPressed(KeyEvent e) {keypress(e);}
@Override public void keyReleased(KeyEvent e) {}
@Override public void keyTyped(KeyEvent e) {}});
this.addMouseWheelListener(new MouseWheelListener() {
@Override public void mouseWheelMoved(MouseWheelEvent e) {
if (zoom-e.getWheelRotation() == -10 || zoom-e.getWheelRotation() == 10) return;
else zoom -= e.getWheelRotation();
double factor = (e.getWheelRotation() > 0)?0.9:10.0/9.0;
zoomFactor *= factor;
g2.scale(factor, factor);
repaint();//TODO
}});
}
/**
* Display an empty settings dialog to register a new device
*/
public void addItem(MouseEvent me) {
Settings.dialog(this, null, me);
repaint();
}
/**
* Align an item to an imaginary grid (cell space 50x50)
* If the item is <i>null</i>, all items will be aligned.
* If one or more items are selected, align them to the grid.
*
* @param item NetItem to align or null for all
*/
public void alignToGrid(NetItem item) {
if (item != null) { // align single item
gridPoint.x = item.getPosition().x - modulo(item.getPosition().x-modulo(displacement.x,50)+25,50);
gridPoint.y = item.getPosition().y - modulo(item.getPosition().y-modulo(displacement.y,50)+25,50);
item.setPosition(gridPoint, -26);
} else { // align all or selected items
if (selected.size() != 0) for (NetItem iitem : selected) alignToGrid(iitem);
else for (NetItem iitem : items) alignToGrid(iitem);
}
repaint();
}
/**
* Removes all items from the canvas, reset the history
*/
public void clear() {
items.clear();
lines.clear();
history.clear();
backgroundImages.clear();
notes.clear();
historyPosition = -1;
stats = new int[4];
updateHistory();
repaint();
}
private void clicked(MouseEvent e) {
findSelectedItem(e);
findSelectedBackgroundImage(e);
if (e.getButton() == MouseEvent.BUTTON3) {
showPopup(e); // right-click, show context-menu
} else if (e.getButton() == MouseEvent.BUTTON2 && linePosition == null) {
connect(); // middle-click, initiate connection mode
} else if (linePosition != null) {
if (selected.size() != 0) { // finish connection, add lines between the selected devices and the target
if (selected.get(0).getPort() == 80000 && getConnectingLines(selected.get(0)).size() > 1) return; // prevent via-points from having more than 2 lines
for (NetItem item : prevSelected) if (!item.equals(selected.get(0))) lines.add(new NetLine2(item, selected.get(0)));
updateHistory();
selected.clear();
prevSelected.clear();
linePosition = null;
repaint();
} else if (prevSelected.size() < 2) { // connection ended in empty space, create via-point
NetItem via = new NetItem(6,"",80000,"",new Point(e.getPoint().x - 24, e.getPoint().y - 24));
items.add(via);
selected.add(via);
if (preferences.drawGrid) alignToGrid(via);
for (NetItem item : prevSelected) {
if (!item.equals(selected.get(0))) {
lines.add(new NetLine2(item, selected.get(0)));
if (via.getColor() != Color.green) via.setColor(item.getColor());
}
}
connect();
} else { // exit connection mode
linePosition = null;
prevSelected.clear();
}
}
}
/**
* Starts drawing a new line between the currently selectedItem item and another
* one. This line is attached to the cursor until the mouse is clicked
* again. If the second click does not select a device, the connection will
* be aborted.
*/
@SuppressWarnings("unchecked")
public void connect() {
if (selected.size() != 0) {
prevSelected = (Vector<NetItem>) selected.clone();
linePosition = selected.get(0).getPosition();
selected.clear();
}
repaint();
}
/**
* Disconnect selected devices and removes all via-points on the way
*
* @param withUpdate count this as a history event
*/
public void disconnect() {
for (NetItem item : selected) {
for (NetLine line : getConnectingLines(item)) {
if (!line.getFrom().equals(item) && line.getFrom().getPort() == 80000)
removeItem(line.getFrom());
else if (!line.getTo().equals(item) && line.getTo().getPort() == 80000)
removeItem(line.getTo());
lines.remove(line);
}
}
repaint();
}
// calculate distance between two points a and b with an offset of aOffset
private double distance(Point a, Point b, int aOffset) {
return (Math.sqrt(Math.pow(b.x-a.x-aOffset, 2)+Math.pow(b.y-a.y-aOffset, 2)));
}
private void dragged(MouseEvent e) {
if (button == 3) { // canvas dragged
if (rectStop == null) {// initiate
rectStop = e.getPoint();
setCursor(new Cursor(Cursor.MOVE_CURSOR));
}
for (NetItem item : items) { // add position-delta to all devices
item.getPosition().x += (e.getPoint().x - rectStop.x);
item.getPosition().y += (e.getPoint().y - rectStop.y);
}
for (BackgroundImage img : backgroundImages) img.move(e.getPoint().x - rectStop.x, e.getPoint().y - rectStop.y);
for (NetNote note : notes) note.move(e.getPoint().x - rectStop.x, e.getPoint().y - rectStop.y);
displacement.x += (e.getPoint().x - rectStop.x);
displacement.y += (e.getPoint().y - rectStop.y);
} else if (button == 2) { // backgroundImage dragged
if (rectStop == null) {// initiate
rectStop = e.getPoint();
setCursor(new Cursor(Cursor.MOVE_CURSOR));
findSelectedBackgroundImage(e);
}
if (selectedImage != null) selectedImage.move(e.getPoint().x - rectStop.x, e.getPoint().y - rectStop.y);
} else if (selected.size() != 0) { // item(s) dragged
if (rectStop == null) {// initiate
rectStop = e.getPoint();
setCursor(new Cursor(Cursor.HAND_CURSOR));
}
for (NetItem item : selected) { // add position-delta to selected devices
item.getPosition().x += (e.getPoint().x - rectStop.x);
item.getPosition().y += (e.getPoint().y - rectStop.y);
}
} else if (selectedNote != null) {// note dragged
if (rectStop == null) {// initiate
rectStop = e.getPoint();
setCursor(new Cursor(Cursor.HAND_CURSOR));
}
selectedNote.move(e.getPoint().x - rectStop.x, e.getPoint().y - rectStop.y);
} else { // drawing rectangle
if (rectStart == null) {
rectStart = e.getPoint();
setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
}
}
rectStop = e.getPoint();
repaint();
}
protected void editNote(final NetNote note) {
final JDialog d = new JDialog(parent.f);
d.setLayout(new BorderLayout());
final JTextField title = new JTextField(note.getTitle());
title.setFont(mono);
final JTextArea text = new JTextArea(note.getText());
text.setLineWrap(true);
text.setFont(mono);
JButton ok = new JButton("OK");
ok.addActionListener(new ActionListener() {@Override public void actionPerformed(ActionEvent e) {
note.setTitle(title.getText());
note.setText(text.getText());
repaint();
d.dispose();
}});
d.add(title, BorderLayout.NORTH);
d.add(text, BorderLayout.CENTER);
d.add(ok, BorderLayout.SOUTH);
d.pack();
d.setLocation(parent.f.getLocation().x + parent.f.getWidth()/2 - 100, parent.f.getLocation().y + parent.f.getHeight()/2 - 100);
d.setMinimumSize(new Dimension(120,180));
d.setResizable(false);
d.setVisible(true);
}
// find last backgroundImage for click
private void findSelectedBackgroundImage(MouseEvent e) {
selectedImage = null;
for (BackgroundImage img : backgroundImages) {
if (e.getPoint().x > img.getPosition().x && e.getPoint().y > img.getPosition().y
&& e.getPoint().x < img.getPosition().x + img.getImage().getWidth(this)
&& e.getPoint().y < img.getPosition().y + img.getImage().getHeight(this)) {
selectedImage = img;
repaint();
return;
}
}
}
// try to find out if the user has clicked on a NetItem or a NetLine and select it
private void findSelectedItem(MouseEvent e) {
for (NetItem item : items) { // scan for items
if (e.getPoint().x > item.getPosition().x && e.getPoint().x <= item.getPosition().x + 48
&& e.getPoint().y > item.getPosition().y && e.getPoint().y <= item.getPosition().y + 48) {
if (e.getButton() != MouseEvent.BUTTON3) selected.clear();
if (!selected.contains(item)) selected.add(item);
selectedLine = null;
selectedNote = null;
repaint(); // enable edit menu entries:
parent.connect.setEnabled(true);
parent.remove.setEnabled(true);
if (selected.get(0).getPort() != 80000) { // only enable settings/disconnect for non-vias
parent.disconnect.setEnabled(true);
parent.settings.setEnabled(true);
} else if (getConnectingLines(selected.get(0)).size() > 1) { // disable connect if via already has 2 lines
parent.connect.setEnabled(false);
}
return;
}
}
// no item selected, scan for lines
for (NetLine2 line : lines) {
// line was selected if the distance of AM+MB is smaller than AB+line_width (where A and B are the two points defining the line and M is the mouse)
if (distance(line.getFrom().getPosition(),e.getPoint(),24)+distance(line.getTo().getPosition(), e.getPoint(),24)
<= distance(line.getFrom().getPosition(), line.getTo().getPosition(),0)+0.5) {
selectedLine = line;
selected.clear();
selectedNote = null;
repaint(); // disable edit menu entries:
parent.connect.setEnabled(false);
parent.disconnect.setEnabled(false);
parent.remove.setEnabled(true);
parent.settings.setEnabled(false);
return;
}
}
// no item/line found, scan for notes
for (NetNote note : notes) {
if(e.getPoint().x > note.getPosition().x && e.getPoint().y > note.getPosition().y &&
e.getPoint().x < note.getPosition().x + 112 && e.getPoint().y < note.getPosition().y + 112) {
selectedNote = note;
selected.clear();
selectedLine = null;
repaint(); // disable edit menu entries:
parent.connect.setEnabled(false);
parent.disconnect.setEnabled(false);
parent.remove.setEnabled(true);
parent.settings.setEnabled(false);
return;
}
}
// neither line nor item found, reset
selected.clear();
selectedLine = null;
selectedNote = null;
repaint(); // disable edit menu entries:
parent.connect.setEnabled(false);
parent.disconnect.setEnabled(false);
parent.remove.setEnabled(false);
parent.settings.setEnabled(false);
}
// find all lines connected to an item
private Vector<NetLine2> getConnectingLines(NetItem item) {
Vector<NetLine2> out = new Vector<NetLine2>();
for (NetLine2 line : lines) {
if (line.getFrom().equals(item)) out.add(line);
else if (line.getTo().equals(item)) out.add(line);
}
return out;
}
// inserts a switch between to connected items
private void insertSwitch(NetLine2 line, int port) {
midWay.x = line.getFrom().getPosition().x + ((line.getTo().getPosition().x - line.getFrom().getPosition().x) / 2);
midWay.y = line.getFrom().getPosition().y + ((line.getTo().getPosition().y - line.getFrom().getPosition().y) / 2);
NetItem item = new NetItem(6, "", port, "", midWay.getLocation());
item.setColor(line.getFrom().getColor());
items.add(item);
lines.add(new NetLine2(line.getFrom(),item));
lines.add(new NetLine2(line.getTo(),item));
lines.get(lines.size()-1).setLabel(line.getLabel());
lines.remove(line);
selectedLine = null;
repaint();
updateHistory();
}
private void keypress(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { // ESC aborts current action
selected.clear();
prevSelected.clear();
selectedLine = null;
linePosition = null;
rectStart = null;
repaint();
}
}
// show line label edit dialog
private void lineSettings() {
String label = selectedLine.getLabel();
new LineDialog(selectedLine, this);
if (!selectedLine.getLabel().equals(label)) updateHistory();
}
// ok, java is wrong! -9%5 doesn't return 1, as one would expect, but -4! this is insane!
// let's fix this, shall we?
private int modulo(int number, int modulus) {
return (((number%modulus)+(2*modulus))%modulus);
}
@Override
public void paint(Graphics g) {
if ((img.getWidth() != getWidth() || img.getHeight() != getHeight()) && !exportPaint) {
// yes, this is somewhat stupid, but there is no better way to
// adjust the image buffer size (that I know of)
img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
g2 = img.createGraphics();
}
// color the canvas
g2.setColor(preferences.background);
g2.fillRect(0, 0, img.getWidth(), img.getHeight());
// backgroundImages (floor plans or similar)
for (BackgroundImage img : backgroundImages) {
if (img.getImage() != null) g2.drawImage(img.getImage(), img.getPosition().x, img.getPosition().y, this);
}
// grid
if (preferences.drawGrid) {
g2.setColor(gridColor);
g2.setStroke(normal);
for (int i = 0; i < img.getWidth(); i += 50) g2.drawLine(i+(displacement.x%50), 0, i+(displacement.x%50), img.getHeight());
for (int i = 0; i < img.getHeight(); i += 50) g2.drawLine(0, i+(displacement.y%50), img.getWidth(),i+(displacement.y%50));
}
// draw notes
for (NetNote note : notes) {
int x = note.getPosition().x, y = note.getPosition().y, i;
if (note.equals(selectedNote)) {
g2.setColor(blueish);
g2.fillRect(x-3, y-3, 118, 118);
}
g2.setColor(yellowish);
g2.fillRect(x, y, 112, 112);
g2.setColor(Color.black);
x += 5; y += 15;
g2.drawString(note.getTitle(), (int) (x+53-(note.getTitle().length()*3.5)), y);
g2.setStroke(normal);
g2.drawLine(x, y+2, x+102, y+2);
y += 20;
for (i = 0; i < note.getText().length()-15;) {
g2.drawString(note.getText().substring(i, i+15), x, y);
y += 12; i += 15;
}
g2.drawString(note.getText().substring(i, note.getText().length()), x, y);
}
// highlight a selected line
if (selectedLine != null) {
g2.setColor(blueish);
g2.setStroke(highlight);
g2.drawLine(selectedLine.getFrom().getPosition().x+24, selectedLine.getFrom().getPosition().y+24, selectedLine.getTo().getPosition().x+24, selectedLine.getTo().getPosition().y+24);
}
g2.setStroke(bold);
// connecting two items, draw indicator line
if (linePosition != null && prevSelected.size() != 0) {
g2.setColor(Color.lightGray);
for (NetItem item : prevSelected)
g2.drawLine(item.getPosition().x + 24, item.getPosition().y + 24, linePosition.x, linePosition.y);
}
// draw all lines (half with the status color of one NetItem, the other
// with the other status color)
for (NetLine2 line : lines) {
// calculate midway of the line, as we split the line into two lines with individual colors
// also used to align the name-string of this line
midWay.x = line.getFrom().getPosition().x
+ ((line.getTo().getPosition().x - line.getFrom().getPosition().x) / 2) + 24;
midWay.y = line.getFrom().getPosition().y
+ ((line.getTo().getPosition().y - line.getFrom().getPosition().y) / 2) + 24;
// if there is a switch at either end of the line, draw a direct line
if (line.getFrom().getImageID() == 6) {
g2.setColor(line.getTo().getColor());
g2.drawLine(line.getFrom().getPosition().x + 24, line.getFrom().getPosition().y + 24, line.getTo().getPosition().x + 24, line.getTo().getPosition().y + 24);
} else if (line.getTo().getImageID() == 6) {
g2.setColor(line.getFrom().getColor());
g2.drawLine(line.getFrom().getPosition().x + 24, line.getFrom().getPosition().y + 24, line.getTo().getPosition().x + 24, line.getTo().getPosition().y + 24);
// otherwise, draw two seperate lines
} else {
g2.setColor(line.getFrom().getColor());
g2.drawLine(line.getFrom().getPosition().x + 24, line.getFrom().getPosition().y + 24, midWay.x, midWay.y);
g2.setColor(line.getTo().getColor());
g2.drawLine(midWay.x, midWay.y, line.getTo().getPosition().x + 24, line.getTo().getPosition().y + 24);
}
// add the label (if you want to specify that this is a cat5 connection etc...)
if (line.getLabel() != "" && line.getLabel() != null) {
g2.setColor(whiteish);
g2.fillRect(midWay.x - (int)((line.getLabel().length()) * 3.5), midWay.y - 5, line.getLabel().length() * 7, 12);
g2.setColor(Color.black);
g2.setFont(mono);
g2.drawString(line.getLabel(), midWay.x - (int)((line.getLabel().length()) * 3.5), midWay.y + 5);
}
}
// draw all icons (aka devices)
g2.setFont(mono);
g2.setStroke(normal);
String name;
for (NetItem item : items) {
// draw blue background to indicate a selected device
if (selected.contains(item)) {
g2.setColor(blueish);
g2.fillOval(item.getPosition().x - 5, item.getPosition().y - 5, 58, 58);
}
// try to get the corresponding image from the imageID
if (item.getPort() != 80000) { // hack to enable "via" and keep backward compatibility
try {g2.drawImage(preferences.images.get(item.getImageID()), item.getPosition().x,item.getPosition().y, this);}
catch (Exception e) {// if the image was not fount, try the default "other.png"
try {g2.drawImage(preferences.images.get(11), item.getPosition().x,item.getPosition().y, this);}
// if this also fails, draw a gray circle
catch (Exception e2) {g2.setColor(Color.gray);g2.fillOval(item.getPosition().x,item.getPosition().y, 48, 48);}
}
} else {
g2.setColor(Color.gray);
g2.drawOval(item.getPosition().x+21,item.getPosition().y+21, 6, 6);
}
// draw IPs
if (item.getImageID() != 6) {
if (item.getPort() != 70000) {name = item.getIP() + ":" + item.getPort();}
else {name = item.getIP();}
g2.setColor(whiteish); // every letter is 7 pixels wide, so centering it is letters*3.5 + 48/2 (image width)
g2.fillRect(item.getPosition().x - (int)((name.length()) * 3.5)+24, item.getPosition().y + 50, name.length() * 7, 12);
g2.setColor(Color.black);
g2.drawString(name, item.getPosition().x - (int)((name.length()) * 3.5)+24, item.getPosition().y + 60);
}
// draw names
if (item.getName() != "") {
g2.setColor(whiteish);
g2.fillRect(item.getPosition().x - (int)((item.getName().length()) * 3.5)+24, item.getPosition().y - 15, item.getName().length() * 7, 12);
g2.setColor(Color.black);
g2.drawString(item.getName(), item.getPosition().x - (int)((item.getName().length()) * 3.5)+24, item.getPosition().y - 5);
}
}
// selection rectangle
if (rectStart != null) {
g2.setStroke(bold);
g2.setColor(blueish); // drawRect doesn't support negative values, so let's use lines...
g2.drawLine(rectStart.x, rectStart.y, rectStop.x, rectStart.y); // top
g2.drawLine(rectStart.x, rectStart.y, rectStart.x, rectStop.y); // left
g2.drawLine(rectStop.x, rectStop.y, rectStop.x, rectStart.y); // right
g2.drawLine(rectStop.x, rectStop.y, rectStart.x, rectStop.y); // bottom
}
// legend
if (preferences.drawLegend) {
g2.setStroke(legend);
g2.setColor(Color.black);
g2.drawString("Legend:", 5, img.getHeight() - 5);
g2.drawString("unknown", 95, img.getHeight() - 5);
g2.drawString("up", 185, img.getHeight() - 5);
g2.drawString("not found", 240, img.getHeight() - 5);
g2.drawString("down", 345, img.getHeight() - 5);
g2.setColor(Color.lightGray);
g2.drawLine(65, img.getHeight() - 9, 90, img.getHeight() - 9);
g2.setColor(Color.green);
g2.drawLine(155, img.getHeight() - 9, 180, img.getHeight() - 9);
g2.setColor(Color.orange);
g2.drawLine(210, img.getHeight() - 9, 235, img.getHeight() - 9);
g2.setColor(Color.red);
g2.drawLine(315, img.getHeight() - 9, 340, img.getHeight() - 9);
}
// statistics
if (preferences.drawStats) {
g2.setStroke(highlight);
g2.setColor(Color.green);
g2.drawLine(img.getWidth() - 13, img.getHeight() - 5, img.getWidth() - 13, img.getHeight() - (stats[1] * 10) - 8);
g2.setColor(Color.orange);
g2.drawLine(img.getWidth() - 31, img.getHeight() - 5, img.getWidth() - 31, img.getHeight() - (stats[2] * 10) - 8);
g2.setColor(Color.red);
g2.drawLine(img.getWidth() - 49, img.getHeight() - 5, img.getWidth() - 49, img.getHeight() - (stats[3] * 10) - 8);
g2.setColor(Color.black);
g2.drawString(stats[1]+"", img.getWidth() - 16, img.getHeight() - (stats[1] * 10) - 10);
g2.drawString(stats[2]+"", img.getWidth() - 34, img.getHeight() - (stats[2] * 10) - 10);
g2.drawString(stats[3]+"", img.getWidth() - 52, img.getHeight() - (stats[3] * 10) - 10);
}
g.drawImage(img, 0, 0, img.getWidth(), img.getHeight(), this); // write to canvas
}
// grab the next copy of all canvas items (if any) and display them
protected void redo() {
historyPosition = (historyPosition < history.size()-1)?historyPosition+1:history.size()-1;
items = history.get(historyPosition).getItems();
lines = history.get(historyPosition).getLines();
backgroundImages = history.get(historyPosition).getBackgrounds();
repaint();
if (historyPosition < history.size()-1) parent.redo.setEnabled(true);
else parent.redo.setEnabled(false);
parent.undo.setEnabled(true);
}
private void released(MouseEvent e) {
if (rectStart != null && rectStop != null) { // rectangle drawn, set devices in rect as selected
selected.clear();
// find out if the rectangle was dawn left to right and up to down (if not, correct this to make it look like it had been)
midWay = rectStart.getLocation();
if (rectStart.x > rectStop.x && rectStart.y > rectStop.y) { // lower right to upper left
rectStart = rectStop; rectStop = midWay;
} else if (rectStart.x < rectStop.x && rectStart.y > rectStop.y) { // lower left to upper right
rectStart.y = rectStop.y; rectStop.y = midWay.y;
} else if (rectStart.x > rectStop.x && rectStart.y < rectStop.y) { // upper right to lower left
rectStart.x = rectStop.x; rectStop.x = midWay.x;
} // else: upper left to lower right, do nothing
for (NetItem item : items) { // item must be inside rectangle defined by rectStart and rectStop
if (item.getPosition().x + 24 < rectStop.x && item.getPosition().x + 24 > rectStart.x
&& item.getPosition().y + 24 < rectStop.y && item.getPosition().y + 24 > rectStart.y
&& item.getPort() != 80000) {
selected.add(item);
}
}
// disable/enable menu entries based on the number of selected items
if (selected.size() > 0) {
parent.connect.setEnabled(true);
parent.disconnect.setEnabled(true);
parent.remove.setEnabled(true);
parent.settings.setEnabled(false);
}
rectStart = null;
rectStop = null;
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
repaint();
return;
} else if (preferences.drawGrid && selected.size() != 0) {
alignToGrid(null); // align device to grid if necessary
}
updateHistory();
rectStop = null;
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
/**
* Removes the currently selectedItem item from the canvas
*/
public void remove() {
if (selected.size() != 0) { // remove item(s)
for (NetItem item : selected) {
removeItem(item);
}
selected.clear();
updateHistory();
} else if (selectedLine != null) { // remove line
if (selectedLine.getFrom().getPort() == 80000) { // remove associated via-points
selected.add(selectedLine.getFrom());
remove();
}
if (selectedLine.getTo().getPort() == 80000) { // remove associated via-points
selected.add(selectedLine.getTo());
remove();
}
lines.remove(selectedLine);
selectedLine = null;
updateHistory();
} else if (selectedImage != null) {
backgroundImages.remove(selectedImage);
selectedImage = null;
updateHistory();
}
parent.connect.setEnabled(false);
parent.disconnect.setEnabled(false);
parent.remove.setEnabled(false);
parent.settings.setEnabled(false);
repaint();
}
// remove a single item and the connecting lines and eventual via-points on the way
private void removeItem(NetItem item) {
for (NetLine line : getConnectingLines(item)) {
lines.remove(line);
if (!line.getFrom().equals(item) && line.getFrom().getPort() == 80000)
removeItem(line.getFrom());
else if (!line.getTo().equals(item) && line.getTo().getPort() == 80000)
removeItem(line.getTo());
}
if (item.getImageID() != 6) {
if (item.getColor() == Color.green) stats[1] -= 1;
else if (item.getColor() == Color.orange) stats[2] -= 1;
else if (item.getColor() == Color.red) stats[3] -= 1;
stats[0] -= 1;
}
items.remove(item);
}
// reset the displacement so that the topmost item is 51px from the top and the leftmost item is 51 from the left
// useful if you have dragged the canvas too far and can't find your items anymore
protected void resetCanvas() {
Point smallest = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE);
for (NetItem item : items) {
smallest.x = (item.getPosition().x < smallest.x)?item.getPosition().x:smallest.x;
smallest.y = (item.getPosition().y < smallest.y)?item.getPosition().y:smallest.y;
}
smallest.x -= 51;
smallest.y -= 51;
for (NetItem item : items) item.setPosition(new Point(item.getPosition().x - smallest.x, item.getPosition().y - smallest.y), 0);
displacement.setLocation(0, 0);
repaint();
}
/**
* Reset the statistics
*/
public void resetStats() {
stats[1] = 0;
stats[2] = 0;
stats[3] = 0;
}
/**
* Changes the status color of all lines connected to the given <i>item</i> to <i>color</i>
*
* @param item NetItem in question
* @param code 1: up, 2: not found, 3: down
*/
public void setStatus(NetItem item, int code) {
switch (code) { // set color and update statistics
case 1:
item.setColor(Color.green);
stats[1] += 1;
break;
case 2:
item.setColor(Color.orange);
stats[2] += 1;
break;
case 3:
item.setColor(Color.red);
stats[3] += 1;
break;
default:
item.setColor(Color.lightGray);
}
repaint();
}
/**
* Show settings window
*/
public void settings() {
if (selected.size() != 0) {
Settings.dialog(this, selected.get(0), null);
repaint();
}
}
// show a different context menu, depending on where the user clicked
private void showPopup(final MouseEvent me) {
JPopupMenu pop = new JPopupMenu(); // draw a new popup menu
if (selected.size() != 0) { // popup if a device is selected
JMenuItem connect = new JMenuItem("Connect");
connect.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {connect();}});
JMenuItem disconnect = new JMenuItem("Disconnect");
disconnect.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {disconnect();}});
JMenuItem remove = new JMenuItem("Remove");
remove.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {remove();}});
JMenuItem settings = new JMenuItem("Settings");
settings.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {settings();}});
JMenuItem align = new JMenuItem("Align to Grid");
align.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {alignToGrid(null);}});
JMenuItem browse = new JMenuItem("Open in Browser");
browse.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {
if (selected.get(0).getPort() != 70000) parent.openURL("http://"+selected.get(0).getIP()+":"+selected.get(0).getPort());
else parent.openURL("http://"+selected.get(0).getIP());
}});
pop.add(connect);
pop.add(disconnect);
pop.add(remove);
pop.add(settings);
pop.add(new JSeparator());
pop.add(align);
pop.add(new JSeparator());
pop.add(browse);
if (selected.size() > 1) { // disable connect and settings if multiple items are selected
settings.setEnabled(false);
browse.setEnabled(false);
} else if (selected.get(0).getImageID() == 6) { // disable browse for switches
browse.setEnabled(false);
if (selected.get(0).getPort() == 80000) { // disable settings/disconnect for via-points
settings.setEnabled(false);
disconnect.setEnabled(false);
if (getConnectingLines(selected.get(0)).size() > 1) connect.setEnabled(false); // via-points can not have more than 2 lines
}
}
} else if (selectedLine != null) { // line is selected
JMenuItem remove = new JMenuItem("Remove");
remove.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {remove();}});
JMenuItem settings = new JMenuItem("Edit Label");
settings.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {lineSettings();}});
JMenuItem insert = new JMenuItem("Insert Switch");
insert.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {insertSwitch(selectedLine, 0);}});
JMenuItem via = new JMenuItem("Insert Via-Point");
via.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {insertSwitch(selectedLine, 80000);}});
pop.add(remove);
pop.add(settings);
pop.add(new JSeparator());
pop.add(insert);
pop.add(via);
} else if (selectedNote != null) {
JMenuItem edit = new JMenuItem("Edit");
edit.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {editNote(selectedNote);}});
JMenuItem remove = new JMenuItem("Remove");
remove.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {notes.remove(selectedNote); selectedNote = null; repaint();}});
pop.add(edit);
pop.add(remove);
} else { // nothing is selected
JMenuItem addItem = new JMenuItem("Add Device");
addItem.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {selected.clear(); addItem(me);}});
JMenuItem addNote = new JMenuItem("Add Note");
addNote.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {NetNote note = new NetNote("","",me.getPoint()); notes.add(note); editNote(note);}});
JMenuItem remove = new JMenuItem("Remove Background Image");
remove.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {remove();}});
JCheckBox grid = new JCheckBox("Grid Mode", preferences.drawGrid);
grid.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {preferences.drawGrid = !preferences.drawGrid;repaint();parent.showGrid.setSelected(preferences.drawGrid);}});
JMenuItem align = new JMenuItem("Align to Grid");
align.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {alignToGrid(null);}});
JCheckBox legend = new JCheckBox("Show Legend", preferences.drawLegend);
legend.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {preferences.drawLegend = !preferences.drawLegend;repaint();parent.showLegend.setSelected(preferences.drawLegend);}});
JCheckBox stats = new JCheckBox("Show Statistics", preferences.drawStats);
stats.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {preferences.drawStats = !preferences.drawStats;repaint();parent.showStats.setSelected(preferences.drawStats);}});
pop.add(addItem);
pop.add(addNote);
if (selectedImage != null) {
pop.add(new JSeparator());
pop.add(remove);
}
pop.add(new JSeparator());
pop.add(grid);
pop.add(align);
pop.add(new JSeparator());
pop.add(legend);
pop.add(stats);
}
pop.show(this, me.getX(), me.getY());
}
// grab a previous copy of all canvas items (if any) and display them
protected void undo() {
historyPosition = (historyPosition > 1)?historyPosition-1:0;
items = history.get(historyPosition).getItems();
lines = history.get(historyPosition).getLines();
backgroundImages = history.get(historyPosition).getBackgrounds();
repaint();
if (historyPosition > 0) parent.undo.setEnabled(true);
else parent.undo.setEnabled(false);
parent.redo.setEnabled(true);
}
@Override
public void update(Graphics g) {
// Ignore the clearRect method used in Canvas to avoid flickering
paint(g);
}
// create a copy of all canvas items and store it
protected void updateHistory() {
HistoryItem newEntry = new HistoryItem(items, lines, backgroundImages);
// you might have dragged the item but it snapped back to the old position or something, so let's check if there actually is a difference
if ((historyPosition > -1 && !newEntry.equals(history.get(historyPosition))) || historyPosition < 0) {
history.add(newEntry);
historyPosition = (historyPosition < preferences.undo-1)?historyPosition+1:(preferences.undo-1); // calc new position in history
for (int i = historyPosition+1; i < history.size();) history.remove(i); // remove old items
}
if (history.size() > 1) parent.undo.setEnabled(true); // enable/disable menu entries
parent.redo.setEnabled(false);
documentChanged = true;
}
/**
* Update switch/via-point colors.
*/
public void updateSwitches() {
int blackItems = 0, oldBlackItems = 0;
for (NetItem item : items) if (item.getImageID() == 6) {item.setColor(Color.lightGray); blackItems++;}; // reset all switch/via colors
while (blackItems > 0) { // loop until all colors != gray
oldBlackItems = blackItems;
for (NetLine line : lines) { // if one end is gray ant the other is not, replace gray by the other color
if (line.getFrom().getColor() == Color.lightGray && line.getTo().getColor() != Color.lightGray) {
line.getFrom().setColor(line.getTo().getColor());
blackItems--;
} else if (line.getFrom().getColor() != Color.lightGray && line.getTo().getColor() == Color.lightGray) {
line.getTo().setColor(line.getFrom().getColor());
blackItems--;
} else if (line.getFrom().getImageID() == 6 && line.getFrom().getColor() != Color.green) { // prefer green to any other colo
line.getFrom().setColor(line.getTo().getColor());
} else if (line.getTo().getImageID() == 6 && line.getTo().getColor() != Color.green) {
line.getTo().setColor(line.getFrom().getColor());
}
}
if (oldBlackItems == blackItems) break; // prevent deadlock: break if nothing has happened
}
}
}