Package pl.tecna.gwt.connectors.client.elements

Source Code of pl.tecna.gwt.connectors.client.elements.Connector

package pl.tecna.gwt.connectors.client.elements;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import pl.tecna.gwt.connectors.client.ConnectionPoint;
import pl.tecna.gwt.connectors.client.CornerPoint;
import pl.tecna.gwt.connectors.client.Diagram;
import pl.tecna.gwt.connectors.client.Point;
import pl.tecna.gwt.connectors.client.listeners.ConnectorListener;
import pl.tecna.gwt.connectors.client.listeners.event.ConnectorClickEvent;
import pl.tecna.gwt.connectors.client.listeners.event.ConnectorDoubleClickEvent;
import pl.tecna.gwt.connectors.client.listeners.event.DiagramAddEvent;
import pl.tecna.gwt.connectors.client.listeners.event.DiagramRemoveEvent;
import pl.tecna.gwt.connectors.client.util.ConnectorStyle;
import pl.tecna.gwt.connectors.client.util.SectionData;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.AbsolutePanel;

public class Connector implements Element {
  public static final int OVERLAP_MARGIN = 5;

  private final Logger LOG = Logger.getLogger("Connector");

  public ArrayList<Section> sections;
  public ArrayList<CornerPoint> cornerPoints;

  public boolean isSelected = false;

  public EndPoint startEndPoint;
  public EndPoint endEndPoint;
  public Diagram diagram;
  public SectionDecoration startPointDecoration;
  public SectionDecoration endPointDecoration;

  public List<SectionData> savedSectionsData;

  public ConnectorStyle style = ConnectorStyle.SOLID;

  private List<ConnectorListener> listeners = new ArrayList<ConnectorListener>();

  private final int sectionMargin = 20;
  private final int lastSectionTolerance = 10;

  /**
   * Connector is a rectilinear connection that connects two {@link EndPoint}. Connector is made of
   * a couple of {@link Section} and it always starts and ends on {@link EndPoint}.
   *
   * @param startLeft a left position of the point where the Connector starts
   * @param startTop a top position of the point where the Connector starts
   * @param endLeft a left position of the point where the Connector ends
   * @param endTop a top position of the point where the Connector ends
   */
  public Connector(int startLeft, int startTop, int endLeft, int endTop) {
    super();
    init(startLeft, startTop, endLeft, endTop, null, null, new ArrayList<CornerPoint>());
  }

  /**
   * Connector is a rectilinear connection that connects two {@link EndPoint}. Connector is made of
   * a couple of {@link Section} and it always starts and ends on {@link EndPoint}.
   *
   * @param startLeft a left position of the point where the Connector starts
   * @param startTop a top position of the point where the Connector starts
   * @param endLeft a left position of the point where the Connector ends
   * @param endTop a top position of the point where the Connector ends
   * @param cornerPoints a list of corner points of the Connector
   */
  public Connector(int startLeft, int startTop, int endLeft, int endTop, ArrayList<CornerPoint> cornerPoints) {

    super();
    init(startLeft, startTop, endLeft, endTop, null, null, cornerPoints);
  }

  /**
   * Connector is a rectilinear connection that connects two {@link EndPoint}. Connector is made of
   * a couple of {@link Section} and it always starts and ends on {@link EndPoint}.
   *
   * @param startLeft a left position of the point where the Connector starts
   * @param startTop a top position of the point where the Connector starts
   * @param endLeft a left position of the point where the Connector ends
   * @param endTop a top position of the point where the Connector ends
   * @param cornerPoints a list of corner points of the Connector
   * @param startDecoration a way the connector is decorated at its start
   * @param endDecoration a way the connector is decorated at its end
   */
  public Connector(int startLeft, int startTop, int endLeft, int endTop, ArrayList<CornerPoint> cornerPoints,
      SectionDecoration startDecoration, SectionDecoration endDecoration) {

    super();
    init(startLeft, startTop, endLeft, endTop, startDecoration, endDecoration, cornerPoints);
  }

  /**
   * Connector is a rectilinear connection that connects two {@link EndPoint}. Connector is made of
   * a couple of {@link Section} and it always starts and ends on {@link EndPoint}.
   *
   * @param startLeft a left position of the point where the Connector starts
   * @param startTop a top position of the point where the Connector starts
   * @param endLeft a left position of the point where the Connector ends
   * @param endTop a top position of the point where the Connector ends
   * @param startDecoration a way the connector is decorated at its start
   * @param endDecoration a way the connector is decorated at its end
   */
  public Connector(int startLeft, int startTop, int endLeft, int endTop, SectionDecoration startDecoration,
      SectionDecoration endDecoration) {

    super();
    init(startLeft, startTop, endLeft, endTop, startDecoration, endDecoration, new ArrayList<CornerPoint>());
  }

  public Connector(int startLeft, int startTop, int endLeft, int endTop, SectionDecoration startDecoration,
      SectionDecoration endDecoration, EndPoint endEndPoint, Diagram diagram, ConnectorStyle style) {

    this.style = style;
    this.startEndPoint = new EndPoint(startLeft, startTop, this);
    this.endEndPoint = endEndPoint;
    endEndPoint.setLeft(endLeft);
    endEndPoint.setTop(endTop);
    this.startEndPoint.connector = this;
    this.endEndPoint.connector = this;

    this.sections = new ArrayList<Section>();
    cornerPoints = new ArrayList<CornerPoint>();

    // Add decorations
    this.startPointDecoration = startDecoration;
    this.endPointDecoration = endDecoration;

    // Remember the Diagram
    this.diagram = diagram;

    // Add Connector to the Diagram
    diagram.connectors.add(this);

    // Calculate standard corner points positions
    if (cornerPoints.isEmpty()) {
      calculateStandardPointsPositions();
    }

    // Recreate Sections between start, end, and corner points
    this.drawSections(cornerPoints, isSelected);

    // Set start and end Sections decorated
    try {
      // TODO this is DIRTY FIX, to make it better fix section horizontal or vertical error
      sections.get(0).setStartPointDecoration(this.startPointDecoration);
      sections.get(sections.size() - 1).setEndPointDecoration(this.endPointDecoration);
    } catch (Exception e) {
      LOG.severe("Error while setting decorations" + e.getStackTrace());
    }

    startEndPoint.showOnDiagram(diagram);

    int connectorX = diagram.boundaryPanel.getWidgetLeft(startEndPoint) - diagram.boundaryPanel.getAbsoluteLeft();
    int connectorY = diagram.boundaryPanel.getWidgetTop(startEndPoint) - diagram.boundaryPanel.getAbsoluteTop();
    diagram.onDiagramAdd(new DiagramAddEvent(this, connectorX, connectorY));

  }

