package jp.ac.aiit.jointry.models.blocks.statement; import java.util.ArrayList; import java.util.List; import javafx.event.EventHandler; import javafx.scene.Node; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.Shape; import jp.ac.aiit.jointry.controllers.ResizePane; import jp.ac.aiit.jointry.models.blocks.Block; import jp.ac.aiit.jointry.models.blocks.Connector; import jp.ac.aiit.jointry.models.blocks.statement.codeblock.CodeBlock; import jp.ac.aiit.jointry.services.broker.app.BlockDialog; import static jp.ac.aiit.jointry.services.broker.app.JointryCommon.M_BLOCK_MOVE; import static jp.ac.aiit.jointry.services.broker.app.JointryCommon.M_BLOCK_ADDLINK; import static jp.ac.aiit.jointry.services.broker.app.JointryCommon.M_BLOCK_ADDCHILD; public abstract class Statement extends Block { public Statement myBlock; public Statement prevBlock; public Statement nextBlock; public CodeBlock parentBlock; public Connector topCon; public Connector bottomCon; public Connector leftCon; public Connector rightCon; public Statement() { super(); this.myBlock = this; makeConnectors(); addEventFilter(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent t) { if (getConnector() == null) { return; } // 上下の接続 if (con.getPosition() == Connector.Position.BOTTOM) { Statement target = (Statement) con.getHolder(); if (target != nextBlock) { target.addLink((Statement) myBlock); move(target.getLayoutX(), target.getLayoutY() + target.getHeight()); con.touch(); BlockDialog.sendMessage(M_BLOCK_ADDLINK, myBlock); } } // 包含の接続 if (con.getPosition() == Connector.Position.RIGHT) { if (con.getHolder() instanceof CodeBlock) { CodeBlock target = (CodeBlock) con.getHolder(); target.addChild(myBlock); // 子供要素 Statement next = myBlock.nextBlock; while (next != null) { target.addChild(next); next = next.nextBlock; } move(target.getLayoutX() + target.wLeft, target.getLayoutY() + target.hUpper); con.touch(); target.resize(); BlockDialog.sendMessage(M_BLOCK_ADDCHILD, myBlock); } return; } } }); // Use Filter (not Handler) to fire first. addEventFilter(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent mouseEvent) { initializeLink(); // Move double dx = mouseEvent.getSceneX() + anchorX; double dy = mouseEvent.getSceneY() + anchorY; move(dx, dy); BlockDialog.sendMessage(M_BLOCK_MOVE, myBlock); if (getCollision() == null) { return; } // 上下の接続(確認) if (con.getPosition() == Connector.Position.BOTTOM) { Statement target = (Statement) con.getHolder(); if (target != nextBlock) { move(target.getLayoutX(), target.getLayoutY() + target.getHeight()); con.touch(); } } // 包含の接続 if (con.getPosition() == Connector.Position.RIGHT) { if (con.getHolder() instanceof CodeBlock) { CodeBlock target = (CodeBlock) con.getHolder(); move(target.getLayoutX() + target.wLeft, target.getLayoutY() + target.hUpper); con.touch(); } return; } } }); } /** * ドラッグするブロックを先頭にする. */ @Override public void initializeLink() { // 前のブロックを外す if (prevBlock != null) { prevBlock.nextBlock = null; } prevBlock = null; // 親のブロックを外す if (parentBlock != null) { List<Statement> blocks = this.fetchAllNextBlocks(); blocks.add(this); parentBlock.childBlocks.removeAll(blocks); parentBlock.resize(); } parentBlock = null; } public Connector getCollision() { Connector connector = null; BorderPane root = (BorderPane) getScene().getRoot(); TabPane tabs = (TabPane) root.getCenter(); for (Tab tab : tabs.getTabs()) { if (tab == null) { continue; } if (!"scriptContent".equals(tab.getContent().getId())) { continue; } // Inside scriptPane ResizePane pane = (ResizePane) tab.getContent(); Pane scriptPane = (Pane) pane.getContent(); for (Node node : scriptPane.getChildren()) { if (node == myBlock) { continue; } if (!(node instanceof Statement)) { continue; } // Inside Block Statement target = (Statement) node; for (Node n : target.getChildren()) { if (n instanceof Connector) { Connector c = (Connector) n; c.detouch(); Shape intersect = null; // 上下の接触 intersect = Shape.intersect(c, myBlock.topCon); if (intersect.getBoundsInLocal().getWidth() != -1) { connector = c; break; } // 包含の接触 intersect = Shape.intersect(c, myBlock.leftCon); if (intersect.getBoundsInLocal().getWidth() != -1) { connector = c; break; } } } } } if (connector != null) { setConnector(connector); } return connector; } @Override public void move(double dx, double dy) { super.move(dx, dy); if (nextBlock != null) { nextBlock.move(dx, dy + getHeight()); } } @Override public double fetchAllHeight() { double height = super.fetchAllHeight(); if (nextBlock != null) { height += nextBlock.fetchAllHeight(); } return height; } /** * 縦方向にブロックを接続する. * * @param next 次のブロック */ public void addLink(Statement next) { Statement tmp = this.nextBlock; this.nextBlock = next; next.prevBlock = this; if (tmp != null) { next.nextBlock = tmp; tmp.prevBlock = next; } // 包含関係も考慮する Statement prevTop = fetchPrevTopBlock(); if (prevTop.parentBlock != null) { prevTop.parentBlock.addChild(next); for (Statement p : next.fetchAllNextBlocks()) { prevTop.parentBlock.addChild(p); } prevTop.parentBlock.resize(); } } /** * 縦方向の最上部にあるブロックを取得する. * * @return */ public Statement fetchPrevTopBlock() { if (prevBlock == null) { return this; } else { return prevBlock.fetchPrevTopBlock(); } } public List<Statement> fetchAllNextBlocks() { List<Statement> list = new ArrayList<>(); Statement nb = this.nextBlock; while (nb != null) { list.add(nb); nb = nb.nextBlock; } return list; } public boolean isTopLevelBlock() { return (prevBlock == null && parentBlock == null); } @Override public void show() { getSprite().getScriptPane().getChildren().add(this); if (nextBlock != null) { nextBlock.show(); } } protected void makeConnectors() { this.topCon = new Connector(); this.bottomCon = new Connector(); this.leftCon = new Connector(); this.rightCon = new Connector(); double connector_width = BASIC_WIDTH; //- 125; double connector_margin = (BASIC_WIDTH - connector_width) / 2.0; // TODO: サイズはPaneと連動させないといけない。Observerかなあ? topCon.setFill(Color.TRANSPARENT); topCon.setWidth(connector_width); topCon.setHeight(10); topCon.setHolder(this); topCon.setPosition(Connector.Position.TOP); AnchorPane.setTopAnchor(topCon, 0.0); AnchorPane.setLeftAnchor(topCon, connector_margin); bottomCon.setFill(Color.TRANSPARENT); bottomCon.setWidth(connector_width); bottomCon.setHeight(10); bottomCon.setHolder(this); bottomCon.setPosition(Connector.Position.BOTTOM); AnchorPane.setBottomAnchor(bottomCon, 0.0); AnchorPane.setLeftAnchor(bottomCon, connector_margin); leftCon.setFill(Color.TRANSPARENT); leftCon.setWidth(10); leftCon.setHeight(BASIC_HEIGHT); leftCon.setHolder(this); leftCon.setPosition(Connector.Position.LEFT); AnchorPane.setLeftAnchor(leftCon, 0.0); rightCon.setFill(Color.TRANSPARENT); rightCon.setWidth(10); rightCon.setHeight(BASIC_HEIGHT); rightCon.setHolder(this); rightCon.setPosition(Connector.Position.RIGHT); AnchorPane.setRightAnchor(rightCon, 0.0); getChildren().addAll(topCon, bottomCon, leftCon, rightCon); } @Override public void remove() { super.remove(); initializeLink(); } }