package jetbrains.mps.vcs.diff.ui; /*Generated by MPS */ import com.intellij.openapi.actionSystem.DataProvider; import jetbrains.mps.project.MPSProject; import jetbrains.mps.vcs.diff.ModelChangeSet; import org.jetbrains.mps.openapi.model.SNodeId; import javax.swing.JPanel; import java.awt.BorderLayout; import com.intellij.ui.JBSplitter; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.SwingConstants; import com.intellij.openapi.diff.ex.DiffStatusBar; import com.intellij.openapi.diff.impl.util.TextDiffType; import jetbrains.mps.vcs.diff.ui.common.GoToNeighbourRootActions; import org.jetbrains.mps.openapi.model.SModel; import jetbrains.mps.util.SNodeOperations; import org.jetbrains.mps.openapi.model.EditableSModel; import jetbrains.mps.vcs.diff.ui.common.DiffModelUtil; import jetbrains.mps.vcs.diff.ChangeSetBuilder; import com.intellij.ui.ScrollPaneFactory; import java.awt.Dimension; import com.intellij.openapi.util.DimensionService; import org.jetbrains.annotations.Nullable; import jetbrains.mps.internal.collections.runtime.ListSequence; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.actionSystem.ActionToolbar; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionPlaces; import jetbrains.mps.vcs.diff.ui.common.Bounds; import org.jetbrains.annotations.NonNls; import jetbrains.mps.vcs.diff.ui.common.DiffModelTree; import com.intellij.openapi.util.Ref; import jetbrains.mps.workbench.action.BaseAction; import java.util.List; import java.util.ArrayList; import jetbrains.mps.vcs.diff.changes.ModelChange; import jetbrains.mps.internal.collections.runtime.Sequence; import jetbrains.mps.internal.collections.runtime.ITranslator2; import jetbrains.mps.internal.collections.runtime.ISelector; import jetbrains.mps.internal.collections.runtime.IWhereFilter; import org.jetbrains.annotations.NotNull; import jetbrains.mps.vcs.diff.changes.ChangeType; import jetbrains.mps.vcs.diff.changes.AddRootChange; import jetbrains.mps.vcs.diff.changes.DeleteRootChange; import jetbrains.mps.vcs.diff.ui.common.ChangeColors; import java.util.Arrays; public class ModelDifferenceViewer implements DataProvider { private final MPSProject myProject; private ModelChangeSet myChangeSet; private ModelChangeSet myMetadataChangeSet; private SNodeId myRootId; private ModelDifferenceViewer.ModelDifferenceTree myTree = null; private JPanel myComponent = new JPanel(new BorderLayout()); private JBSplitter myPanel = new JBSplitter(true, 0.25f); private RootDifferencePane myRootDifferencePane = null; private final JComponent myNoRootPanel = new JLabel("Select root to show", SwingConstants.CENTER); private DiffStatusBar myStatusBar = new DiffStatusBar(TextDiffType.DIFF_TYPES); private GoToNeighbourRootActions myGoToNeighbourRootActions; private String[] myContentTitles = new String[]{"before", "after"}; private boolean myEditable; private boolean myOldRegistered; private boolean myNewRegistered; public ModelDifferenceViewer(MPSProject project, final SModel oldModel, final SModel newModel, SNodeId rootId, boolean showTree) { myProject = project; myOldRegistered = SNodeOperations.isRegistered(oldModel); myNewRegistered = SNodeOperations.isRegistered(newModel); myEditable = newModel instanceof EditableSModel && myNewRegistered; // register models in repository and create changeset project.getRepository().getModelAccess().runWriteAction(new Runnable() { public void run() { if (!(myNewRegistered)) { DiffModelUtil.renameModelAndRegister(newModel, "new"); } if (!(myOldRegistered)) { DiffModelUtil.renameModelAndRegister(oldModel, "old"); } } }); project.getRepository().getModelAccess().runReadAction(new Runnable() { public void run() { myChangeSet = ChangeSetBuilder.buildChangeSet(oldModel, newModel, true); } }); project.getRepository().getModelAccess().runWriteAction(new Runnable() { public void run() { SModel newMetaModel = MetadataUtil.createMetadataModel(newModel, "metadata_new", myEditable); SModel oldMetaModel = MetadataUtil.createMetadataModel(oldModel, "metadata_old", false); myMetadataChangeSet = ChangeSetBuilder.buildChangeSet(oldMetaModel, newMetaModel, true); } }); // create panels myPanel.setSplitterProportionKey(getClass().getName() + "ModelTreeSplitter"); myPanel.setSecondComponent(myNoRootPanel); if (showTree) { myTree = new ModelDifferenceViewer.ModelDifferenceTree(); myPanel.setFirstComponent(ScrollPaneFactory.createScrollPane(myTree)); // actions connected to model tree myGoToNeighbourRootActions = new ModelDifferenceViewer.MyGoToNeighbourRootActions(); myGoToNeighbourRootActions.previous().registerCustomShortcutSet(GoToNeighbourRootActions.PREV_ROOT_SHORTCUT, myComponent); myGoToNeighbourRootActions.next().registerCustomShortcutSet(GoToNeighbourRootActions.NEXT_ROOT_SHORTCUT, myComponent); if (rootId != null) { setCurrentRoot(rootId); } } else { setCurrentRoot(rootId); } myComponent.add(myPanel, BorderLayout.CENTER); myComponent.add(myStatusBar, BorderLayout.SOUTH); Dimension size = DimensionService.getInstance().getSize(getDimensionServiceKey()); if (size == null) { myComponent.setPreferredSize(new Dimension(500, 700)); } } public String getDimensionServiceKey() { return getClass().getName(); } @Nullable public JComponent getPreferredFocusedComponent() { return (myTree != null ? myTree : null); } public void dispose() { syncMetadataChanges(); myProject.getRepository().getModelAccess().runWriteAction(new Runnable() { public void run() { MetadataUtil.dispose(myMetadataChangeSet.getOldModel()); MetadataUtil.dispose(myMetadataChangeSet.getNewModel()); if (!(myOldRegistered)) { DiffModelUtil.unregisterModel(myChangeSet.getOldModel()); } if (!(myNewRegistered)) { DiffModelUtil.unregisterModel(myChangeSet.getNewModel()); } } }); if (myRootDifferencePane != null) { myRootDifferencePane.dispose(); } } /*package*/ void rebuildChangeSets() { ChangeSetBuilder.rebuildChangeSet(myChangeSet); ChangeSetBuilder.rebuildChangeSet(myMetadataChangeSet); if (myTree != null) { myTree.rebuildLater(); } } public boolean isEditable() { return myEditable; } private void syncMetadataChanges() { if (myEditable) { myProject.getModelAccess().executeCommand(new Runnable() { public void run() { MetadataUtil.applyMetadataChanges(myChangeSet.getNewModel(), myMetadataChangeSet.getNewModel()); } }); } } public void setContentTitles(String before, String after) { myContentTitles[0] = before; myContentTitles[1] = after; } public void resetCurrentRoot() { if (myRootDifferencePane == null) { return; } myRootDifferencePane.unregisterShortcuts(myComponent); myPanel.setSecondComponent(myNoRootPanel); myRootDifferencePane.dispose(); myRootDifferencePane = null; myRootId = null; myStatusBar.setText(""); syncMetadataChanges(); } private void changeCurrentRoot(@Nullable final SNodeId rootId) { if (myRootDifferencePane != null && myRootId == rootId) { return; } syncMetadataChanges(); myRootId = rootId; myProject.getRepository().getModelAccess().runReadAction(new Runnable() { public void run() { ModelChangeSet changeSet = (rootId == null ? myMetadataChangeSet : myChangeSet); SNodeId nodeId = (rootId == null ? ListSequence.fromList(SModelOperations.roots(myMetadataChangeSet.getOldModel(), null)).first().getNodeId() : rootId); if (myRootDifferencePane == null) { myRootDifferencePane = new RootDifferencePane(myProject, changeSet, nodeId, getNameForRoot(rootId), myContentTitles, myEditable, myStatusBar); DefaultActionGroup actionGroup = new DefaultActionGroup(); actionGroup.addAll(myRootDifferencePane.getActions()); ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, actionGroup, true); myRootDifferencePane.registerShortcuts(myComponent); JPanel panel = new JPanel(new BorderLayout()); panel.add(toolbar.getComponent(), BorderLayout.NORTH); panel.add(myRootDifferencePane.getPanel(), BorderLayout.CENTER); myPanel.setSecondComponent(panel); myRootDifferencePane.navigateInitial(null); } else { myRootDifferencePane.setRootId(nodeId, changeSet); } } }); } private String getNameForRoot(SNodeId rootId) { return (myTree != null ? myTree.getNameForRoot(rootId) : ""); } public void setCurrentRoot(@Nullable SNodeId rootId) { if (myTree != null) { myTree.setSelected(rootId); } changeCurrentRoot(rootId); } @Nullable public SNodeId getCurrentRoot() { return myRootId; } public JComponent getComponent() { return myComponent; } public void navigate(@Nullable final Bounds scrollTo) { assert myRootDifferencePane != null; myProject.getModelAccess().runReadAction(new Runnable() { public void run() { myRootDifferencePane.navigateInitial(scrollTo); } }); } @Nullable public Object getData(@NonNls String dataId) { if (DiffModelTree.NODE_ID_DATAKEY.is(dataId)) { return new Ref<SNodeId>(myRootId); } return null; } public class MyGoToNeighbourRootActions extends GoToNeighbourRootActions.GoToByTree { public MyGoToNeighbourRootActions() { super(myTree); } @Nullable @Override protected SNodeId getCurrentNodeId() { return getCurrentRoot(); } @Override public void setCurrentNodeId(@Nullable SNodeId nodeId) { changeCurrentRoot(nodeId); } } private class ModelDifferenceTree extends DiffModelTree { private ModelDifferenceTree() { } @Override protected Iterable<BaseAction> getRootActions() { List<BaseAction> actions = ListSequence.fromList(new ArrayList<BaseAction>()); if (myEditable) { ListSequence.fromList(actions).addElement(new RevertRootsAction("roots") { @Override protected Iterable<ModelChange> getChanges() { return Sequence.fromIterable(Sequence.fromArray(getSelectedNodes(DiffModelTree.RootTreeNode.class, null))).translate(new ITranslator2<DiffModelTree.RootTreeNode, ModelChange>() { public Iterable<ModelChange> translate(DiffModelTree.RootTreeNode r) { return myChangeSet.getChangesForRoot(r.getRootId()); } }); } @Override protected void after() { rebuildChangeSets(); } @Override protected String getRevertTitle() { Iterable<SNodeId> roots = Sequence.fromIterable(Sequence.fromArray(getSelectedNodes(DiffModelTree.RootTreeNode.class, null))).select(new ISelector<DiffModelTree.RootTreeNode, SNodeId>() { public SNodeId select(DiffModelTree.RootTreeNode rtn) { return rtn.getRootId(); } }); if (Sequence.fromIterable(roots).count() == 1) { return (Sequence.fromIterable(roots).first() == null ? "Properties" : "Root"); } else if (Sequence.fromIterable(roots).any(new IWhereFilter<SNodeId>() { public boolean accept(SNodeId r) { return r == null; } })) { return "Roots and Properties "; } return "Roots"; } }); } return actions; } @Override protected void updateRootCustomPresentation(@NotNull DiffModelTree.RootTreeNode rootTreeNode) { ChangeType compositeChangeType = ChangeType.CHANGE; if (rootTreeNode.getRootId() != null) { ModelChange firstChange = Sequence.fromIterable(myChangeSet.getChangesForRoot(rootTreeNode.getRootId())).first(); if (firstChange instanceof AddRootChange || firstChange instanceof DeleteRootChange) { compositeChangeType = firstChange.getType(); } else if (firstChange == null) { compositeChangeType = null; } } else { if (myMetadataChangeSet == null || ListSequence.fromList(myMetadataChangeSet.getModelChanges()).isEmpty()) { compositeChangeType = null; } } rootTreeNode.setColor((compositeChangeType == null ? null : ChangeColors.getForTree(compositeChangeType))); } @Override protected Iterable<SModel> getModels() { return Arrays.asList(myChangeSet.getNewModel(), myChangeSet.getOldModel()); } @Override protected Iterable<SNodeId> getAffectedRoots() { return myChangeSet.getAffectedRoots(); } @Override protected void onUnselect() { resetCurrentRoot(); } @Override protected void onSelectRoot(@Nullable SNodeId rootId) { changeCurrentRoot(rootId); } } }