  private void init(int startLeft, int startTop, int endLeft, int endTop, SectionDecoration startDecoration,
      SectionDecoration endDecoration, ArrayList<CornerPoint> cornerPoints) {

    this.startEndPoint = new EndPoint(startLeft, startTop, this);
    this.endEndPoint = new EndPoint(endLeft, endTop, this);
    this.startEndPoint.connector = this;
    this.endEndPoint.connector = this;

    this.sections = new ArrayList<Section>();

    // Add decorations
    this.startPointDecoration = startDecoration;
    this.endPointDecoration = endDecoration;

    // TODO Change workaround for trouble of 3 CornerPoint in a row
    // removing neighbour corner points with same coordinates (3 in a row)
    List<CornerPoint> toRemove = new ArrayList<CornerPoint>();
    for (int i = 1; i < cornerPoints.size() - 1; i++) {
      if (cornerPoints.get(i).compareTo(cornerPoints.get(i + 1)) == 0
          && cornerPoints.get(i).compareTo(cornerPoints.get(i - 1)) == 0) {
        toRemove.add(cornerPoints.get(i + 1));
      }
    }
    for (CornerPoint removed : toRemove) {
      cornerPoints.remove(removed);
    }

    if (toRemove.size() > 0) {
      logCornerPointsData(cornerPoints);
    }

    this.cornerPoints = cornerPoints;
  }

  /**
   * Calculates positions of Connector's sections and shows Connector on a given panel. The
   * Connector is represented by horizontal or vertical lines which are {@link Section}s. The panel
   * argument's type must be AbsolutePanel.
   * <p>
   * This method also add all necessary {@link Point}s: {@link EndPoint}s at the beginning and at
   * the end of the Connector and {@link CornerPoint}s at the Connector's corners.
   * <p>
   * The way sections are generated: </br> Let "width" a width of rectangle drown on connectors
   * start point and end point </br> Let "height" a height of rectangle drown on connectors start
   * point and end point </br> If ("width" < "height") the connection contains two vertical sections
   * and one horizontal section. </br> If ("height" < "width") the connection contains two
   * horizontal sections and one vertical section.
   *
   * @param diagram a Diagram the Connector will be added to
   */
  public void showOnDiagram(Diagram diagram) {

    // Remember the Diagram
    this.diagram = diagram;

    // Add Connector to the Diagram
    diagram.connectors.add(this);

    // Calculate standard corner points positions
    if (cornerPoints.isEmpty()) {
      calculateStandardPointsPositions();
    }

    // Recreate Sections between start, end, and corner points
    this.drawSections(cornerPoints, isSelected);

    // Set start and end Sections decorated
    sections.get(0).setStartPointDecoration(this.startPointDecoration);
    sections.get(sections.size() - 1).setEndPointDecoration(this.endPointDecoration);

    // Show startEndPoint and endEndPoint
    startEndPoint.showOnDiagram(diagram);
    endEndPoint.showOnDiagram(diagram);

    int connectorX = diagram.boundaryPanel.getWidgetLeft(startEndPoint) - diagram.boundaryPanel.getAbsoluteLeft();
    int connectorY = diagram.boundaryPanel.getWidgetTop(startEndPoint) - diagram.boundaryPanel.getAbsoluteTop();
    diagram.onDiagramAdd(new DiagramAddEvent(this, connectorX, connectorY));
  }

  /**
   * Removes Connector from Diagram and from its boundaryPanel.
   *
   * @param diagram a Diagram the Connector will be removed from
   */
  public void removeFromDiagram(Diagram diagram) {
    removeFromDiagram(diagram, true);
  }

  /**
   * Removes Connector from Diagram and from its boundaryPanel
   *
   * @param diagram a Diagram the Connector will be removed from
   */
  public void removeFromDiagram(Diagram diagram, boolean fireEvent) {

    // Remove Connector from Diagram
    diagram.connectors.remove(this);

    // Remove Connector from Diagram's boundaryPanel
    for (int i = 0; i < sections.size(); i++) {
      diagram.boundaryPanel.remove(sections.get(i));

    }
    sections.removeAll(sections);

    // Remove connector's decorations
    if (startPointDecoration != null) {
      diagram.boundaryPanel.remove(startPointDecoration);
    }
    if (endPointDecoration != null) {
      diagram.boundaryPanel.remove(endPointDecoration);
    }

    if (startEndPoint.isGluedToConnectionPoint()) {
      startEndPoint.unglueFromConnectionPoint();
    }
    if (endEndPoint.isGluedToConnectionPoint()) {
      endEndPoint.unglueFromConnectionPoint();
    }

    // Remove end points
    startEndPoint.clear();
    endEndPoint.clear();

    if (fireEvent) {
      diagram.onDiagramRemove(new DiagramRemoveEvent(this, null, null));
    }
  }

