/* * Copyright 2000-2009 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.debugger.ui; import com.intellij.debugger.DebugUIEnvironment; import com.intellij.debugger.DebuggerBundle; import com.intellij.debugger.actions.DebuggerActions; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.TextWithImports; import com.intellij.debugger.impl.DebuggerContextImpl; import com.intellij.debugger.impl.DebuggerContextListener; import com.intellij.debugger.impl.DebuggerSession; import com.intellij.debugger.impl.DebuggerStateManager; import com.intellij.debugger.settings.DebuggerSettings; import com.intellij.debugger.ui.impl.MainWatchPanel; import com.intellij.debugger.ui.impl.ThreadsPanel; import com.intellij.debugger.ui.impl.VariablesPanel; import com.intellij.debugger.ui.impl.WatchDebuggerTree; import com.intellij.debugger.ui.impl.watch.*; import com.intellij.execution.DefaultExecutionResult; import com.intellij.execution.ExecutionManager; import com.intellij.execution.ExecutionResult; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.executors.DefaultDebugExecutor; import com.intellij.execution.filters.ExceptionFilters; import com.intellij.execution.filters.TextConsoleBuilder; import com.intellij.execution.filters.TextConsoleBuilderFactory; import com.intellij.execution.ui.ConsoleView; import com.intellij.execution.ui.ExecutionConsoleEx; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.execution.ui.RunnerLayoutUi; import com.intellij.execution.ui.layout.PlaceInGrid; import com.intellij.icons.AllIcons; import com.intellij.idea.ActionsBundle; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.ui.content.AlertIcon; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManagerAdapter; import com.intellij.ui.content.ContentManagerEvent; import com.intellij.ui.content.tabs.PinToolwindowTabAction; import com.intellij.unscramble.ThreadDumpPanel; import com.intellij.unscramble.ThreadState; import com.intellij.xdebugger.XDebuggerBundle; import com.intellij.xdebugger.impl.actions.XDebuggerActions; import com.intellij.xdebugger.impl.settings.XDebuggerSettingsManager; import com.intellij.xdebugger.impl.ui.DebuggerSessionTabBase; import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.TreePath; import java.util.List; public class DebuggerSessionTab extends DebuggerSessionTabBase implements Disposable { private static final Logger LOG = Logger.getInstance(DebuggerSessionTab.class); private final VariablesPanel myVariablesPanel; private final MainWatchPanel myWatchPanel; private volatile DebuggerSession myDebuggerSession; private final MyDebuggerStateManager myStateManager = new MyDebuggerStateManager(); private final FramesPanel myFramesPanel; private final DebugUIEnvironment myDebugUIEnvironment; private final ThreadsPanel myThreadsPanel; private static final String THREAD_DUMP_CONTENT_PREFIX = "Dump"; public DebuggerSessionTab(final Project project, final String sessionName, @NotNull final DebugUIEnvironment environment, @NotNull DebuggerSession debuggerSession) { super(project, "JavaDebugger", sessionName, debuggerSession.getSearchScope()); myDebuggerSession = debuggerSession; myDebugUIEnvironment = environment; final DefaultActionGroup focus = new DefaultActionGroup(); focus.add(ActionManager.getInstance().getAction("Debugger.FocusOnBreakpoint")); myUi.getOptions().setAdditionalFocusActions(focus); final DebuggerSettings debuggerSettings = DebuggerSettings.getInstance(); if (!ApplicationManager.getApplication().isUnitTestMode()) { getContextManager().addListener(new DebuggerContextListener() { @Override public void changeEvent(DebuggerContextImpl newContext, int event) { switch (event) { case DebuggerSession.EVENT_DETACHED: myUi.updateActionsNow(); if (XDebuggerSettingsManager.getInstanceImpl().getGeneralSettings().isHideDebuggerOnProcessTermination()) { try { ExecutionManager.getInstance(getProject()).getContentManager().hideRunContent(DefaultDebugExecutor.getDebugExecutorInstance(), myRunContentDescriptor); } catch (NullPointerException e) { //if we can get closeProcess after the project have been closed LOG.debug(e); } } break; } } }); } DefaultActionGroup topToolbar = new DefaultActionGroup(); ActionManager actionManager = ActionManager.getInstance(); topToolbar.addAll(getCustomizedActionGroup(XDebuggerActions.TOOL_WINDOW_TOP_TOOLBAR_GROUP)); topToolbar.add(actionManager.getAction(DebuggerActions.POP_FRAME), new Constraints(Anchor.AFTER, XDebuggerActions.STEP_OUT)); topToolbar.add(Separator.getInstance(), new Constraints(Anchor.BEFORE, DebuggerActions.POP_FRAME)); topToolbar.add(Separator.getInstance(), new Constraints(Anchor.AFTER, DebuggerActions.POP_FRAME)); myUi.getOptions().setTopToolbar(topToolbar, ActionPlaces.DEBUGGER_TOOLBAR); myWatchPanel = new MainWatchPanel(getProject(), getContextManager()); myFramesPanel = new FramesPanel(getProject(), getContextManager()); final AlertIcon breakpointAlert = new AlertIcon(AllIcons.Debugger.BreakpointAlert); // watches Content watches = myUi.createContent(DebuggerContentInfo.WATCHES_CONTENT, myWatchPanel, XDebuggerBundle.message("debugger.session.tab.watches.title"), AllIcons.Debugger.Watches, null); watches.setCloseable(false); watches.setAlertIcon(breakpointAlert); myUi.addContent(watches, 0, PlaceInGrid.right, false); // frames Content framesContent = myUi.createContent(DebuggerContentInfo.FRAME_CONTENT, myFramesPanel, XDebuggerBundle.message("debugger.session.tab.frames.title"), AllIcons.Debugger.Frame, myFramesPanel.getFramesList()); framesContent.setCloseable(false); framesContent.setAlertIcon(breakpointAlert); myUi.addContent(framesContent, 0, PlaceInGrid.left, false); // variables myVariablesPanel = new VariablesPanel(getProject(), myStateManager, this); myVariablesPanel.getFrameTree().setAutoVariablesMode(debuggerSettings.AUTO_VARIABLES_MODE); Content vars = myUi.createContent(DebuggerContentInfo.VARIABLES_CONTENT, myVariablesPanel, XDebuggerBundle.message("debugger.session.tab.variables.title"), AllIcons.Debugger.Value, null); vars.setCloseable(false); vars.setAlertIcon(breakpointAlert); myUi.addContent(vars, 0, PlaceInGrid.center, false); // threads myThreadsPanel = new ThreadsPanel(project, getContextManager()); Content threadsContent = myUi.createContent(DebuggerContentInfo.THREADS_CONTENT, myThreadsPanel, XDebuggerBundle.message("debugger.session.tab.threads.title"), AllIcons.Debugger.Threads, null); threadsContent.setCloseable(false); //threadsContent.setAlertIcon(breakpointAlert); //final DefaultActionGroup threadsGroup = new DefaultActionGroup(); //threadsContent.setActions(threadsGroup, ActionPlaces.DEBUGGER_TOOLBAR, threadsPanel.getThreadsTree()); myUi.addContent(threadsContent, 0, PlaceInGrid.left, true); for (Content each : myUi.getContents()) { updateStatus(each); } myUi.addListener(new ContentManagerAdapter() { @Override public void selectionChanged(ContentManagerEvent event) { updateStatus(event.getContent()); } }, this); debuggerSession.getContextManager().addListener(new DebuggerContextListener() { @Override public void changeEvent(DebuggerContextImpl newContext, int event) { if (!myUi.isDisposed()) { attractFramesOnPause(event); myStateManager.fireStateChanged(newContext, event); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (!myUi.isDisposed()) { myUi.updateActionsNow(); } } }); } } }); ExecutionResult executionResult = debuggerSession.getProcess().getExecutionResult(); myConsole = executionResult.getExecutionConsole(); myRunContentDescriptor = new RunContentDescriptor(myConsole, executionResult.getProcessHandler(), myUi.getComponent(), getSessionName(), environment.getIcon()); initUI(executionResult); } private static void updateStatus(final Content content) { if (content.getComponent() instanceof DebuggerView) { final DebuggerView view = (DebuggerView)content.getComponent(); if (content.isSelected()) { view.setUpdateEnabled(true); if (view.isRefreshNeeded()) { view.rebuildIfVisible(DebuggerSession.EVENT_CONTEXT); } } else { view.setUpdateEnabled(false); } } } public MainWatchPanel getWatchPanel() { return myWatchPanel; } @Override public RunContentDescriptor getRunContentDescriptor() { return myRunContentDescriptor; } private void initUI(ExecutionResult executionResult) { if (ApplicationManager.getApplication().isUnitTestMode()) { return; } myUi.removeContent(myUi.findContent(DebuggerContentInfo.CONSOLE_CONTENT), true); Content console = null; if (myConsole instanceof ExecutionConsoleEx) { ((ExecutionConsoleEx)myConsole).buildUi(myUi); console = myUi.findContent(DebuggerContentInfo.CONSOLE_CONTENT); if (console == null) { LOG.debug("Reuse console created with non-debug runner"); } } if (console == null) { console = myUi.createContent(DebuggerContentInfo.CONSOLE_CONTENT, myConsole.getComponent(), XDebuggerBundle.message("debugger.session.tab.console.content.name"), AllIcons.Debugger.Console, myConsole.getPreferredFocusableComponent()); console.setCloseable(false); myUi.addContent(console, 1, PlaceInGrid.bottom, false); } attachNotificationTo(console); if (myConsole != null) { Disposer.register(this, myConsole); } final DefaultActionGroup consoleActions = new DefaultActionGroup(); if (myConsole instanceof ConsoleView) { AnAction[] actions = ((ConsoleView)myConsole).createConsoleActions(); for (AnAction goAction : actions) { consoleActions.add(goAction); } } console.setActions(consoleActions, ActionPlaces.DEBUGGER_TOOLBAR, myConsole.getPreferredFocusableComponent()); myDebugUIEnvironment.initLogs(myRunContentDescriptor, myManager); DefaultActionGroup leftToolbar = new DefaultActionGroup(); if (executionResult instanceof DefaultExecutionResult) { final AnAction[] actions = ((DefaultExecutionResult)executionResult).getRestartActions(); if (actions != null) { leftToolbar.addAll(actions); if (actions.length > 0) { leftToolbar.addSeparator(); } } } final AnAction[] profileActions = executionResult.getActions(); leftToolbar.addAll(profileActions); leftToolbar.add(getCustomizedActionGroup(XDebuggerActions.TOOL_WINDOW_LEFT_TOOLBAR_GROUP)); if (executionResult instanceof DefaultExecutionResult) { AnAction[] actions = ((DefaultExecutionResult)executionResult).getAdditionalStopActions(); for (AnAction action : actions) { leftToolbar.add(action, new Constraints(Anchor.AFTER, IdeActions.ACTION_STOP_PROGRAM)); } } leftToolbar.addSeparator(); addAction(leftToolbar, DebuggerActions.EXPORT_THREADS); addAction(leftToolbar, DebuggerActions.DUMP_THREADS); leftToolbar.addSeparator(); leftToolbar.add(myUi.getOptions().getLayoutActions()); final AnAction[] commonSettings = myUi.getOptions().getSettingsActionsList(); final AnAction commonSettingsList = myUi.getOptions().getSettingsActions(); final DefaultActionGroup settings = new DefaultActionGroup("DebuggerSettings", true) { @Override public void update(AnActionEvent e) { e.getPresentation().setText(ActionsBundle.message("group.XDebugger.settings.text")); e.getPresentation().setIcon(commonSettingsList.getTemplatePresentation().getIcon()); } @Override public boolean isDumbAware() { return true; } }; for (AnAction each : commonSettings) { settings.add(each); } if (commonSettings.length > 0) { settings.addSeparator(); } settings.add(new WatchLastMethodReturnValueAction()); settings.add(new AutoVarsSwitchAction()); settings.addSeparator(); addActionToGroup(settings, XDebuggerActions.AUTO_TOOLTIP); leftToolbar.add(settings); leftToolbar.addSeparator(); addActionToGroup(leftToolbar, PinToolwindowTabAction.ACTION_NAME); myDebugUIEnvironment.initActions(myRunContentDescriptor, leftToolbar); myUi.getOptions().setLeftToolbar(leftToolbar, ActionPlaces.DEBUGGER_TOOLBAR); } private static void addAction(DefaultActionGroup group, String actionId) { group.add(ActionManager.getInstance().getAction(actionId)); } private static void addActionToGroup(final DefaultActionGroup group, final String actionId) { AnAction action = ActionManager.getInstance().getAction(actionId); if (action != null) group.add(action); } @Override public void dispose() { disposeSession(); myFramesPanel.dispose(); myVariablesPanel.dispose(); myWatchPanel.dispose(); myThreadsPanel.dispose(); myConsole = null; super.dispose(); } private void disposeSession() { final DebuggerSession session = myDebuggerSession; myDebuggerSession = null; if (session != null) { session.dispose(); } } @Nullable private DebugProcessImpl getDebugProcess() { final DebuggerSession session = myDebuggerSession; return session != null ? session.getProcess() : null; } public void reuse(DebuggerSessionTab reuseSession) { DebuggerTreeNodeImpl[] watches = reuseSession.getWatchPanel().getWatchTree().getWatches(); final WatchDebuggerTree watchTree = getWatchPanel().getWatchTree(); for (DebuggerTreeNodeImpl watch : watches) { watchTree.addWatch((WatchItemDescriptor)watch.getDescriptor()); } DebugProcessImpl process = getDebugProcess(); DebugProcessImpl reuseProcess = reuseSession.getDebugProcess(); if (process != null && reuseProcess != null) { //process.setBreakpointsMuted(reuseProcess.areBreakpointsMuted()); } } public String getSessionName() { return myDebugUIEnvironment.getEnvironment().getSessionName(); } public DebuggerStateManager getContextManager() { return myStateManager; } @Nullable public TextWithImports getSelectedExpression() { final DebuggerSession session = myDebuggerSession; if (session == null || session.getState() != DebuggerSession.STATE_PAUSED) { return null; } JTree tree = myVariablesPanel.getFrameTree(); if (tree == null || !tree.hasFocus()) { tree = myWatchPanel.getWatchTree(); if (tree == null || !tree.hasFocus()) { return null; } } TreePath path = tree.getSelectionPath(); if (path == null) { return null; } DebuggerTreeNodeImpl node = (DebuggerTreeNodeImpl)path.getLastPathComponent(); if (node == null) { return null; } NodeDescriptorImpl descriptor = node.getDescriptor(); if (!(descriptor instanceof ValueDescriptorImpl)) { return null; } if (descriptor instanceof WatchItemDescriptor) { return ((WatchItemDescriptor)descriptor).getEvaluationText(); } try { return DebuggerTreeNodeExpression.createEvaluationText(node, getContextManager().getContext()); } catch (EvaluateException e) { return null; } } @Nullable @Override protected RunProfile getRunProfile() { return myDebugUIEnvironment.getRunProfile(); } private void attractFramesOnPause(final int event) { if (DebuggerSession.EVENT_PAUSE == event) { myUi.attractBy(XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION); } else if (DebuggerSession.EVENT_RESUME == event) { myUi.clearAttractionBy(XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION); } } public DebuggerSession getSession() { return myDebuggerSession; } public void showFramePanel() { myUi.selectAndFocus(myUi.findContent(DebuggerContentInfo.FRAME_CONTENT), true, false); } private static int myThreadDumpsCount = 0; private static int myCurrentThreadDumpId = 1; public static void addThreadDump(Project project, List threads, final RunnerLayoutUi ui, DebuggerSession session) { final TextConsoleBuilder consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(project); consoleBuilder.filters(ExceptionFilters.getFilters(session.getSearchScope())); final ConsoleView consoleView = consoleBuilder.getConsole(); final DefaultActionGroup toolbarActions = new DefaultActionGroup(); consoleView.allowHeavyFilters(); final ThreadDumpPanel panel = new ThreadDumpPanel(project, consoleView, toolbarActions, threads); final String id = createThreadDumpContentId(); final Content content = ui.createContent(id, panel, id, null, null); content.setCloseable(true); content.setDescription("Thread Dump"); ui.addContent(content); ui.selectAndFocus(content, true, true); myThreadDumpsCount += 1; myCurrentThreadDumpId += 1; //Disposer.register(this, new Disposable() { // @Override // public void dispose() { // ui.removeContent(content, true); // } //}); Disposer.register(content, new Disposable() { @Override public void dispose() { myThreadDumpsCount -= 1; if (myThreadDumpsCount == 0) { myCurrentThreadDumpId = 1; } } }); Disposer.register(content, consoleView); ui.selectAndFocus(content, true, false); if (threads.size() > 0) { panel.selectStackFrame(0); } } private static String createThreadDumpContentId() { return THREAD_DUMP_CONTENT_PREFIX + " #" + myCurrentThreadDumpId; } private class MyDebuggerStateManager extends DebuggerStateManager { @Override public void fireStateChanged(DebuggerContextImpl newContext, int event) { super.fireStateChanged(newContext, event); } @Override public DebuggerContextImpl getContext() { final DebuggerSession session = myDebuggerSession; return session != null ? session.getContextManager().getContext() : DebuggerContextImpl.EMPTY_CONTEXT; } @Override public void setState(DebuggerContextImpl context, int state, int event, String description) { final DebuggerSession session = myDebuggerSession; if (session != null) { session.getContextManager().setState(context, state, event, description); } } } private class AutoVarsSwitchAction extends ToggleAction { private volatile boolean myAutoModeEnabled; public AutoVarsSwitchAction() { super("", "", AllIcons.Debugger.AutoVariablesMode); myAutoModeEnabled = DebuggerSettings.getInstance().AUTO_VARIABLES_MODE; } @Override public void update(final AnActionEvent e) { super.update(e); final Presentation presentation = e.getPresentation(); final boolean autoModeEnabled = (Boolean)presentation.getClientProperty(SELECTED_PROPERTY); presentation.setText(autoModeEnabled ? "All-Variables Mode" : "Auto-Variables Mode"); } @Override public boolean isSelected(AnActionEvent e) { return myAutoModeEnabled; } @Override public void setSelected(AnActionEvent e, boolean enabled) { myAutoModeEnabled = enabled; DebuggerSettings.getInstance().AUTO_VARIABLES_MODE = enabled; myVariablesPanel.getFrameTree().setAutoVariablesMode(enabled); } } private class WatchLastMethodReturnValueAction extends ToggleAction { private volatile boolean myWatchesReturnValues; private final String myTextEnable; private final String myTextUnavailable; private final String myMyTextDisable; public WatchLastMethodReturnValueAction() { super("", DebuggerBundle.message("action.watch.method.return.value.description"), null); myWatchesReturnValues = DebuggerSettings.getInstance().WATCH_RETURN_VALUES; myTextEnable = DebuggerBundle.message("action.watches.method.return.value.enable"); myMyTextDisable = DebuggerBundle.message("action.watches.method.return.value.disable"); myTextUnavailable = DebuggerBundle.message("action.watches.method.return.value.unavailable.reason"); } @Override public void update(final AnActionEvent e) { super.update(e); final Presentation presentation = e.getPresentation(); final boolean watchValues = (Boolean)presentation.getClientProperty(SELECTED_PROPERTY); final DebugProcessImpl process = getDebugProcess(); final String actionText = watchValues ? myMyTextDisable : myTextEnable; if (process != null && process.canGetMethodReturnValue()) { presentation.setEnabled(true); presentation.setText(actionText); } else { presentation.setEnabled(false); presentation.setText(process == null ? actionText : myTextUnavailable); } } @Override public boolean isSelected(AnActionEvent e) { return myWatchesReturnValues; } @Override public void setSelected(AnActionEvent e, boolean watch) { myWatchesReturnValues = watch; DebuggerSettings.getInstance().WATCH_RETURN_VALUES = watch; final DebugProcessImpl process = getDebugProcess(); if (process != null) { process.setWatchMethodReturnValuesEnabled(watch); } } } }