Package ch.rakudave.NetMap

Source Code of ch.rakudave.NetMap.NetCanvas

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;

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[5];
  protected int historyPosition, button, zoom;
  protected Point displacement;
  protected Preferences preferences;
  protected NetMap parent;
  protected Vector<NetItem> selected, prevSelected;
  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, 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 mouseReleased(MouseEvent e) {released(e);}
      @Override public void mousePressed(MouseEvent e) {button = e.getButton();}
      @Override public void mouseExited(MouseEvent e) {}
      @Override public void mouseEntered(MouseEvent e) {}
      @Override public void mouseClicked(MouseEvent e) {clicked(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) {zoom += e.getWheelRotation();}});
  }
 
  @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.setColor(whiteish);
      g2.fillRect(0, img.getHeight() - 17, 375, 17);
      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(whiteish);
      g2.fillRect(img.getWidth() - 60, img.getHeight() - (stats[4] * 10) - 22, img.getWidth(), img.getHeight());
      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
  }

  @Override
  public void update(Graphics g) {
    // Ignore the clearRect method used in Canvas to avoid flickering
    paint(g);
  }
 
  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));
  }

  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();
      }
    }
  }

  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();
  }
 
  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();
  }
 
  /**
   * Show settings window
   */
  public void settings() {
    if (selected.size() != 0) {
      Settings.dialog(this, selected.get(0), null);
      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[5];
    updateHistory();
    repaint();
  }
 
  /**
   * Display an empty settings dialog to register a new device
   */
  public void addItem(MouseEvent me) {
    Settings.dialog(this, null, me);
    repaint();
  }

  /**
   * 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();
  }

  /**
   * 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();
  }

  /**
   * 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();
  }

  /**
   * 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);
    }
    // save biggest of the three in [4] for the background box
    stats[4] = (stats[1] > stats[2]) ? stats[1] : stats[2];
    stats[4] = (stats[3] > stats[4]) ? stats[3] : stats[4];
    repaint();
  }
 
  /**
   * Reset the statistics
   */
  public void resetStats() {
    stats[1] = 0;
    stats[2] = 0;
    stats[3] = 0;
  }
 
  /**
   * 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
    }
  }
 
  /**
   * 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();
  }

  // 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);
  }
 
  // 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 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;
      }
    }
  }
 
  // 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)));
  }
 
 
  // 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();
  }
 
 
  // 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;
  }
 
 
  // 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);
  }
 
  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);
  }
 
  // 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;
  }
 
 
  // 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);
  }
 
 
  // 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);
  }
 
 
  // 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();
  }
 

  // 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());
  }
}
TOP

Related Classes of ch.rakudave.NetMap.NetCanvas

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.