  /**
   * Calculates values of local variables cornerPoint1Left, cornerPoint1Top, cornerPoint2Left,
   * cornerPoint2Top to draw a standard rectilinear tree-sections connector. Points positions
   * depends on connector's width and height. If the width is less than the height the connection
   * contains two vertical sections and one horizontal section. If the width is greater than height
   * the connection contains two horizontal sections and one vertical section.
   */
  public void calculateStandardPointsPositions() {
    // LOG.i("calculateStandardPP");
    cornerPoints.removeAll(cornerPoints);

    int width = Math.abs(startEndPoint.getLeft() - endEndPoint.getLeft());
    int height = Math.abs(startEndPoint.getTop() - endEndPoint.getTop());

    CornerPoint cp1 = new CornerPoint(0, 0);
    CornerPoint cp2 = new CornerPoint(0, 0);
    if (width < height) {
      // the connection contains two vertical sections and one horizontal section
      cp1.setLeft(startEndPoint.getLeft());
      cp1.setTop(startEndPoint.getTop() + ((endEndPoint.getTop() - startEndPoint.getTop()) / 2));
      cp2.setLeft(endEndPoint.getLeft());
      cp2.setTop(cp1.getTop());
    } else {
      // the connection contains two horizontal sections and one vertical section
      cp1.setLeft(startEndPoint.getLeft() + ((endEndPoint.getLeft() - startEndPoint.getLeft()) / 2));
      cp1.setTop(startEndPoint.getTop());
      cp2.setLeft(cp1.getLeft());
      cp2.setTop(endEndPoint.getTop());
    }
    cornerPoints.add(cp1);
    cornerPoints.add(cp2);

  }

  public void updateCornerPoints() {
    // Log.info("updateCornerPoints - Connector - 275");
    this.cornerPoints = (ArrayList<CornerPoint>) getCorners(sections);
  }

  public Section findNeighborSection(Section section, Point point) {
    Section sec = null;
    for (int i = 0; i < sections.size(); i++) {
      if ((sections.get(i) != section)
          && ((sections.get(i).startPoint == point) || (sections.get(i).endPoint == point))) {
        sec = sections.get(i);
      }
    }
    return sec;
  }

  public Section findSectionWithThisStartPoint(Point point) {
    Section sec = null;
    for (int i = 0; i < sections.size(); i++) {
      if (sections.get(i).startPoint == point) {
        sec = sections.get(i);
      }
    }
    return sec;
  }

  public Section findSectionWithThisEndPoint(Point point) {
    Section sec = null;
    for (int i = 0; i < sections.size(); i++) {
      if (sections.get(i).endPoint == point) {
        sec = sections.get(i);
      }
    }
    return sec;
  }

  public Section prevSectionForPoint(Point point) {
    Section sec = null;
    for (int i = 0; i < sections.size(); i++) {
      if (sections.get(i).endPoint == point) {
        sec = sections.get(i);
      }
    }
    return sec;
  }

  public Section nextSectionForPoint(Point point) {
    Section sec = null;
    for (int i = 0; i < sections.size(); i++) {
      if (sections.get(i).startPoint == point) {
        sec = sections.get(i);
      }
    }
    return sec;
  }

  public void update() {
    for (int i = 0; i < sections.size(); i++) {
      sections.get(i).update();
    }
  }

  public boolean isOnDiagram(AbsolutePanel panel) {
    if (this.diagram == null) {
      return false;
    }
    if (panel == this.diagram.boundaryPanel) {
      return true;
    } else {
      return false;
    }
  }

  public Section getPrevSection(Section currentSection) {

    if (this.sections.indexOf(currentSection) > 0) {
      return this.sections.get(this.sections.indexOf(currentSection) - 1);
    } else {
      return null;
    }
  }

  public Section getNextSection(Section currentSection) {

    if (this.sections.size() > this.sections.indexOf(currentSection) + 1) {
      return this.sections.get(this.sections.indexOf(currentSection) + 1);
    } else {
      return null;
    }
  }

  /**
   * Disconnects {@link Connector} end point (if attached), and creates dragable {@link EndPoint}
   */
  public void disconnectEnd() {
    if (endEndPoint.isGluedToConnectionPoint()) {
      int left = endEndPoint.getLeft();
      int top = endEndPoint.getTop();
      endEndPoint.unglueFromConnectionPoint();
      endEndPoint = new EndPoint(left, top, this);
      endEndPoint.showOnDiagram(diagram);

      try {
        Section endSection = sections.get(sections.size() - 1);
        endSection.setEndPoint(endEndPoint);
      } catch (IndexOutOfBoundsException e) {
        LOG.log(Level.SEVERE, "Error disconnecting end point : no end section", e);
      }
    }
  }

  /**
   * Disconnects {@link Connector} start point (if attached), and creates dragable {@link EndPoint}
   */
  public void disconnectStart() {
    if (startEndPoint.isGluedToConnectionPoint()) {
      int left = startEndPoint.getLeft();
      int top = startEndPoint.getTop();
      startEndPoint.unglueFromConnectionPoint();
      startEndPoint = new EndPoint(left, top, this);
      startEndPoint.showOnDiagram(diagram);

      try {
        Section startSection = sections.get(0);
        startSection.setStartPoint(startEndPoint);
      } catch (IndexOutOfBoundsException e) {
        LOG.log(Level.SEVERE, "Error disconnecting start point : no start section", e);
      }
    }
  }

