public static boolean draw()

in designer/src/com/android/tools/idea/uibuilder/handlers/constraint/draw/DrawConnection.java [292:810]


  public static boolean draw(Graphics2D g,
                             ColorSet color,
                             ScenePicker.Writer picker,
                             SecondarySelector secondarySelector,
                             int connectionType,
                             @SwingCoordinate Rectangle source,
                             int sourceDirection,
                             @SwingCoordinate Rectangle dest,
                             int destDirection,
                             int myDestType,
                             int margin,
                             @SwingCoordinate int marginDistance,
                             boolean isMarginReference,
                             int modeFrom,
                             int modeTo,
                             long stateChange) {
    boolean animate = false;
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    Color constraintColor = modeGetConstraintsColor(modeTo, color);
    Color marginColor = modeGetMarginColor(modeTo, color);
    long timeSince = System.nanoTime() - stateChange;
    boolean hover = (HOVER_FLAG & modeTo) > 0 || modeTo == MODE_CONSTRAINT_SELECTED;
    modeTo &= HOVER_MASK;
    if (timeSince < TRANSITION_TIME) {
      float t = (float)((timeSince) / (double)TRANSITION_TIME);
      Color fromColor = modeGetConstraintsColor(modeFrom, color);
      Color toColor = modeGetConstraintsColor(modeTo, color);

      constraintColor = interpolate(fromColor, toColor, t);
      animate = true;
    }

    if (connectionType == TYPE_BASELINE) {
      Color hoverColor = modeGetConstraintsColor(MODE_WILL_HOVER, color);
      drawBaseLine(g, source, dest, constraintColor, hoverColor, picker, secondarySelector, hover);
      return animate;
    }
    int startx = getConnectionX(sourceDirection, source);
    int starty = getConnectionY(sourceDirection, source);
    int endx = getConnectionX(destDirection, dest);
    int endy = getConnectionY(destDirection, dest);
    int dx = getDestinationDX(destDirection);
    int dy = getDestinationDY(destDirection);

    int manhattanDistance = Math.abs(startx - endx) + Math.abs(starty - endy);
    int scale_source = Math.min(90, manhattanDistance);
    int scale_dest = scale_source;
    boolean flip_arrow = false;
    if (myDestType != DEST_NORMAL) {
      switch (destDirection) {
        case DIR_BOTTOM:
        case DIR_TOP:
          endx = startx;
          break;
        case DIR_LEFT:
        case DIR_RIGHT:
          endy = starty;
          break;
      }
    }
    if (sourceDirection == destDirection) {
      switch (myDestType) {
        case DEST_PARENT:
          scale_dest *= -1;
          flip_arrow = true;
          switch (destDirection) {
            case DIR_BOTTOM:
            case DIR_TOP:
              dy *= -1;
              break;
            case DIR_LEFT:
            case DIR_RIGHT:
              dx *= -1;
              break;
          }
          break;
        case DEST_NORMAL:
          switch (destDirection) {
            case DIR_BOTTOM:
              if (endy - 1 > starty) {
                scale_dest *= -1;
                dy *= -1;
                flip_arrow = true;
              }
              break;
            case DIR_TOP:
              if (endy < starty) {
                scale_dest *= -1;
                dy *= -1;
                flip_arrow = true;
              }
              break;
            case DIR_LEFT:
              if (endx < startx) {
                scale_dest *= -1;
                dx *= -1;
                flip_arrow = true;
              }
              break;
            case DIR_RIGHT:
              if (endx - 1 > startx) {
                scale_dest *= -1;
                dx *= -1;
                flip_arrow = true;
              }
              break;
          }
          break;
      }
    }

    int[] xPoints = new int[3];
    int[] yPoints = new int[3];
    int dir = flip_arrow ? ourOppositeDirection[destDirection] : destDirection;
    ourPath.reset();
    ourPath.moveTo(startx, starty);
    Stroke defaultStroke;
    if (manhattanDistance == 0) {
      g.setColor(constraintColor);
      DrawConnectionUtils.getArrow(dir, endx, endy, xPoints, yPoints);
      g.fillPolygon(xPoints, yPoints, 3);
      g.draw(ourPath);
      ourPath.reset();
      ourPath.moveTo(startx, starty);
    }
    defaultStroke = g.getStroke();
    g.setStroke(getStroke(StrokeType.NORMAL, false, modeTo));
    switch (connectionType) {
      case TYPE_CHAIN:
        g.setColor(constraintColor);
        // Draw curved line twice mirroring so it looks like a chain
        DrawConnectionUtils
          .drawChain(g, hover, startx, starty, endx, endy, scale_source, scale_dest, sourceDirection, destDirection, picker,
                     secondarySelector, color, modeTo);
        DrawConnectionUtils
          .drawChain(g, hover, endx, endy, startx, starty, scale_source, scale_dest, destDirection, sourceDirection, picker,
                     secondarySelector, color, modeTo);
        if (modeTo == MODE_DELETING) {
          DrawConnectionUtils.getArrow(dir, endx, endy, xPoints, yPoints);
          g.fillPolygon(xPoints, yPoints, 3);
        }
        break;
      case TYPE_ADJACENT:
        g.setColor(constraintColor);

        DrawConnectionUtils.getSmallArrow(dir, startx - dx / 2, starty - dy / 2, xPoints, yPoints);
        g.fillPolygon(xPoints, yPoints, 3);
        if (destDirection == DIR_LEFT || destDirection == DIR_RIGHT) {
          startx = (startx + endx) / 2;
          endx = startx;
        }
        else {
          starty = (starty + endy) / 2;
          endy = starty;
        }
        g.drawLine(startx, starty, endx, endy);
        if (picker != null && secondarySelector != null) {
          picker.addLine(secondarySelector, 4, startx, starty, endx, endy, 4);
        }
        break;
      case TYPE_SPRING:
        boolean drawArrow = true;
        int springEndX = endx;
        int springEndY = endy;
        if (myDestType != DEST_NORMAL) {
          int rectGap = scale(4);
          int rectDim = scale(9);
          if (margin != 0) {
            String marginString = Integer.toString(margin);
            if (destDirection == DIR_LEFT || destDirection == DIR_RIGHT) {
              int gap = Math.max(marginDistance, DrawConnectionUtils.getHorizontalMarginGap(g, marginString));
              if (Math.abs(startx - endx) < gap) {
                // Doesn't have enough space to paint margin string.
                marginString = null;
              }

              int marginX = endx - ((endx > startx) ? gap : -gap);
              int arrow = ((endx > startx) ? 1 : -1) * DrawConnectionUtils.ARROW_SIDE;
              if (hover) {
                g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
                Stroke tmpStroke = g.getStroke();
                g.setStroke(myHoverStroke);
                g.drawLine(marginX, endy, endx - arrow, endy);
                g.setStroke(tmpStroke);
              }
              if (picker != null && secondarySelector != null) {
                picker.addLine(secondarySelector, 8, marginX, endy, endx - arrow, endy, 4);
              }
              g.setColor(constraintColor);
              g.drawLine(marginX, endy, endx - arrow, endy);
              DrawConnectionUtils.drawHorizontalMarginString(g, marginColor, marginString, isMarginReference, marginX, endx - arrow, endy);

              springEndX = marginX;
            }
            else {
              int gap = Math.max(marginDistance, DrawConnectionUtils.getVerticalMarginGap(g));
              if (Math.abs(starty - endy) < gap) {
                // Doesn't have enough space to paint margin string.
                marginString = null;
              }

              int marginY = endy - ((endy > starty) ? gap : -gap);
              int arrow = ((endy > starty) ? 1 : -1) * DrawConnectionUtils.ARROW_SIDE;
              if (hover) {
                g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
                Stroke tmpStroke = g.getStroke();
                g.setStroke(myHoverStroke);
                g.drawLine(endx, marginY, endx, endy - arrow);
                g.setStroke(tmpStroke);
              }
              if (picker != null && secondarySelector != null) {
                picker.addLine(secondarySelector, 8, endx, marginY, endx, endy - arrow, 4);
              }
              g.setColor(constraintColor);
              g.drawLine(endx, marginY, endx, endy - arrow);
              DrawConnectionUtils.drawVerticalMarginString(g, marginColor, marginString, isMarginReference, endx, marginY, endy - arrow);
              springEndY = marginY;
            }
          }

          if (endx == startx) {
            if (hover) {
              g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
              Stroke tmpStroke = g.getStroke();
              g.setStroke(myHoverStroke);
              g.drawLine(startx, starty, startx, springEndY);
              g.setStroke(tmpStroke);
            }

            g.setColor(constraintColor);
            DrawConnectionUtils.drawVerticalZigZagLine(ourPath, startx, starty, springEndY);
            if (picker != null && secondarySelector != null) {
              picker.addLine(secondarySelector, 8, startx, starty, startx, springEndY, 4);
            }
            g.fillRect(startx - rectGap, springEndY, rectDim, 1);
          }
          else {
            if (hover) {
              g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
              Stroke tmpStroke = g.getStroke();
              g.setStroke(myHoverStroke);
              g.drawLine(startx, endy, springEndX, endy);
              g.setStroke(tmpStroke);
            }
            g.setColor(constraintColor);
            DrawConnectionUtils.drawHorizontalZigZagLine(ourPath, startx, springEndX, endy);
            if (picker != null && secondarySelector != null) {
              picker.addLine(secondarySelector, 8, startx, endy, springEndX, endy, 4);
            }

            g.fillRect(springEndX, endy - rectGap, 1, rectDim);
          }
        }
        else {
          g.setColor(constraintColor);
          int rectGap = scale(2);
          int rectDim = scale(5);
          if (destDirection == DIR_LEFT || destDirection == DIR_RIGHT) {
            if (hover) {
              g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
              Stroke tmpStroke = g.getStroke();
              g.setStroke(myHoverStroke);
              GeneralPath hoverPath = new GeneralPath(ourPath);
              hoverPath.moveTo(startx, starty);
              hoverPath.lineTo(endx, starty);
              hoverPath.lineTo(endx, endy);
              g.draw(hoverPath);
              g.setStroke(tmpStroke);
            }
            g.setColor(constraintColor);
            DrawConnectionUtils.drawHorizontalZigZagLine(ourPath, startx, endx, starty);
            if (picker != null && secondarySelector != null) {
              picker.addLine(secondarySelector, 8, startx, starty, endx, starty, 4);
              picker.addLine(secondarySelector, 8, endx, starty, endx, endy, 4);
            }

            g.setStroke(getStroke(StrokeType.SPRING, false, modeTo));
            drawArrow = false;
            g.drawLine(endx, starty, endx, endy);
            g.fillRoundRect(endx - rectGap, endy - rectGap, rectDim, rectDim, rectGap, rectGap);
          }
          else {

            if (hover) {
              g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
              Stroke tmpStroke = g.getStroke();
              g.setStroke(myHoverStroke);
              GeneralPath hoverPath = new GeneralPath(ourPath);
              hoverPath.moveTo(startx, starty);
              hoverPath.lineTo(startx, endy);
              hoverPath.lineTo(endx, endy);
              g.draw(hoverPath);
              g.setStroke(tmpStroke);
            }

            g.setColor(constraintColor);
            DrawConnectionUtils.drawVerticalZigZagLine(ourPath, startx, starty, endy);
            if (picker != null && secondarySelector != null) {
              picker.addLine(secondarySelector, 8, startx, starty, startx, endy, 4);
              picker.addLine(secondarySelector, 8, startx, endy, endx, endy, 4);
            }
            g.setStroke(getStroke(StrokeType.SPRING, false, modeTo));
            drawArrow = false;
            g.drawLine(startx, endy, endx, endy);
            g.fillRoundRect(endx - rectGap, endy - rectGap, rectDim, rectDim, rectGap, rectGap);
          }
        }
        g.setStroke(getStroke(StrokeType.NORMAL, false, modeTo));
        g.setColor(constraintColor);
        g.draw(ourPath);
        if (drawArrow) {
          g.setColor(constraintColor);
          DrawConnectionUtils.getArrow(dir, endx, endy, xPoints, yPoints);
          g.fillPolygon(xPoints, yPoints, 3);
        }
        break;
      case TYPE_CENTER:
      case TYPE_CENTER_WIDGET:
        int dir0_x = 0, dir0_y = 0; // direction of the start
        int dir1_x = 0, dir1_y = 0; // direction the arch must go
        int dir2_x = 0, dir2_y = 0;
        int p6x, p6y; // position of the 6'th point on the curve
        if (destDirection == DIR_LEFT || destDirection == DIR_RIGHT) {

          dir0_x = (sourceDirection == DIR_LEFT) ? -1 : 1;
          dir1_y = (endy > starty) ? 1 : -1;
          dir2_x = (destDirection == DIR_LEFT) ? -1 : 1;
          p6x = (destDirection == DIR_LEFT)
                ? endx - GAP * 2
                : endx + GAP * 2;
          p6y = starty + dir0_y * GAP + (source.height / 2 + GAP) * dir1_y;
          int vline_y1 = -1, vline_y2 = -1;
          if (source.y > dest.y + dest.height) {
            vline_y1 = dest.y + dest.height;
            vline_y2 = source.y;
          }
          if (source.y + source.height < dest.y) {
            vline_y1 = source.y + source.height;
            vline_y2 = dest.y;
          }
          if (vline_y1 != -1) {
            if (hover) {
              g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
              Stroke tmpStroke = g.getStroke();
              g.setStroke(myHoverStroke);
              int xpos = source.x + source.width / 2;
              g.drawLine(xpos, vline_y1, xpos, vline_y2);
              g.setStroke(tmpStroke);
            }
            g.setStroke(getStroke(StrokeType.DASH, false, modeTo));
            int xpos = source.x + source.width / 2;
            g.setColor(constraintColor);
            g.drawLine(xpos, vline_y1, xpos, vline_y2);
          }
        }
        else {
          dir1_x = (endx > startx) ? 1 : -1;
          dir0_y = (sourceDirection == DIR_TOP) ? -1 : 1;
          dir2_y = (destDirection == DIR_TOP) ? -1 : 1;
          p6y = (destDirection == DIR_TOP)
                ? endy - GAP * 2
                : endy + GAP * 2;
          p6x = startx + dir0_x * GAP + (source.width / 2 + GAP) * dir1_x;

          int vline_x1 = -1, vline_x2 = -1;
          if (source.x > dest.x + dest.width) {
            vline_x1 = dest.x + dest.width;
            vline_x2 = source.x;
          }
          if (source.x + source.width < dest.x) {
            vline_x1 = source.x + source.width;
            vline_x2 = dest.x;
          }
          if (vline_x1 != -1) {
            if (hover) {
              g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
              Stroke tmpStroke = g.getStroke();
              g.setStroke(myHoverStroke);
              int ypos = source.y + source.height / 2;
              g.drawLine(vline_x1, ypos, vline_x2, ypos);
              g.setStroke(tmpStroke);
            }
            g.setStroke(getStroke(StrokeType.DASH, false, modeTo));
            int ypos = source.y + source.height / 2;
            g.setColor(constraintColor);
            g.drawLine(vline_x1, ypos, vline_x2, ypos);
          }
        }
        int len = 6;
        int[] px = new int[len];
        int[] py = new int[len];
        px[0] = startx;
        py[0] = starty;
        px[1] = startx + dir0_x * GAP;
        py[1] = starty + dir0_y * GAP;
        px[2] = px[1] + (source.width / 2 + GAP) * dir1_x;
        py[2] = py[1] + (source.height / 2 + GAP) * dir1_y;
        px[3] = p6x;
        py[3] = p6y;
        px[4] = endx + 2 * dir2_x * GAP;
        py[4] = endy + 2 * dir2_y * GAP;
        px[5] = endx;
        py[5] = endy;

        if (TYPE_CENTER_WIDGET == connectionType) {
          len = DrawConnectionUtils.removeZigZag(px, py, len, 50);
        }
        DrawConnectionUtils.drawRound(ourPath, px, py, len, GAP);
        if (hover) {
          g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
          Stroke tmpStroke = g.getStroke();
          g.setStroke(myHoverStroke);
          g.draw(ourPath);
          g.setStroke(tmpStroke);
        }
        if (picker != null && secondarySelector != null) {
          DrawConnectionUtils.drawPick(picker, secondarySelector, px, py, len, GAP);
        }
        g.setStroke(getStroke(StrokeType.NORMAL, false, modeTo));
        g.setColor(constraintColor);
        DrawConnectionUtils.getArrow(destDirection, endx, endy, xPoints, yPoints);
        g.fillPolygon(xPoints, yPoints, 3);
        g.draw(ourPath);
        break;
      case TYPE_NORMAL:
        if (margin > 0) {
          if (sourceDirection == DIR_RIGHT || sourceDirection == DIR_LEFT) {
            boolean above = starty < endy;
            int line_y = starty + (above ? -1 : 1) * source.height / 4;
            g.setColor(marginColor);
            DrawConnectionUtils.drawHorizontalMarginIndicator(g, String.valueOf(margin), isMarginReference, startx, endx, line_y);
            if (myDestType != DEST_PARENT || (line_y < dest.y || line_y > dest.y + dest.height)) {
              int constraintX = (destDirection == DIR_LEFT) ? dest.x : dest.x + dest.width;
              g.setStroke(getStroke(StrokeType.DASH, false, modeTo));
              int overlap = (above) ? -OVER_HANG : OVER_HANG;
              if (hover) {
                g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
                Stroke tmpStroke = g.getStroke();
                g.setStroke(myHoverStroke);
                g.drawLine(constraintX, line_y + overlap, constraintX, above ? dest.y : dest.y + dest.height);
                g.setStroke(tmpStroke);
              }
              g.setColor(constraintColor);
              g.drawLine(constraintX, line_y + overlap, constraintX, above ? dest.y : dest.y + dest.height);
            }
          }
          else {
            boolean left = startx < endx;
            int line_x = startx + (left ? -1 : 1) * source.width / 4;
            g.setColor(marginColor);
            DrawConnectionUtils.drawVerticalMarginIndicator(g, String.valueOf(margin), isMarginReference, line_x, starty, endy);

            if (myDestType != DEST_PARENT || (line_x < dest.x || line_x > dest.x + dest.width)) {
              int constraint_y = (destDirection == DIR_TOP) ? dest.y : dest.y + dest.height;
              g.setStroke(getStroke(StrokeType.DASH, false, modeTo));
              int overlap = (left) ? -OVER_HANG : OVER_HANG;
              if (hover) {
                g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
                Stroke tmpStroke = g.getStroke();
                g.setStroke(myHoverStroke);
                g.drawLine(line_x + overlap, constraint_y,
                           left ? dest.x : dest.x + dest.width, constraint_y);
                g.setStroke(tmpStroke);
              }
              g.setColor(constraintColor);
              g.drawLine(line_x + overlap, constraint_y,
                         left ? dest.x : dest.x + dest.width, constraint_y);
            }
          }
        }
        g.setStroke(getStroke(StrokeType.NORMAL, false, modeTo));
        if (startx - endx == 0 && (sourceDirection != destDirection || dirDeltaX[sourceDirection] == 0)) {
          // Case for straight vertical lines or adjacent widgets connecting opposite horizontal anchors
          scale_source = 0;
          scale_dest = 0;
        }
        else if (starty - endy == 0 && (sourceDirection != destDirection || dirDeltaY[sourceDirection] == 0)) {
          // Case for straight horizontal lines or adjacent widgets connecting opposite vertical anchors
          scale_source = 0;
          scale_dest = 0;
        }
        if (sourceDirection == destDirection && margin == 0) {
          // For adjacent widgets connecting the same anchor
          scale_source /= 3;
          scale_dest /= 2;
        }
        float x1 = startx, y1 = starty, x2, y2, x3, y3, x4, y4;
        if (hover) {
          g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
          Stroke tmpStroke = g.getStroke();
          g.setStroke(myHoverStroke);
          GeneralPath hoverPath = new GeneralPath(ourPath);
          hoverPath
            .curveTo(startx + scale_source * dirDeltaX[sourceDirection], starty + scale_source * dirDeltaY[sourceDirection],
                     endx + dx + scale_dest * dirDeltaX[destDirection], endy + dy + scale_dest * dirDeltaY[destDirection],
                     endx + dx, endy + dy);
          g.draw(hoverPath);
          g.setStroke(tmpStroke);
        }
        g.setColor(constraintColor);
        ourPath.curveTo(x2 = startx + scale_source * dirDeltaX[sourceDirection], y2 = starty + scale_source * dirDeltaY[sourceDirection],
                        x3 = endx + dx + scale_dest * dirDeltaX[destDirection], y3 = endy + dy + scale_dest * dirDeltaY[destDirection],
                        x4 = endx + dx, y4 = endy + dy);
        if (picker != null && secondarySelector != null) {
          picker.addCurveTo(secondarySelector, 4, (int)x1, (int)y1, (int)x2, (int)y2, (int)x3, (int)y3, (int)x4, (int)y4, 4);
        }
        g.setStroke(getStroke(StrokeType.BACKGROUND, false, modeTo));
        g.setColor(color.getBackground());
        DrawConnectionUtils.getArrow(dir, endx, endy, xPoints, yPoints);
        g.fillPolygon(xPoints, yPoints, 3);
        g.draw(ourPath);
        g.setColor(constraintColor);
        DrawConnectionUtils.getArrow(dir, endx, endy, xPoints, yPoints);
        g.fillPolygon(xPoints, yPoints, 3);
        g.draw(ourPath);
    }
    g.setStroke(defaultStroke);
    return animate;
  }