package jetbrains.mps.nodeEditor.cells.jetpad; /*Generated by MPS */ import jetbrains.mps.nodeEditor.EditorCell_WithComponent; import jetbrains.jetpad.mapper.MapperFactory; import org.jetbrains.mps.openapi.model.SNode; import jetbrains.jetpad.projectional.diagram.view.DiagramView; import java.awt.event.FocusListener; import jetbrains.mps.nodeEditor.cells.SelectCellOnFocusGainedFocusListener; import jetbrains.jetpad.mapper.Mapper; import jetbrains.jetpad.projectional.view.ViewContainer; import jetbrains.jetpad.projectional.view.awt.ViewContainerComponent; import jetbrains.jetpad.model.collections.list.ObservableList; import jetbrains.jetpad.model.collections.list.ObservableArrayList; import jetbrains.jetpad.model.property.Property; import jetbrains.jetpad.model.property.ValueProperty; import jetbrains.jetpad.projectional.diagram.view.PolyLineConnection; import jetbrains.jetpad.projectional.view.ViewTrait; import jetbrains.mps.lang.editor.diagram.runtime.jetpad.palette.ui.DiagramPalette; import javax.swing.JPanel; import jetbrains.jetpad.base.Registration; import jetbrains.mps.openapi.editor.EditorContext; import org.jetbrains.annotations.NotNull; import javax.swing.JComponent; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.event.FocusEvent; import jetbrains.mps.nodeEditor.cellMenu.SubstituteInfoPartExt; import jetbrains.jetpad.projectional.view.ViewTraitBuilder; import jetbrains.jetpad.projectional.view.ViewEvents; import jetbrains.jetpad.projectional.view.ViewEventHandler; import jetbrains.jetpad.event.MouseEvent; import jetbrains.jetpad.projectional.view.View; import jetbrains.jetpad.event.KeyEvent; import jetbrains.jetpad.event.Key; import jetbrains.mps.editor.runtime.selection.SelectionUtil; import java.util.List; import jetbrains.mps.openapi.editor.cells.SubstituteAction; import jetbrains.mps.editor.runtime.commands.EditorCommand; import org.jetbrains.mps.openapi.language.SAbstractConcept; import org.jetbrains.mps.openapi.language.SContainmentLink; import jetbrains.mps.baseLanguage.closures.runtime._FunctionTypes; import jetbrains.mps.nodeEditor.cellMenu.CellContext; import java.util.ArrayList; import jetbrains.mps.internal.collections.runtime.ListSequence; import jetbrains.mps.smodel.action.ModelActions; import jetbrains.mps.smodel.action.DefaultSChildSetter; import org.jetbrains.annotations.Nullable; import jetbrains.mps.smodel.action.AbstractNodeSubstituteAction; import jetbrains.mps.smodel.action.NodeFactoryManager; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations; import java.util.Collections; import jetbrains.mps.nodeEditor.cellMenu.NodeSubstitutePatternEditor; import java.awt.Window; import java.awt.Point; import java.awt.Dimension; import jetbrains.jetpad.event.ModifierKey; import jetbrains.jetpad.mapper.Synchronizers; import jetbrains.jetpad.model.property.WritableProperty; import jetbrains.mps.lang.editor.diagram.runtime.jetpad.views.DiagramDecoratorView; import jetbrains.jetpad.model.event.EventHandler; import jetbrains.jetpad.model.property.PropertyChangeEvent; import jetbrains.jetpad.projectional.diagram.view.RootTrait; import jetbrains.jetpad.geometry.Vector; import java.util.ListIterator; import java.util.Set; import jetbrains.mps.internal.collections.runtime.Sequence; import jetbrains.mps.openapi.editor.cells.EditorCell; import jetbrains.mps.smodel.action.NodeSubstituteActionWrapper; public abstract class DiagramCell extends AbstractJetpadCell implements EditorCell_WithComponent, MapperFactory<SNode, DiagramView> { private final FocusListener mySelectCellOnFocusGained = new SelectCellOnFocusGainedFocusListener(this); private Mapper<SNode, ViewContainer> myRootMapper; private Mapper<SNode, ViewContainer> myDecorationRootMapper; private ViewContainerComponent myComponent; private boolean mySubstituteEditorVisible = false; private int myPatternEditorX; private int myPatternEditorY; protected ObservableList<SNode> myBlocks = new ObservableArrayList<SNode>(); protected ObservableList<SNode> myConnectors = new ObservableArrayList<SNode>(); protected Property<Boolean> myIsShowingDragFeedBack = new ValueProperty<Boolean>(false); protected PolyLineConnection myDragConnection = new PolyLineConnection(); private ViewTrait myHandlingTrait; private DiagramPalette myPalettePanel; private JPanel myPanel; protected Registration myRegistration; protected Property<ViewTrait> myTraitProperty = new ValueProperty<ViewTrait>(null); public DiagramCell(EditorContext editorContext, SNode node) { super(editorContext, node); myTraitProperty.set(getEventHandlingTrait()); } @NotNull @Override public JComponent getComponent() { if (myPanel == null) { int columnCount = (myPalettePanel == null ? 1 : 2); myPanel = new JPanel(); myPanel.setLayout(new GridBagLayout()); GridBagConstraints gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridheight = 1; gridBagConstraints.gridwidth = columnCount; gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; gridBagConstraints.fill = GridBagConstraints.BOTH; gridBagConstraints.anchor = GridBagConstraints.NORTHEAST; if (myPalettePanel != null) { myPanel.add(myPalettePanel, gridBagConstraints); } gridBagConstraints.gridx = columnCount - 1; myPanel.add(getContainerComponent()); } return myPanel; } private ViewContainerComponent getContainerComponent() { if (myComponent == null) { myComponent = new ViewContainerComponent(); getRootMapper().attachRoot(); myComponent.container(getRootMapper().getTarget()); getDecorationRootMapper().attachRoot(); myComponent.addFocusListener(new FocusListener() { public void focusGained(FocusEvent event) { } public void focusLost(FocusEvent focusEvent) { hidePatternEditor(); } }); } return myComponent; } @Override protected void relayoutImpl() { super.relayoutImpl(); setWidth(getComponent().getWidth() + myGapLeft + myGapRight); setHeight(getComponent().getHeight()); } @Override public void onAdd() { super.onAdd(); getEditor().getCellTracker().addComponentCell(this); getComponent().addFocusListener(mySelectCellOnFocusGained); } @Override public void onRemove() { getComponent().removeFocusListener(mySelectCellOnFocusGained); getEditor().getCellTracker().removeComponentCell(this); super.onRemove(); } @Override public void setX(int x) { getComponent().setLocation(x, getComponent().getY()); super.setX(x); } @Override public void setY(int y) { getComponent().setLocation(getComponent().getX(), y); super.setY(y); } @Override public void moveTo(int x, int y) { super.moveTo(x, y); getComponent().setLocation(myX, myY); } /*package*/ void setPatternEditorX(int x) { myPatternEditorX = x; } /*package*/ void setPatternEditorY(int y) { myPatternEditorY = y; } public void setExternalTrait(ViewTrait trait) { check_xnhqai_a0a82(myRegistration); if (trait == null) { myTraitProperty.set(getEventHandlingTrait()); } else { myTraitProperty.set(trait); } } @Override public boolean isDrawBorder() { return false; } public void setPalette(DiagramPalette palette) { myPalettePanel = palette; } protected abstract SubstituteInfoPartExt[] createPaletteBlockSubstituteInfoPartExts(); protected abstract SubstituteInfoPartExt[] createPaletteConnectorSubstituteInfoPartExts(); private ViewTrait getEventHandlingTrait() { if (myHandlingTrait == null) { this.myHandlingTrait = new ViewTraitBuilder().on(ViewEvents.MOUSE_PRESSED, new ViewEventHandler<MouseEvent>() { public void handle(View view, MouseEvent event) { if (view.viewAt(event.location()) != view) { return; } if (!(view.focused().get())) { view.container().focusedView().set(view); } else { hidePatternEditor(); createNewDiagramElement(event.x(), event.y()); } event.consume(); } }).on(ViewEvents.KEY_PRESSED, new ViewEventHandler<KeyEvent>() { public void handle(View view, KeyEvent event) { if (mySubstituteEditorVisible) { getEditor().processKeyPressed(getAWTKeyEvent(event, false)); event.consume(); return; } if (event.key() == Key.ESCAPE) { SelectionUtil.selectCell(getContext(), getSNode(), getCellId()); event.consume(); } } }).on(ViewEvents.KEY_TYPED, new ViewEventHandler<KeyEvent>() { public void handle(View view, KeyEvent event) { if (!(mySubstituteEditorVisible)) { return; } getEditor().processKeyTyped(getAWTKeyEvent(event, false)); event.consume(); } }).on(ViewEvents.MOUSE_DRAGGED, new ViewEventHandler<MouseEvent>() { @Override public void handle(View view, MouseEvent event) { if (!(hasConnectionDragFeedback())) { View sourceView = view.viewAt(event.location()); if (sourceView == null || !(check_xnhqai_a0a1a0a0a0b0a0a0a0a0hb(sourceView.prop(JetpadUtils.CONNECTION_SOURCE).get()))) { return; } showConnectionDragFeedback(sourceView); } updateConnectionDragFeedback(event.location()); requestRelayout(); getEditor().relayout(); } }).on(ViewEvents.MOUSE_RELEASED, new ViewEventHandler<MouseEvent>() { @Override public void handle(View view, MouseEvent event) { if (!(hasConnectionDragFeedback())) { return; } updateConnectionDragFeedback(event.location()); createNewDiagramElement(event.location().x, event.location().y); } }).build(); } return this.myHandlingTrait; } public void createNewDiagramElement(int x, int y) { myPatternEditorX = x; myPatternEditorY = y; if (trySubstituteImmediately()) { return; } getEditor().activateNodeSubstituteChooser(this, false); } private boolean trySubstituteImmediately() { List<SubstituteAction> matchingActions = getSubstituteInfo().getMatchingActions("", false); if (matchingActions.size() == 0) { hideConnectionDragFeedback(); return true; } if (matchingActions.size() > 1) { return false; } final SubstituteAction theAction = matchingActions.get(0); final boolean[] result = new boolean[]{false}; getContext().getRepository().getModelAccess().runReadAction(new Runnable() { public void run() { result[0] = theAction.canSubstitute(""); } }); if (!(result[0])) { return false; } getContext().getRepository().getModelAccess().executeCommand(new EditorCommand(getContext()) { protected void doExecute() { theAction.substitute(getContext(), ""); } }); hideConnectionDragFeedback(); return true; } private void hidePatternEditor() { getEditor().getNodeSubstituteChooser().setVisible(false); } public SubstituteInfoPartExt createNewDiagramNodeActions(final SNode container, SAbstractConcept childNodeConcept, final SContainmentLink containingLink, final _FunctionTypes._void_P3_E0<? super SNode, ? super Integer, ? super Integer> setNodePositionCallback) { return new SubstituteInfoPartExt() { public List<SubstituteAction> createActions(CellContext cellContext, EditorContext editorContext) { List<SubstituteAction> result = new ArrayList<SubstituteAction>(); for (SubstituteAction action : ListSequence.fromList(ModelActions.createChildNodeSubstituteActions(container, null, containingLink, null, new DefaultSChildSetter(containingLink) { @Override public SNode doExecute(SNode parentNode, SNode oldChild, SNode newChild, @Nullable EditorContext editorContext) { super.doExecute(parentNode, oldChild, newChild, editorContext); setNodePositionCallback.invoke(newChild, myPatternEditorX, myPatternEditorY); return newChild; } }, editorContext))) { result.add(new DiagramCell.DiagramSubstituteActionWraper(action) { @Override public boolean canSubstitute(String string) { return !(hasConnectionDragFeedback()) && super.canSubstitute(string); } }); } return result; } }; } public SubstituteInfoPartExt createNewDiagramConnectorActions(final SNode container, final SAbstractConcept childNodeConcept, final SContainmentLink containingLink, final _FunctionTypes._return_P4_E0<? extends Boolean, ? super SNode, ? super Object, ? super SNode, ? super Object> canCreateConnector, final _FunctionTypes._void_P5_E0<? super SNode, ? super SNode, ? super Object, ? super SNode, ? super Object> setConnectorCallback) { // TMP solution: manually creating instance of connection instead of using // ModelActions.createChildNodeSubstituteActions() because of mbeddr reqirements: // hiding text-specific connection substitute actions from the diagram return new SubstituteInfoPartExt() { public List<SubstituteAction> createActions(CellContext cellContext, final EditorContext editorContext) { AbstractNodeSubstituteAction action = new AbstractNodeSubstituteAction(childNodeConcept.getDeclarationNode(), childNodeConcept, container) { @Override public boolean canSubstitute(String string) { if (!(hasConnectionDragFeedback()) || !(super.canSubstitute(string))) { return false; } DiagramCell.ConnectionInfo connectionInfo = getConnectionInfo(); return connectionInfo.isValid() && canCreateConnector.invoke(connectionInfo.getFromNode(), connectionInfo.getFromId(), connectionInfo.getToNode(), connectionInfo.getToId()); } @Override protected SNode doSubstitute(@Nullable EditorContext context, String string) { SNode result = NodeFactoryManager.createNode(childNodeConcept, null, container, SNodeOperations.getModel(container)); ListSequence.fromList(SNodeOperations.getChildren(container, containingLink)).addElement(result); DiagramCell.ConnectionInfo connectionInfo = getConnectionInfo(); setConnectorCallback.invoke(result, connectionInfo.getFromNode(), connectionInfo.getFromId(), connectionInfo.getToNode(), connectionInfo.getToId()); return result; } }; return Collections.<SubstituteAction>singletonList(new DiagramCell.DiagramSubstituteActionWraper(action)); } }; } public DiagramCell.ConnectionInfo getConnectionInfo() { return new DiagramCell.ConnectionInfo(); } @Override public NodeSubstitutePatternEditor createSubstitutePatternEditor() { return new NodeSubstitutePatternEditor() { @Override public void activate(Window window, Point point, Dimension dimension, boolean show) { Dimension actualDimension = new Dimension(100, 0); point.translate(myPatternEditorX + getContainerComponent().getX(), myPatternEditorY + getContainerComponent().getY()); super.activate(window, point, actualDimension, show); mySubstituteEditorVisible = true; } @Override public void done() { super.done(); hideConnectionDragFeedback(); mySubstituteEditorVisible = false; } }; } private java.awt.event.KeyEvent getAWTKeyEvent(KeyEvent jetPadKeyEvent, boolean isTyped) { // TODO: better integration with MPS substitute editor is required here int id = (isTyped ? java.awt.event.KeyEvent.KEY_TYPED : java.awt.event.KeyEvent.KEY_PRESSED); long when = System.currentTimeMillis(); int modifiers = 0; if (jetPadKeyEvent.has(ModifierKey.ALT)) { modifiers |= java.awt.event.KeyEvent.ALT_MASK; } if (jetPadKeyEvent.has(ModifierKey.CONTROL)) { modifiers |= java.awt.event.KeyEvent.CTRL_MASK; } if (jetPadKeyEvent.has(ModifierKey.META)) { modifiers |= java.awt.event.KeyEvent.META_MASK; } if (jetPadKeyEvent.has(ModifierKey.SHIFT)) { modifiers |= java.awt.event.KeyEvent.SHIFT_MASK; } int keyCode; switch (jetPadKeyEvent.key()) { case ESCAPE: keyCode = java.awt.event.KeyEvent.VK_ESCAPE; break; case SPACE: keyCode = java.awt.event.KeyEvent.VK_SPACE; break; case BACKSPACE: keyCode = java.awt.event.KeyEvent.VK_BACK_SPACE; break; case DELETE: keyCode = java.awt.event.KeyEvent.VK_DELETE; break; case LEFT: keyCode = java.awt.event.KeyEvent.VK_LEFT; break; case RIGHT: keyCode = java.awt.event.KeyEvent.VK_RIGHT; break; case UP: keyCode = java.awt.event.KeyEvent.VK_UP; break; case DOWN: keyCode = java.awt.event.KeyEvent.VK_DOWN; break; case ENTER: keyCode = java.awt.event.KeyEvent.VK_ENTER; break; default: keyCode = 0; } return new java.awt.event.KeyEvent(getComponent(), id, when, modifiers, keyCode, jetPadKeyEvent.keyChar()); } public Mapper<SNode, ViewContainer> getRootMapper() { if (myRootMapper == null) { myRootMapper = new Mapper<SNode, ViewContainer>(getSNode(), createViewContainer()) { @Override protected void registerSynchronizers(Mapper.SynchronizersConfiguration configuration) { super.registerSynchronizers(configuration); configuration.add(Synchronizers.forConstantRole(this, getSource(), getTarget().contentRoot().children(), DiagramCell.this)); configuration.add(Synchronizers.forProperty(myTraitProperty, new WritableProperty<ViewTrait>() { public void set(ViewTrait trait) { myRegistration = getTarget().root().addTrait(trait); } })); } }; } return myRootMapper; } public Mapper<SNode, ViewContainer> getDecorationRootMapper() { if (myDecorationRootMapper == null) { myDecorationRootMapper = new Mapper<SNode, ViewContainer>(getSNode(), getRootMapper().getTarget()) { @Override protected void registerSynchronizers(Mapper.SynchronizersConfiguration configuration) { super.registerSynchronizers(configuration); configuration.add(Synchronizers.forConstantRole(this, getSource(), getTarget().decorationRoot().children(), new MapperFactory<SNode, View>() { public Mapper<? extends SNode, ? extends View> createMapper(SNode source) { return createDecorationMapper(source); } })); } }; } return myDecorationRootMapper; } public abstract Mapper<SNode, DiagramDecoratorView> createDecorationMapper(SNode node); private ViewContainer createViewContainer() { ViewContainer result = new ViewContainer(); result.root().focusable().set(true); result.root().focused().addHandler(new EventHandler<PropertyChangeEvent<Boolean>>() { public void onEvent(PropertyChangeEvent<Boolean> focused) { if (!(focused.getNewValue())) { hidePatternEditor(); } else if (!(isSelected())) { SelectionUtil.selectCell(getContext(), getSNode(), getCellId()); } } }); result.root().addTrait(RootTrait.ROOT_TRAIT); return result; } public boolean hasConnectionDragFeedback() { return myIsShowingDragFeedBack.get(); } public PolyLineConnection showConnectionDragFeedback(View fromView) { assert !(myIsShowingDragFeedBack.get()); PolyLineConnection connection = new PolyLineConnection(); connection.fromView().set(fromView); myDragConnection = connection; myIsShowingDragFeedBack.set(true); return connection; } public void updateConnectionDragFeedback(Vector toLocation) { View targetView = getRootMapper().getTarget().contentRoot().viewAt(toLocation); while (targetView != null && targetView.prop(JetpadUtils.CONNECTABLE).get() == null) { targetView = targetView.parent().get(); } if (targetView != null && targetView.prop(JetpadUtils.CONNECTABLE).get()) { myDragConnection.toView().set(targetView); } else { myDragConnection.toView().set(null); } myDragConnection.toLocation().set(toLocation); } public void hideConnectionDragFeedback() { myIsShowingDragFeedBack.set(false); } protected void syncDiagramElements(Iterable<SNode> elements, ListIterator<SNode> blocksIterator, Set<SNode> existingBlocks, ListIterator<SNode> connectorsIterator, Set<SNode> existingConnectors) { for (SNode nextElement : Sequence.fromIterable(elements)) { EditorCell cell = getContext().getEditorComponent().getUpdater().getCurrentUpdateSession().updateChildNodeCell(nextElement); if (!(cell instanceof BlockCell) && !(cell instanceof ConnectorCell)) { continue; } syncToNextNode((cell instanceof BlockCell ? blocksIterator : connectorsIterator), (cell instanceof BlockCell ? existingBlocks : existingConnectors), nextElement, cell); } } /*package*/ static class DiagramSubstituteActionWraper extends NodeSubstituteActionWrapper { private DiagramSubstituteActionWraper(SubstituteAction action) { super(action); } } public class ConnectionInfo { public ConnectionInfo() { if (myDragConnection == null) { return; } View fromView = myDragConnection.fromView().get(); View toView = myDragConnection.toView().get(); if (fromView == null || toView == null) { return; } setFromNode(fromView.prop(JetpadUtils.SOURCE).get()); setFromId(fromView.prop(JetpadUtils.ID).get()); setToNode(toView.prop(JetpadUtils.SOURCE).get()); setToId(toView.prop(JetpadUtils.ID).get()); } public boolean isValid() { return getFromNode() != null && getToNode() != null; } private SNode myFromNode; public SNode getFromNode() { return this.myFromNode; } private void setFromNode(SNode value) { this.myFromNode = value; } private Object myFromId; public Object getFromId() { return this.myFromId; } private void setFromId(Object value) { this.myFromId = value; } private SNode myToNode; public SNode getToNode() { return this.myToNode; } private void setToNode(SNode value) { this.myToNode = value; } private Object myToId; public Object getToId() { return this.myToId; } private void setToId(Object value) { this.myToId = value; } } private static void check_xnhqai_a0a82(Registration checkedDotOperand) { if (null != checkedDotOperand) { checkedDotOperand.remove(); } } private static boolean check_xnhqai_a0a1a0a0a0b0a0a0a0a0hb(Boolean checkedDotOperand) { if (null != checkedDotOperand) { return checkedDotOperand.booleanValue(); } return false; } }