  /**
   * Creates new {@link Section} when {@link Connector} is connected to the {@link ConnectionPoint}
   * </br> Section is created when direction of last {@link Section} is wrong (horizontal when
   * {@link ConnectionPoint} is on top or bottom of {@link Shape} or vertical when
   * {@link ConnectionPoint} is on left or right of {@link Shape}
   *
   * @param endPoint
   */
  public boolean fixEndSectionDirection(EndPoint endPoint) {
    // end point must be connected
    if (!endPoint.isGluedToConnectionPoint()) {
      LOG.severe("End point is not glued to the connection point");
      return false;
    }

    // defines if endPoint is end or start of connector
    boolean last = false;
    if (this.endEndPoint.equals(endPoint)) {
      last = true;
    } else {
      last = false;
    }

    // defines if section is horizontal or vertical
    boolean sectionHorizontal = true;
    if (last) {
      sectionHorizontal = sections.get(sections.size() - 1).isHorizontal();
    } else {
      sectionHorizontal = sections.get(0).isHorizontal();
    }

    try {
      List<CornerPoint> cornerPoints = getCorners(sections);
      ConnectionPoint connectionPoint = endPoint.gluedConnectionPoint;

      // Connect element to the center of connection point
      if (last) {
        endEndPoint.setLeft(connectionPoint.getCenterLeft());
        endEndPoint.setTop(connectionPoint.getCenterTop());
        if (!sectionHorizontal) {
          cornerPoints.get(cornerPoints.size() - 1).setLeft(endEndPoint.getLeft());
        } else {
          cornerPoints.get(cornerPoints.size() - 1).setTop(endEndPoint.getTop());
        }
      } else {
        startEndPoint.setLeft(connectionPoint.getCenterLeft());
        startEndPoint.setTop(connectionPoint.getCenterTop());
        if (!sectionHorizontal) {
          cornerPoints.get(0).setLeft(startEndPoint.getLeft());
        } else {
          cornerPoints.get(0).setTop(startEndPoint.getTop());
        }
      }

      if (((connectionPoint.connectionDirection == ConnectionPoint.DIRECTION_BOTTOM || connectionPoint.connectionDirection == ConnectionPoint.DIRECTION_TOP))
          && !sectionHorizontal) {
        drawSections(cornerPoints, isSelected);
        return false;
      } else if (((connectionPoint.connectionDirection == ConnectionPoint.DIRECTION_LEFT || connectionPoint.connectionDirection == ConnectionPoint.DIRECTION_RIGHT))
          && sectionHorizontal) {
        drawSections(cornerPoints, isSelected);
        return false;
      }

      // Last or first cornerPont, depends on endPoint place (start or end of Connector)
      CornerPoint extremeCorner = null;

      // New cornerPoint which is added to make last section perpendicular to shape
      CornerPoint newCornerPoint = null;

      // Creating new CornerPoint
      if (connectionPoint.connectionDirection == ConnectionPoint.DIRECTION_LEFT
          || connectionPoint.connectionDirection == ConnectionPoint.DIRECTION_RIGHT) {
        // Horizontal
        if (last) {
          extremeCorner = cornerPoints.get(cornerPoints.size() - 1);
        } else {
          extremeCorner = cornerPoints.get(0);
        }

        if (connectionPoint.connectionDirection == ConnectionPoint.DIRECTION_RIGHT) {
          extremeCorner.setLeft(extremeCorner.getLeft() + sectionMargin);
        } else {
          extremeCorner.setLeft(extremeCorner.getLeft() - sectionMargin);
        }

        if (last) {
          newCornerPoint = new CornerPoint(extremeCorner.getLeft(), endEndPoint.getTop());
        } else {
          newCornerPoint = new CornerPoint(extremeCorner.getLeft(), startEndPoint.getTop());
        }
      } else if (connectionPoint.connectionDirection == ConnectionPoint.DIRECTION_BOTTOM
          || connectionPoint.connectionDirection == ConnectionPoint.DIRECTION_TOP) {
        // Vertical
        if (last) {
          extremeCorner = cornerPoints.get(cornerPoints.size() - 1);
        } else {
          extremeCorner = cornerPoints.get(0);
        }
        if (connectionPoint.connectionDirection == ConnectionPoint.DIRECTION_BOTTOM) {
          extremeCorner.setTop(extremeCorner.getTop() + sectionMargin);
        } else {
          extremeCorner.setTop(extremeCorner.getTop() - sectionMargin);
        }
        if (last) {
          newCornerPoint = new CornerPoint(endEndPoint.getLeft(), extremeCorner.getTop());
        } else {
          newCornerPoint = new CornerPoint(startEndPoint.getLeft(), extremeCorner.getTop());
        }
      }

      if (newCornerPoint != null) {
        if (last) {
          cornerPoints.add(newCornerPoint);
        } else {
          cornerPoints.add(0, newCornerPoint);
        }
      }

      drawSections(cornerPoints, isSelected);
      return true;
    } catch (Exception e) {
      LOG.log(Level.SEVERE, "Unexpected exception", e);
      return false;
    }
  }

  /**
   * Retrieve list of {@link CornerPoint}'s from list of {@link Section} (without {@link Connector}
   * end points)
   *
   * @param sectionList
   * @return the list of corner points
   */
  public List<CornerPoint> getCorners(List<Section> sectionList) {
    List<CornerPoint> retList = new ArrayList<CornerPoint>();
    for (int i = 0; i < sectionList.size() - 1; i++) {
      retList.add(new CornerPoint(sectionList.get(i).endPoint.getLeft(), sectionList.get(i).endPoint.getTop()));
    }

    return retList;
  }

  /**
   * Retrieve list of {@link CornerPoint}'s from list of {@link Section} of this {@link Connector}
   * (without {@link Connector}'s end points)
   *
   * @return the list of corner points
   */
  public List<CornerPoint> getCorners() {
    return getCorners(sections);
  }

  public void drawSections(List<CornerPoint> cp, EndPoint startEndPoint, EndPoint endEndPoint) {
    this.startEndPoint = startEndPoint;
    this.endEndPoint = endEndPoint;

    drawSections(cp, isSelected);
  }

  public void drawSections() {
    drawSections(cornerPoints, isSelected);
  }

  /**
   * Removes old connector's sections from diagram, and shows new ones on diagram depending on old
   * sections data and given list of {@link CornerPoint}
   *
   * @param cp list of {@link CornerPoint} containing {@link Connector} shape
   */
  public void drawSections(List<CornerPoint> cp) {
    drawSections(cp, isSelected);
  }

