/* * Copyright 2000-2017 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 com.intellij.xdebugger.impl.frame; import com.intellij.ide.dnd.DnDManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.event.SelectionEvent; import com.intellij.openapi.editor.event.SelectionListener; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerBundle; import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.evaluation.ExpressionInfo; import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; import com.intellij.xdebugger.evaluation.XDebuggerEvaluator; import com.intellij.xdebugger.frame.XStackFrame; import com.intellij.xdebugger.frame.XValueContainer; import com.intellij.xdebugger.impl.XDebuggerInlayUtil; import com.intellij.xdebugger.impl.actions.XDebuggerActions; import com.intellij.xdebugger.impl.evaluate.quick.XValueHint; import com.intellij.xdebugger.impl.evaluate.quick.common.ValueHintType; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreePanel; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeRestorer; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.nodes.XStackFrameNode; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueContainerNode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; /** * @author nik */ public abstract class XVariablesViewBase extends XDebugView { private final XDebuggerTreePanel myTreePanel; private XDebuggerTreeState myTreeState; private XDebuggerTreeRestorer myTreeRestorer; private Object myFrameEqualityObject; private MySelectionListener mySelectionListener; protected XVariablesViewBase(@NotNull Project project, @NotNull XDebuggerEditorsProvider editorsProvider, @Nullable XValueMarkers<?, ?> markers) { myTreePanel = new XDebuggerTreePanel( project, editorsProvider, this, null, this instanceof XWatchesView ? XDebuggerActions.WATCHES_TREE_POPUP_GROUP : XDebuggerActions.VARIABLES_TREE_POPUP_GROUP, markers); getTree().getEmptyText().setText(XDebuggerBundle.message("debugger.variables.not.available")); DnDManager.getInstance().registerSource(myTreePanel, getTree()); } protected void buildTreeAndRestoreState(@NotNull final XStackFrame stackFrame) { XSourcePosition position = stackFrame.getSourcePosition(); XDebuggerTree tree = getTree(); tree.setSourcePosition(position); createNewRootNode(stackFrame); final Project project = tree.getProject(); project.putUserData(XVariablesView.DEBUG_VARIABLES, new XVariablesView.InlineVariablesInfo()); clearInlays(tree); Object newEqualityObject = stackFrame.getEqualityObject(); if (myFrameEqualityObject != null && newEqualityObject != null && myFrameEqualityObject.equals(newEqualityObject) && myTreeState != null) { disposeTreeRestorer(); myTreeRestorer = myTreeState.restoreState(tree); } if (position != null && Registry.is("debugger.valueTooltipAutoShowOnSelection")) { registerInlineEvaluator(stackFrame, position, project); } } protected static void clearInlays(XDebuggerTree tree) { if (Registry.is("debugger.show.values.inplace")) XDebuggerInlayUtil.clearInlays(tree.getProject()); } protected XValueContainerNode createNewRootNode(@Nullable XStackFrame stackFrame) { XValueContainerNode root; if (stackFrame == null) { root = new XValueContainerNode<XValueContainer>(getTree(), null, new XValueContainer() {}) {}; } else { root = new XStackFrameNode(getTree(), stackFrame); } getTree().setRoot(root, false); return root; } private void registerInlineEvaluator(final XStackFrame stackFrame, final XSourcePosition position, final Project project) { final VirtualFile file = position.getFile(); final FileEditor fileEditor = FileEditorManagerEx.getInstanceEx(project).getSelectedEditor(file); if (fileEditor instanceof PsiAwareTextEditorImpl) { final Editor editor = ((PsiAwareTextEditorImpl)fileEditor).getEditor(); removeSelectionListener(); mySelectionListener = new MySelectionListener(editor, stackFrame, project); editor.getSelectionModel().addSelectionListener(mySelectionListener); } } protected void saveCurrentTreeState(@Nullable XStackFrame stackFrame) { removeSelectionListener(); myFrameEqualityObject = stackFrame != null ? stackFrame.getEqualityObject() : null; if (myTreeRestorer == null || myTreeRestorer.isFinished()) { myTreeState = XDebuggerTreeState.saveState(getTree()); } disposeTreeRestorer(); } private void removeSelectionListener() { if (mySelectionListener != null) { mySelectionListener.remove(); mySelectionListener = null; } } @Override protected void clear() { removeSelectionListener(); } private void disposeTreeRestorer() { if (myTreeRestorer != null) { myTreeRestorer.dispose(); myTreeRestorer = null; } } @NotNull public final XDebuggerTree getTree() { return myTreePanel.getTree(); } public JComponent getPanel() { return myTreePanel.getMainPanel(); } @Override public void dispose() { disposeTreeRestorer(); removeSelectionListener(); DnDManager.getInstance().unregisterSource(myTreePanel, getTree()); } private class MySelectionListener implements SelectionListener { private final Editor myEditor; private final XStackFrame myStackFrame; private final Project myProject; public MySelectionListener(Editor editor, XStackFrame stackFrame, Project project) { myEditor = editor; myStackFrame = stackFrame; myProject = project; } public void remove() { myEditor.getSelectionModel().removeSelectionListener(this); } @Override public void selectionChanged(final SelectionEvent e) { if (!Registry.is("debugger.valueTooltipAutoShowOnSelection") || myEditor.getCaretModel().getCaretCount() > 1) { return; } final String text = myEditor.getDocument().getText(e.getNewRange()); if (!StringUtil.isEmpty(text) && !(text.contains("exec(") || text.contains("++") || text.contains("--") || text.contains("="))) { final XDebugSession session = getSession(getTree()); if (session == null) return; XDebuggerEvaluator evaluator = myStackFrame.getEvaluator(); if (evaluator == null) return; TextRange range = e.getNewRange(); ExpressionInfo info = new ExpressionInfo(range); int offset = range.getStartOffset(); LogicalPosition pos = myEditor.offsetToLogicalPosition(offset); Point point = myEditor.logicalPositionToXY(pos); new XValueHint(myProject, myEditor, point, ValueHintType.MOUSE_OVER_HINT, info, evaluator, session, true).invokeHint(); } } } }