diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyCellEditor.java index 3a8603e..205b5a7 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyCellEditor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyCellEditor.java @@ -2,6 +2,7 @@ import application.editor.FlowCellEditor; import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; import com.mxgraph.swing.mxGraphComponent; import javax.swing.*; @@ -24,24 +25,232 @@ mxCell cell = (mxCell) cellObj; - switch (ddmStage.getCurState()){ + if (!(eventObj instanceof MouseEvent)) return; - case IDLE: //未選択状態 - if (cell.isEdge()) { - ddmStage.setState(DependencyModelingStageStatus.SELECT_DEPENDENCY_GRAPH); - } else { - ddmStage.setState(DependencyModelingStageStatus.SELECT_INTERFACE_NODE); - editInterfaceName(cell, eventObj); - ddmStage.setState(DependencyModelingStageStatus.IDLE); - } - break; - case SELECT_DEPENDENCY_GRAPH: //依存関係グラフを選択中 - break; - case SELECT_INTERFACE_NODE: //interface選択中 - break; + MouseEvent mouseEvent = (MouseEvent) eventObj; + + //Right-Click + if (SwingUtilities.isRightMouseButton(mouseEvent)) { + if (cell.isEdge()) { + showEdgeContextMenu(cell, mouseEvent); + } else if (isInterfaceNode(cell)) { + showInterfaceContextMenu(cell, mouseEvent); + } + return; } + // Left-Click + if (ddmStage.getCurState() == DependencyModelingStageStatus.IDLE && + isInterfaceNode(cell) && + mouseEvent.getClickCount() == 2) { + editInterfaceName(cell, eventObj); + } + } + + /** + * セルがインターフェースノード(ひし形)かどうかを判定 + */ + private boolean isInterfaceNode(mxCell cell) { + if (cell == null || !cell.isVertex()) return false; + String style = cell.getStyle(); + return style != null && style.contains("shape=rhombus"); + } + + /** + * エッジがインターフェースノードに接続されているかどうかを判定 + */ + private boolean isConnectedToInterface(mxCell edgeCell) { + if (edgeCell == null || !edgeCell.isEdge()) return false; + + mxCell source = (mxCell) edgeCell.getSource(); + mxCell target = (mxCell) edgeCell.getTarget(); + + return isInterfaceNode(source) || isInterfaceNode(target); + } + + /** + * エッジ右クリック時のコンテキストメニューを表示 + */ + private void showEdgeContextMenu(mxCell edgeCell, MouseEvent mouseEvent) { + // インターフェースノードに接続されているエッジの場合は何もしない + if (isConnectedToInterface(edgeCell)) { + return; + } + + JPopupMenu popup = new JPopupMenu(); + JMenuItem reverseItem = new JMenuItem("依存関係の逆転を適用"); + + reverseItem.addActionListener(e -> { + applyDependencyInversion(edgeCell); + }); + + popup.add(reverseItem); + popup.show(graphComponent, mouseEvent.getX(), mouseEvent.getY()); + } + + /** + * インターフェースノード右クリック時のコンテキストメニューを表示 + */ + private void showInterfaceContextMenu(mxCell interfaceCell, MouseEvent mouseEvent) { + JPopupMenu popup = new JPopupMenu(); + JMenuItem deleteItem = new JMenuItem("Interfaceを削除"); + + deleteItem.addActionListener(e -> { + removeInterface(interfaceCell); + }); + + popup.add(deleteItem); + popup.show(graphComponent, mouseEvent.getX(), mouseEvent.getY()); + } + + /** + * インターフェースノードを削除し、元のエッジに戻す + */ + private void removeInterface(mxCell interfaceCell) { + DependencyModelingStage ddmStage = (DependencyModelingStage) stage; + + // DEPENDENCY_LAYERを取得 + mxCell root = (mxCell) graphComponent.getGraph().getDefaultParent(); + mxCell layerCell = (mxCell) root.getChildAt(ddmStage.DEPENDENCY_LAYER); + + // インターフェースノードに接続されているエッジを取得 + int edgeCount = interfaceCell.getEdgeCount(); + if (edgeCount != 2) { + // 正確に2つのエッジがない場合は処理しない + return; + } + + mxCell edge1 = (mxCell) interfaceCell.getEdgeAt(0); + mxCell edge2 = (mxCell) interfaceCell.getEdgeAt(1); + + // source -> interface と target -> interface のエッジを識別 + mxCell sourceCell = null; + mxCell targetCell = null; + String edgeStyle = null; + Object edgeValue = null; + + // edge1とedge2から元のsourceとtargetを特定 + if (edge1.getTarget() == interfaceCell) { + // edge1: source -> interface + sourceCell = (mxCell) edge1.getSource(); + edgeStyle = edge1.getStyle(); + edgeValue = edge1.getValue(); + } else if (edge1.getSource() == interfaceCell) { + // edge1: interface -> ? + targetCell = (mxCell) edge1.getTarget(); + } + + if (edge2.getTarget() == interfaceCell) { + if (sourceCell == null) { + // edge2: source -> interface + sourceCell = (mxCell) edge2.getSource(); + edgeStyle = edge2.getStyle(); + edgeValue = edge2.getValue(); + } else { + // edge2: target -> interface (逆向き) + targetCell = (mxCell) edge2.getSource(); + } + } else if (edge2.getSource() == interfaceCell) { + // edge2: interface -> ? + if (targetCell == null) { + targetCell = (mxCell) edge2.getTarget(); + } + } + + if (sourceCell == null || targetCell == null) { + return; + } + + graphComponent.getGraph().getModel().beginUpdate(); + try { + // インターフェースノードとそのエッジを削除 + graphComponent.getGraph().getModel().remove(edge1); + graphComponent.getGraph().getModel().remove(edge2); + graphComponent.getGraph().getModel().remove(interfaceCell); + + // 元のエッジを復元 (source -> target) + // dashedスタイルを削除 + String restoredStyle = edgeStyle; + if (restoredStyle != null && restoredStyle.contains("dashed=true")) { + restoredStyle = restoredStyle.replace(";dashed=true", "").replace("dashed=true;", "").replace("dashed=true", ""); + } + + graphComponent.getGraph().insertEdge( + layerCell, + null, + edgeValue, + sourceCell, + targetCell, + restoredStyle + ); + + } finally { + graphComponent.getGraph().getModel().endUpdate(); + graphComponent.refresh(); + } + } + + /** + * Apply dependency inversion + */ + private void applyDependencyInversion(mxCell edgeCell) { + DependencyModelingStage ddmStage = (DependencyModelingStage) stage; + + mxCell source = (mxCell) edgeCell.getSource(); + mxCell target = (mxCell) edgeCell.getTarget(); + + if (source == null || target == null) return; + + mxCell root = (mxCell) graphComponent.getGraph().getDefaultParent(); + mxCell layerCell = (mxCell) root.getChildAt(ddmStage.DEPENDENCY_LAYER); + + // エッジの中点を計算 + mxGeometry sourceGeo = source.getGeometry(); + mxGeometry targetGeo = target.getGeometry(); + + double midX = (sourceGeo.getCenterX() + targetGeo.getCenterX()) / 2; + double midY = (sourceGeo.getCenterY() + targetGeo.getCenterY()) / 2; + + graphComponent.getGraph().getModel().beginUpdate(); + try { + // 元のエッジを削除 + graphComponent.getGraph().getModel().remove(edgeCell); + + // インターフェースノード(ひし形)をDEPENDENCY_LAYERに挿入 + mxCell interfaceNode = (mxCell) graphComponent.getGraph().insertVertex( + layerCell, + null, + "Interface", + midX - ddmStage.DEPENDENCY_NODE_SIZE / 2, + midY - ddmStage.DEPENDENCY_NODE_SIZE / 2, + ddmStage.DEPENDENCY_NODE_SIZE, + ddmStage.DEPENDENCY_NODE_SIZE, + "shape=rhombus;fillColor=#BBFFBB;perimeter=rhombusPerimeter;resizable=0;" + ); + + // 元のエッジのスタイルを取得 + String edgeStyle = edgeCell.getStyle(); + Object edgeValue = edgeCell.getValue(); + + // source -> interface のエッジを作成 (元のスタイルを維持) + graphComponent.getGraph().insertEdge(layerCell, null, edgeValue, source, interfaceNode, edgeStyle); + + // target -> interface のエッジを作成 (逆向き・点線) + // 元のスタイルに dashed=true を追加 + String reversedStyle = edgeStyle; + if (reversedStyle != null && !reversedStyle.contains("dashed")) { + reversedStyle = reversedStyle + ";dashed=true"; + } else if (reversedStyle == null) { + reversedStyle = "dashed=true"; + } + + graphComponent.getGraph().insertEdge(layerCell, null, edgeValue, target, interfaceNode, reversedStyle); + + } finally { + graphComponent.getGraph().getModel().endUpdate(); + graphComponent.refresh(); + } } public void editInterfaceName(mxCell cell, EventObject eventObj){ @@ -66,6 +275,7 @@ graphComponent.refresh(); } } + @Override public void stopEditing(boolean cancel) {