  /**
   * Removes old connector's sections from diagram, and shows new ones on diagram depending on old
   * sections data and given list of {@link CornerPoint}
   *
   * @param cp list of {@link CornerPoint} containing {@link Connector} shape
   */
  public void drawSections(List<CornerPoint> cp, boolean isSelected) {
    this.cornerPoints = (ArrayList<CornerPoint>) cp;
    // logCornerPointsData();

    try {
      for (Section section : sections) {
        // LOG.i(section.toDebugString());
        section.removeFromDiagram();
      }

      sections.clear();

      if (cornerPoints.size() == 0) {
        Section section = new Section(startEndPoint, endEndPoint, this);
        sections.add(section);
      } else {
        Section startSection = new Section(startEndPoint, cp.get(0), this);
        if (this.startPointDecoration != null) {
          startSection.startPointDecoration = startPointDecoration;
        }
        sections.add(startSection);

        for (int i = 0; i < cp.size() - 1; i++) {
          sections.add(new Section(cp.get(i), cp.get(i + 1), this));
        }

        Section endSection = new Section(cp.get(cp.size() - 1), endEndPoint, this);
        if (this.endPointDecoration != null) {
          endSection.endPointDecoration = this.endPointDecoration;
        }
        sections.add(endSection);
      }

      for (Section section : sections) {
        section.showOnDiagram(diagram, isSelected, style);
      }
      refreshCursorStyles();
    } catch (IllegalArgumentException e) {
      logCornerPointsData();
      this.calculateStandardPointsPositions();
      LOG.log(Level.SEVERE, "Section must be horizontal or vertical, calculating standard connection points", e);
    }
  }

  public void select() {
    this.isSelected = true;

    for (Section section : sections) {
      section.select();
    }
  }

  public void deselect() {
    this.isSelected = false;

    for (Section section : sections) {
      section.deselect();
    }
  }

  /**
   * Merges two {@link Section} on end of {@link Connector} </br> {@link Connector} sections length
   * must be greater equals 3, and last section length must be lesser than defined length (default -
   * 10)
   *
   * @param endSection last {@link Section} of {@link Connection}
   */
  public boolean mergeTwoLastSections(Section endSection, List<CornerPoint> cornerPoints) {
    int connDirection = this.endEndPoint.gluedConnectionPoint.connectionDirection;

    if (!this.endEndPoint.isGluedToConnectionPoint() || endSection.getLength() > lastSectionTolerance
        || this.sections.size() < 2 || cornerPoints.size() < 2) {
      return false;
    }

    CornerPoint beforeLast = cornerPoints.get(cornerPoints.size() - 2);
    CornerPoint newCorner = null;

    switch (connDirection) {
      case 1:
        if (endSection.isHorizontal()) {
          newCorner = new CornerPoint(endSection.endPoint.getLeft(), beforeLast.getTop());
        }
        break;

      case 2:
        if (endSection.isVertical()) {
          newCorner = new CornerPoint(beforeLast.getLeft(), endSection.endPoint.getTop());
        }
        break;

      case 3:
        if (endSection.isHorizontal()) {
          newCorner = new CornerPoint(endSection.endPoint.getLeft(), beforeLast.getTop());
        }
        break;

      case 4:
        if (endSection.isVertical()) {
          newCorner = new CornerPoint(beforeLast.getLeft(), endSection.endPoint.getTop());
        }
        break;

    }
    if (newCorner != null) {
      cornerPoints.remove(cornerPoints.size() - 1);
      cornerPoints.remove(cornerPoints.size() - 1);
      cornerPoints.add(newCorner);
      drawSections(cornerPoints, isSelected);
      return true;
    } else {
      LOG.severe("New corner is null");
    }

    return false;
  }

  /**
   * Merges two {@link Section} on start of {@link Connector} </br> {@link Connector} sections
   * length must be greater equals 3, and first section length must be lesser than defined length
   * (default - 10)
   */
  public boolean mergeTwoFirstSections(Section startSection, List<CornerPoint> cornerPoints) {
    int connDirection = this.startEndPoint.gluedConnectionPoint.connectionDirection;

    if (!this.startEndPoint.isGluedToConnectionPoint() || startSection.getLength() > lastSectionTolerance
        || this.sections.size() < 2 || cornerPoints.size() < 2) {
      return false;
    }

    CornerPoint second = cornerPoints.get(1);

    CornerPoint newCorner = null;
    switch (connDirection) {
      case 1:
        if (startSection.isHorizontal()) {
          newCorner = new CornerPoint(startSection.startPoint.getLeft(), second.getTop());
        }
        break;

      case 2:
        if (startSection.isVertical()) {
          newCorner = new CornerPoint(second.getLeft(), startSection.startPoint.getTop());
        }
        break;

      case 3:
        if (startSection.isHorizontal()) {
          newCorner = new CornerPoint(startSection.startPoint.getLeft(), second.getTop());
        }
        break;

      case 4:
        if (startSection.isVertical()) {
          newCorner = new CornerPoint(second.getLeft(), startSection.startPoint.getTop());
        }
        break;
    }

    if (newCorner != null) {
      cornerPoints.remove(0);
      cornerPoints.remove(0);
      cornerPoints.add(0, newCorner);
      drawSections(cornerPoints, isSelected);
      return true;
    }

    return false;
  }

  /**
   * Check if some sections in connection are mergeable (some section's length is shorter than
   * defined value) and merges them.
   *
   * @return true, if some sections were merged
   */
  public boolean fixSections() {
    try {
      if (!fixOverlapSections()) {
        fixLineSections(cornerPoints);
        drawSections(cornerPoints, isSelected);
      }
    } catch (Exception e) {
      LOG.log(Level.SEVERE, "fixSections error :", e);
    }

    return true;
  }

  public void logCornerPointsData() {
    LOG.info("Start end point : top:" + startEndPoint.getTop() + " left:" + startEndPoint.getLeft());

    if (cornerPoints != null && cornerPoints.size() != 0) {
      for (CornerPoint cp : cornerPoints) {
        LOG.info("CornerPoint top:" + cp.getTop() + " left:" + cp.getLeft());
      }
    }

    LOG.info("End end point : top:" + endEndPoint.getTop() + " left:" + endEndPoint.getLeft());
  }

