/* * Copyright 2003-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.nodeEditor; import com.intellij.ide.DataManager; import com.intellij.ide.plugins.cl.PluginClassLoader; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.ui.SimpleToolWindowPanel; import com.intellij.openapi.wm.ToolWindowAnchor; import com.intellij.ui.HyperlinkLabel; import com.intellij.ui.LightColors; import jetbrains.mps.ide.ThreadUtils; import jetbrains.mps.ide.actions.MPSCommonDataKeys; import jetbrains.mps.ide.icons.IdeIcons; import jetbrains.mps.ide.project.ProjectHelper; import jetbrains.mps.ide.tools.BaseTool; import jetbrains.mps.nodeEditor.configuration.EditorConfigurationBuilder; import jetbrains.mps.nodeEditor.inspector.InspectorEditorComponent; import jetbrains.mps.openapi.editor.EditorInspector; import jetbrains.mps.openapi.editor.extensions.EditorExtensionUtil; import jetbrains.mps.openapi.editor.style.StyleRegistry; import jetbrains.mps.openapi.navigation.EditorNavigator; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.model.SNodeReference; import org.jetbrains.mps.openapi.model.SNodeUtil; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import java.awt.BorderLayout; import java.awt.Color; public class InspectorTool extends BaseTool implements EditorInspector, ProjectComponent { public static final String ID = "Inspector"; private MyPanel myComponent; private InspectorEditorComponent myInspectorComponent; private MyMessagePanel myMessagePanel; private FileEditor myFileEditor; public InspectorTool(Project project) { super(project, ID,2, IdeIcons.INSPECTOR_ICON, ToolWindowAnchor.BOTTOM, true, false); } @Override public void initComponent() { createTool(); StartupManager.getInstance(getProject()).registerPostStartupActivity(this::registerLater); } @Override public void disposeComponent() { if (myInspectorComponent == null) { return; } ThreadUtils.runInUIThreadNoWait(myInspectorComponent::dispose); unregister(); } @Override public void projectOpened() { } @Override public void projectClosed() { } protected void createTool() { StartupManager.getInstance(getProject()).registerStartupActivity(() -> ApplicationManager.getApplication().invokeLater(() -> { InspectorTool.this.myMessagePanel = new MyMessagePanel(); myComponent = new MyPanel(); jetbrains.mps.project.Project project = ProjectHelper.toMPSProject(getProject()); myInspectorComponent = new InspectorEditorComponent(project.getRepository(), new EditorConfigurationBuilder().editorPanelManager(new EditorPanelManagerImpl(project)).build()); EditorExtensionUtil.extendUsingProject(myInspectorComponent, project); myComponent.setContent(myInspectorComponent.getExternalComponent()); myMessagePanel.setNode(null); myComponent.setToolbar(myMessagePanel); AnAction moveDownAction = ActionManager.getInstance().getAction("jetbrains.mps.ide.editor.actions.MoveElementsDown_Action"); moveDownAction.registerCustomShortcutSet(moveDownAction.getShortcutSet(), myComponent); AnAction moveUpAction = ActionManager.getInstance().getAction("jetbrains.mps.ide.editor.actions.MoveElementsUp_Action"); moveUpAction.registerCustomShortcutSet(moveUpAction.getShortcutSet(), myComponent); })); } @Override protected boolean isInitiallyAvailable() { return true; } @Override public void activate() { openTool(true); } public EditorComponent getInspector() { return myInspectorComponent; } @Override public JComponent getComponent() { return myComponent; } public void inspect(SNode node, FileEditor fileEditor, String[] enabledHints) { if (node instanceof jetbrains.mps.smodel.SNode && !SNodeUtil.isAccessible(node, myInspectorComponent.getRepository())) { // Note: inspector does not support disposed nodes. If we get one, just clear the tool. // The editor holds references to nodes between read actions and these references are updated asynchronously. // This means that sometimes an editor may give us an outdated node. node = null; } myFileEditor = fileEditor; boolean needToEdit = myInspectorComponent.getUpdater().setInitialEditorHints(enabledHints); if (needToEdit || myInspectorComponent.getEditedNode() != node) { myInspectorComponent.editNode(node); } myMessagePanel.setNode(node); } private class MyPanel extends SimpleToolWindowPanel { private MyPanel() { super(true, true); setProvideQuickActions(false); } @Override @Nullable public Object getData(@NonNls String dataId) { if (MPSCommonDataKeys.FILE_EDITOR.getName().equals(dataId)) { return myFileEditor; } if (PlatformDataKeys.VIRTUAL_FILE.getName().equals(dataId) && myFileEditor != null) { return DataManager.getInstance().getDataContext(myFileEditor.getComponent()).getData(dataId); } if (PlatformDataKeys.HELP_ID.is(dataId)) { return "ideaInterface.editor.inspector"; } return super.getData(dataId); } } private class MyMessagePanel extends JPanel { private static final String NO_CONCEPT_MESSAGE = "<no node>"; private JLabel myLabel = new JLabel(); private HyperlinkLabel myOpenConceptLabel = new HyperlinkLabel("Open Concept Declaration"); private SNode myNode; private MyMessagePanel() { setLayout(new BorderLayout()); setBackground(StyleRegistry.getInstance().isDarkTheme() ? Color.LIGHT_GRAY : LightColors.YELLOW); setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4)); myLabel.setForeground(StyleRegistry.getInstance().isDarkTheme() ? Color.DARK_GRAY : StyleRegistry.getInstance().getEditorForeground()); add(myLabel, BorderLayout.CENTER); add(myOpenConceptLabel, BorderLayout.EAST); myOpenConceptLabel.setOpaque(false); myOpenConceptLabel.addHyperlinkListener(e -> { if (myNode == null) { return; } SNodeReference conceptDecl = myNode.getConcept().getSourceNode(); if (conceptDecl != null) { new EditorNavigator(ProjectHelper.fromIdeaProject(getProject())).shallFocus(true).shallSelect(false).open(conceptDecl); } }); } public void setNode(SNode node) { myNode = node; if (node == null) { myLabel.setText(NO_CONCEPT_MESSAGE); myOpenConceptLabel.setVisible(false); } else { myLabel.setText(node.getConcept().getQualifiedName()); myOpenConceptLabel.setVisible(true); } } } }