  public void logCornerPointsData(List<CornerPoint> corners) {
    LOG.info("Start end point : top:" + startEndPoint.getTop() + " left:" + startEndPoint.getLeft());

    if (corners != null && corners.size() != 0) {
      for (CornerPoint cp : corners) {
        LOG.info("CornerPoint top:" + cp.getTop() + " left:" + cp.getLeft());
      }
    }

    LOG.info("End end point : top:" + endEndPoint.getTop() + " left:" + endEndPoint.getLeft());
  }

  public void logSectionData() {
    LOG.info("Start end point : top:" + startEndPoint.getTop() + " left:" + startEndPoint.getLeft());

    if (sections != null && sections.size() != 0) {
      for (int i = 0; i < sections.size() - 1; i++) {
        LOG.info("Section top:" + sections.get(i).endPoint.getTop() + " left:" + sections.get(i).endPoint.getLeft());
      }
    }

    LOG.info("End end point : top:" + endEndPoint.getTop() + " left:" + endEndPoint.getLeft());
  }

  /**
   * Removes short sections, and merges sections that are in one line
   *
   * @param corners
   */
  public List<CornerPoint> fixLineSections(List<CornerPoint> corners) {
    if (corners.size() < 3) {
      return corners;
    }

    CornerPoint startCorner = new CornerPoint(startEndPoint.getLeft(), startEndPoint.getTop());
    CornerPoint endCorner = new CornerPoint(endEndPoint.getLeft(), endEndPoint.getTop());
    corners.add(0, startCorner);
    corners.add(endCorner);

    List<CornerPoint> toRemove = new ArrayList<CornerPoint>();
    // fix corners that are in one exact line
    for (int i = 1; i < corners.size() - 1; i++) {
      if (corners.get(i).getLeft().intValue() == corners.get(i + 1).getLeft()
          && corners.get(i).getLeft().intValue() == corners.get(i - 1).getLeft()) {
        toRemove.add(corners.get(i));
      } else if (corners.get(i).getTop().intValue() == corners.get(i + 1).getTop()
          && corners.get(i).getTop().intValue() == corners.get(i - 1).getTop()) {
        toRemove.add(corners.get(i));
      }
    }

    for (CornerPoint removed : toRemove) {
      corners.remove(removed);
    }

    boolean allCornersChecked = false;

    while (!allCornersChecked && corners.size() > 4) {
      toRemove = new ArrayList<CornerPoint>();
      boolean findShortSection = true;
      int i = 2;
      CornerPoint toChange = null;
      CornerPoint nextToChanged = null;
      while (i < corners.size() - 1 && findShortSection) {
        if ((Math.abs(corners.get(i - 1).getLeft() - corners.get(i).getLeft()) < Shape.SECTION_TOLERANCE)
            && ((Math.abs(corners.get(i - 1).getTop() - corners.get(i).getTop()) < Shape.SECTION_TOLERANCE))) {

          boolean doProceed = true;
          if (i < corners.size() - 2) {
            toChange = corners.get(i + 1);
            nextToChanged = corners.get(i - 2);
          } else if (i > 2) {
            toChange = corners.get(i - 2);
            nextToChanged = corners.get(i + 1);
          } else {
            LOG.severe("No condition was meet");
            doProceed = false;
          }

          if (doProceed) {
            toRemove.add(corners.get(i - 1));
            toRemove.add(corners.get(i));

            if (toRemove.get(0).getLeft().compareTo(toRemove.get(1).getLeft()) == 0) {
              // removed corners points were vertically matched
              toChange.setTop(nextToChanged.getTop().intValue());
            } else {
              toChange.setLeft(nextToChanged.getLeft().intValue());
            }

            findShortSection = false;
          }
        }

        i++;
      }
      for (CornerPoint removed : toRemove) {
        corners.remove(removed);
      }

      if (i >= corners.size() - 1) {
        allCornersChecked = true;
      }
    }

    corners.remove(startCorner);
    corners.remove(endCorner);

    return corners;
  }

  /**
   * Refresh mouse over styles of containing {@link Section}
   */
  public void refreshCursorStyles() {

    for (Section section : sections) {
      if (section.isVertical()) {
        DOM.setStyleAttribute(section.getElement(), "cursor", "w-resize");
      } else if (section.isHorizontal()) {
        DOM.setStyleAttribute(section.getElement(), "cursor", "n-resize");
      }
    }
  }

  /**
   * If there are {@link Section}'s, that overlap some {@link Shape}, the {@link Shape} is evaded
   */
  public boolean fixOverlapSections() {
    return fixOverlapSections(getCorners());
  }

  /**
   * If there are {@link Section}'s, that overlap some {@link Shape}, the {@link Shape} is evaded
   */
  public boolean fixOverlapSections(List<CornerPoint> corners) {
    boolean result = false;

    for (Shape shape : diagram.shapes) {
      List<Section> overlapSections = shape.overlapSections(this);
      if (overlapSections.size() != 0) {
        result = true;
        evadeShape(shape, overlapSections, corners);
      }
    }

    return result;
  }

  /**
   * Evade {@link Shape} by creating new {@link Section}s which omit given {@link Shape}
   *
   * @param shape {@link Shape} to omit
   * @param overlapSections {@link Section}s which overlap given {@link Section}s
   */
  public void evadeShape(Shape shape, List<Section> overlapSections, List<CornerPoint> corners) {
    if (shape.isEnableOverlap()) {
      return;
    }

    int shapeLeft = shape.getRelativeShapeLeft();
    int shapeTop = shape.getRelativeShapeTop();
    int shapeRight = shapeLeft + shape.getOffsetWidth();
    int shapeBottom = shapeTop + shape.getOffsetHeight();

    Section first = null;
    Section last = null;
    if (overlapSections.size() == 1) {
      first = overlapSections.get(0);
      last = overlapSections.get(0);
    } else {
      first = overlapSections.get(0);
      last = overlapSections.get(overlapSections.size() - 1);
    }

    int shapeEnterDirection = 0;
    int shapeLeaveDirection = 0;

    /*
     * define from which direction shape is leaved by overlap sections 0 - left 1 - top 2 - right 3
     * - bottom
     */
    if (first.isHorizontal()) {
      if (first.startPoint.getLeft() <= shapeLeft) {
        shapeEnterDirection = 0;
      } else if (first.startPoint.getLeft() >= shapeRight) {
        shapeEnterDirection = 2;
      }
    } else if (first.isVertical()) {
      if (first.startPoint.getTop() <= shapeTop) {
        shapeEnterDirection = 1;
      } else if (first.startPoint.getTop() >= shapeBottom) {
        shapeEnterDirection = 3;
      }
    }

    if (last.isHorizontal()) {
      if (last.endPoint.getLeft() <= shapeLeft) {
        shapeLeaveDirection = 0;
      } else if (last.endPoint.getLeft() >= shapeRight) {
        shapeLeaveDirection = 2;
      }
    } else if (last.isVertical()) {
      if (last.endPoint.getTop() <= shapeTop) {
        shapeLeaveDirection = 1;
      } else if (last.endPoint.getTop() >= shapeBottom) {
        shapeLeaveDirection = 3;
      }
    }

    /*
     * define which way from start section to the end section is shorter : clockwise or not 0 -
     * clockwise 1 - not clockwise
     */
    int direction = 0;
    boolean proceed = true;
    int clockwiseDistance = 0;
    int i = shapeEnterDirection;
    while (proceed) {
      if (i == shapeLeaveDirection) {
        proceed = false;
      } else {
        clockwiseDistance++;
        i++;
        if (i == 4) {
          i = 0;
        }
      }
    }

    int notClockwiseDistance = 0;
    proceed = true;
    i = shapeEnterDirection;
    while (proceed) {
      if (i == shapeLeaveDirection) {
        proceed = false;
      } else {
        notClockwiseDistance++;
        i--;
        if (i == -1) {
          i = 3;
        }
      }
    }

    if (clockwiseDistance < notClockwiseDistance) {
      direction = 0;
    } else if (clockwiseDistance > notClockwiseDistance) {
      direction = 1;
    } else {
      if (first.isHorizontal() && last.isHorizontal()) {
        int topDistance = first.endPoint.getTop() - shapeTop + last.endPoint.getTop() - shapeTop;
        int bottomDistance = shapeBottom - first.endPoint.getTop() + shapeBottom - last.endPoint.getTop();
        if (shapeEnterDirection == 0) {
          if (topDistance >= bottomDistance) {
            direction = 1;
          } else {
            direction = 0;
          }
        } else {
          if (topDistance >= bottomDistance) {
            direction = 0;
          } else {
            direction = 1;
          }
        }
      } else {
        int leftDistance = first.endPoint.getLeft() - shapeLeft + last.endPoint.getLeft() - shapeLeft;
        int rightDistance = shapeRight - first.endPoint.getLeft() + shapeRight - last.endPoint.getLeft();
        if (shapeEnterDirection == 1) {
          if (leftDistance >= rightDistance) {
            direction = 0;
          } else {
            direction = 1;
          }
        } else {
          if (leftDistance >= rightDistance) {
            direction = 1;
          } else {
            direction = 0;
          }
        }
      }
    }

    // Define start connection point
    CornerPoint startCorner = null;
    CornerPoint endCorner = null;

    switch (shapeEnterDirection) {
      case 0:
        startCorner = new CornerPoint(shapeLeft, first.endPoint.getTop().intValue());
        break;
      case 1:
        startCorner = new CornerPoint(first.endPoint.getLeft().intValue(), shapeTop);
        break;
      case 2:
        startCorner = new CornerPoint(shapeRight, first.endPoint.getTop().intValue());
        break;
      case 3:
        startCorner = new CornerPoint(first.endPoint.getLeft().intValue(), shapeBottom);
        break;
    }

    // Define leave connection point
    switch (shapeLeaveDirection) {
      case 0:
        endCorner = new CornerPoint(shapeLeft, last.endPoint.getTop().intValue());
        break;
      case 1:
        endCorner = new CornerPoint(last.endPoint.getLeft().intValue(), shapeTop);
        break;
      case 2:
        endCorner = new CornerPoint(shapeRight, last.endPoint.getTop().intValue());
        break;
      case 3:
        endCorner = new CornerPoint(last.endPoint.getLeft().intValue(), shapeBottom);
        break;
    }

    // Define middle connection points
    i = shapeEnterDirection;
    proceed = true;
    CornerPoint tempCorner = null;
    List<CornerPoint> middleCorners = new ArrayList<CornerPoint>();

    while (i != shapeLeaveDirection) {
      if (direction == 0) {
        i++;
        if (i == 4) {
          i = 0;
        }
      } else {
        i--;
        if (i == -1) {
          i = 3;
        }
      }

      switch (i) {
        case 0:
          if (direction == 0) {
            tempCorner = new CornerPoint(shapeLeft, shapeBottom);
          } else {
            tempCorner = new CornerPoint(shapeLeft, shapeTop);
          }
          break;

        case 1:
          if (direction == 0) {
            tempCorner = new CornerPoint(shapeLeft, shapeTop);
          } else {
            tempCorner = new CornerPoint(shapeRight, shapeTop);
          }
          break;

        case 2:
          if (direction == 0) {
            tempCorner = new CornerPoint(shapeRight, shapeTop);
          } else {
            tempCorner = new CornerPoint(shapeRight, shapeBottom);
          }
          break;

        case 3:
          if (direction == 0) {
            tempCorner = new CornerPoint(shapeRight, shapeBottom);
          } else {
            tempCorner = new CornerPoint(shapeLeft, shapeBottom);
          }
          break;
      }
      middleCorners.add(tempCorner);
    }

    // add startEndPoint and endEndPoint to corners, for easier defining
    // insert position
    CornerPoint firstSectionStart = new CornerPoint(first.startPoint.getLeft(), first.startPoint.getTop());
    CornerPoint lastSectionEnd = new CornerPoint(last.endPoint.getLeft(), last.endPoint.getTop());
    boolean addToRemove = false;

    // Index in cornerpoints list after which new corner points will be inserted
    int insertIndex = -1;

    List<CornerPoint> toRemove = new ArrayList<CornerPoint>();

    CornerPoint startEndPointCorner =
        new CornerPoint(this.startEndPoint.getLeft().intValue(), this.startEndPoint.getTop().intValue());
    CornerPoint endEndPointCorner =
        new CornerPoint(this.endEndPoint.getLeft().intValue(), this.endEndPoint.getTop().intValue());

    corners.add(0, startEndPointCorner);
    corners.add(endEndPointCorner);

    // define corner points to remove (corners lying inside evaded shape)
    // and index of first removed corner (index to add new corners)
    for (int j = 0; j < corners.size(); j++) {
      if (corners.get(j).compareTo(lastSectionEnd) == 0) {
        addToRemove = false;
      }

      if (addToRemove) {
        toRemove.add(corners.get(j));
      }

      if (corners.get(j).compareTo(firstSectionStart) == 0) {
        addToRemove = true;
        insertIndex = j;
      }
    }

    for (CornerPoint removed : toRemove) {
      corners.remove(removed);
    }

    if (insertIndex == corners.size() - 1) {
      corners.add(startCorner);

      for (CornerPoint added : middleCorners) {
        corners.add(added);
      }
      corners.add(endCorner);
    } else {
      corners.add(insertIndex + 1, endCorner);

      for (int j = middleCorners.size() - 1; j >= 0; j--) {
        corners.add(insertIndex + 1, middleCorners.get(j));
      }
      corners.add(insertIndex + 1, startCorner);
    }

    // remove startEndPoint and endEndPoint from corner points list
    corners.remove(startEndPointCorner);
    corners.remove(endEndPointCorner);
  }

  public void rememberSectionsPositions() {
    savedSectionsData = new ArrayList<SectionData>();

    for (Section section : sections) {
      savedSectionsData.add(new SectionData(section.startPoint.getLeft().intValue(), section.startPoint.getTop()
          .intValue(), section.endPoint.getLeft().intValue(), section.endPoint.getTop().intValue(), section
          .isVertical()));
    }
  }

  /**
   * Move {@link Connector} by defined x and y offset from saved {@link Section}'s positions.
   *
   * @param xOffset
   * @param yOffset
   */
  public void moveOffsetFromStartPos(int xOffset, int yOffset) {
    xOffset = xOffset - diagram.boundaryPanel.getAbsoluteLeft();
    yOffset = yOffset - diagram.boundaryPanel.getAbsoluteTop();

    for (int i = 0; i < sections.size(); i++) {

      Point sectionTopLeft = null;
      if (savedSectionsData.get(i).startPoint.getLeft() <= savedSectionsData.get(i).endPoint.getLeft()
          && savedSectionsData.get(i).startPoint.getTop() <= savedSectionsData.get(i).endPoint.getTop()) {
        sectionTopLeft = savedSectionsData.get(i).startPoint;
      } else {
        sectionTopLeft = savedSectionsData.get(i).endPoint;
      }

      diagram.boundaryPanel.setWidgetPosition(sections.get(i), sectionTopLeft.getLeft() + xOffset, sectionTopLeft
          .getTop()
          + yOffset);

      sections.get(i).startPoint.setLeft(savedSectionsData.get(i).startPoint.getLeft() + xOffset);
      sections.get(i).startPoint.setTop(savedSectionsData.get(i).startPoint.getTop() + yOffset);
      sections.get(i).endPoint.setLeft(savedSectionsData.get(i).endPoint.getLeft() + xOffset);
      sections.get(i).endPoint.setTop(savedSectionsData.get(i).endPoint.getTop() + yOffset);

      if (sections.get(i).startPointDecoration != null) {
        this.startPointDecoration.update(sections.get(i).calculateStartPointDecorationDirection(),
            sections.get(i).startPoint.getLeft(), sections.get(i).startPoint.getTop());
      }
      if (sections.get(i).endPointDecoration != null) {
        this.endPointDecoration.update(sections.get(i).calculateEndPointDecorationDirection(), sections.get(i).endPoint
            .getLeft(), sections.get(i).endPoint.getTop());
      }
    }
  }

  public boolean isOnThisConnector(Point point) {
    for (Section section : sections) {
      if (((point.getLeft() >= section.startPoint.getLeft() && point.getLeft() <= section.endPoint.getLeft()) || (point
          .getLeft() <= section.startPoint.getLeft() && point.getLeft() >= section.endPoint.getLeft()))
          && ((point.getTop() >= section.startPoint.getTop() && point.getTop() <= section.endPoint.getTop()) || (point
              .getTop() <= section.startPoint.getTop() && point.getTop() >= section.endPoint.getTop()))) {
        return true;
      }
    }

    return false;
  }

  public void addConnectorListener(ConnectorListener listener) {
    listeners.add(listener);
  }

  public void removeConnectorListener(ConnectorListener listener) {
    listeners.remove(listener);
  }

  public List<ConnectorListener> getListeners() {
    return listeners;
  }

  public void onConnectorClick(ConnectorClickEvent event) {
    for (ConnectorListener listener : listeners) {
      listener.onConnectorClick(event);
    }
  }

  public void onConnectorDoubleClick(ConnectorDoubleClickEvent event) {
    for (ConnectorListener listener : listeners) {
      listener.onConnectorDoubleClick(event);
    }
  }

}
TOP

Related Classes of pl.tecna.gwt.connectors.client.elements.Connector

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.