2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.xdebugger.impl.ui;
18 import com.intellij.debugger.ui.DebuggerContentInfo;
19 import com.intellij.execution.Executor;
20 import com.intellij.execution.executors.DefaultDebugExecutor;
21 import com.intellij.execution.runners.ExecutionEnvironment;
22 import com.intellij.execution.runners.RunContentBuilder;
23 import com.intellij.execution.ui.RunContentDescriptor;
24 import com.intellij.execution.ui.RunnerLayoutUi;
25 import com.intellij.execution.ui.actions.CloseAction;
26 import com.intellij.execution.ui.layout.PlaceInGrid;
27 import com.intellij.execution.ui.layout.impl.RunnerContentUi;
28 import com.intellij.execution.ui.layout.impl.ViewImpl;
29 import com.intellij.icons.AllIcons;
30 import com.intellij.ide.DataManager;
31 import com.intellij.ide.actions.ContextHelpAction;
32 import com.intellij.idea.ActionsBundle;
33 import com.intellij.openapi.actionSystem.*;
34 import com.intellij.openapi.application.ApplicationManager;
35 import com.intellij.openapi.util.Disposer;
36 import com.intellij.openapi.util.registry.Registry;
37 import com.intellij.psi.search.GlobalSearchScope;
38 import com.intellij.ui.AppUIUtil;
39 import com.intellij.ui.content.Content;
40 import com.intellij.ui.content.ContentManagerAdapter;
41 import com.intellij.ui.content.ContentManagerEvent;
42 import com.intellij.ui.content.tabs.PinToolwindowTabAction;
43 import com.intellij.util.SystemProperties;
44 import com.intellij.util.containers.ContainerUtil;
45 import com.intellij.util.containers.hash.LinkedHashMap;
46 import com.intellij.xdebugger.XDebugSession;
47 import com.intellij.xdebugger.XDebuggerBundle;
48 import com.intellij.xdebugger.impl.XDebugSessionImpl;
49 import com.intellij.xdebugger.impl.actions.XDebuggerActions;
50 import com.intellij.xdebugger.impl.frame.*;
51 import com.intellij.xdebugger.impl.ui.tree.actions.SortValuesToggleAction;
52 import com.intellij.xdebugger.ui.XDebugTabLayouter;
53 import org.jetbrains.annotations.NonNls;
54 import org.jetbrains.annotations.NotNull;
55 import org.jetbrains.annotations.Nullable;
58 import java.util.List;
60 public class XDebugSessionTab extends DebuggerSessionTabBase {
61 public static final DataKey<XDebugSessionTab> TAB_KEY = DataKey.create("XDebugSessionTab");
63 private XWatchesViewImpl myWatchesView;
64 private boolean myWatchesInVariables = Registry.is("debugger.watches.in.variables");
65 private final LinkedHashMap<String, XDebugView> myViews = new LinkedHashMap<>();
68 private XDebugSessionImpl mySession;
69 private XDebugSessionData mySessionData;
71 private final Runnable myRebuildWatchesRunnable = new Runnable() {
74 if (myWatchesView != null && myWatchesView.rebuildNeeded()) {
75 myWatchesView.processSessionEvent(XDebugView.SessionEvent.SETTINGS_CHANGED);
81 public static XDebugSessionTab create(@NotNull XDebugSessionImpl session,
83 @Nullable ExecutionEnvironment environment,
84 @Nullable RunContentDescriptor contentToReuse) {
85 if (contentToReuse != null && SystemProperties.getBooleanProperty("xdebugger.reuse.session.tab", false)) {
86 JComponent component = contentToReuse.getComponent();
87 if (component != null) {
88 XDebugSessionTab oldTab = TAB_KEY.getData(DataManager.getInstance().getDataContext(component));
90 oldTab.setSession(session, environment, icon);
91 oldTab.attachToSession(session);
96 return new XDebugSessionTab(session, icon, environment);
100 public RunnerLayoutUi getUi() {
104 private XDebugSessionTab(@NotNull XDebugSessionImpl session,
106 @Nullable ExecutionEnvironment environment) {
107 super(session.getProject(), "Debug", session.getSessionName(), GlobalSearchScope.allScope(session.getProject()));
109 setSession(session, environment, icon);
111 myUi.addContent(createFramesContent(), 0, PlaceInGrid.left, false);
112 addVariablesAndWatches(session);
114 attachToSession(session);
116 DefaultActionGroup focus = new DefaultActionGroup();
117 focus.add(ActionManager.getInstance().getAction(XDebuggerActions.FOCUS_ON_BREAKPOINT));
118 myUi.getOptions().setAdditionalFocusActions(focus);
120 myUi.addListener(new ContentManagerAdapter() {
122 public void selectionChanged(ContentManagerEvent event) {
123 Content content = event.getContent();
124 if (mySession != null && content.isSelected() && getWatchesContentId().equals(ViewImpl.ID.get(content))) {
125 myRebuildWatchesRunnable.run();
128 }, myRunContentDescriptor);
133 private void addVariablesAndWatches(@NotNull XDebugSessionImpl session) {
134 myUi.addContent(createVariablesContent(session), 0, PlaceInGrid.center, false);
135 if (!myWatchesInVariables) {
136 myUi.addContent(createWatchesContent(session), 0, PlaceInGrid.right, false);
140 private void setSession(@NotNull XDebugSessionImpl session, @Nullable ExecutionEnvironment environment, @Nullable Icon icon) {
141 myEnvironment = environment;
143 mySessionData = session.getSessionData();
144 myConsole = session.getConsoleView();
146 AnAction[] restartActions;
147 List<AnAction> restartActionsList = session.getRestartActions();
148 if (ContainerUtil.isEmpty(restartActionsList)) {
149 restartActions = AnAction.EMPTY_ARRAY;
152 restartActions = restartActionsList.toArray(new AnAction[restartActionsList.size()]);
155 myRunContentDescriptor = new RunContentDescriptor(myConsole, session.getDebugProcess().getProcessHandler(),
156 myUi.getComponent(), session.getSessionName(), icon, myRebuildWatchesRunnable, restartActions);
157 Disposer.register(myRunContentDescriptor, this);
158 Disposer.register(myProject, myRunContentDescriptor);
163 public Object getData(@NonNls String dataId) {
164 if (XWatchesView.DATA_KEY.is(dataId)) {
165 return myWatchesView;
167 else if (TAB_KEY.is(dataId)) {
170 else if (XDebugSessionData.DATA_KEY.is(dataId)) {
171 return mySessionData;
174 if (mySession != null) {
175 if (XDebugSession.DATA_KEY.is(dataId)) {
178 else if (LangDataKeys.CONSOLE_VIEW.is(dataId)) {
179 return mySession.getConsoleView();
183 return super.getData(dataId);
186 private Content createVariablesContent(@NotNull XDebugSessionImpl session) {
187 XVariablesView variablesView;
188 if (myWatchesInVariables) {
189 variablesView = myWatchesView = new XWatchesViewImpl(session, myWatchesInVariables);
192 variablesView = new XVariablesView(session);
194 registerView(DebuggerContentInfo.VARIABLES_CONTENT, variablesView);
195 Content result = myUi.createContent(DebuggerContentInfo.VARIABLES_CONTENT, variablesView.getPanel(),
196 XDebuggerBundle.message("debugger.session.tab.variables.title"),
197 AllIcons.Debugger.Value, null);
198 result.setCloseable(false);
200 ActionGroup group = getCustomizedActionGroup(XDebuggerActions.VARIABLES_TREE_TOOLBAR_GROUP);
201 result.setActions(group, ActionPlaces.DEBUGGER_TOOLBAR, variablesView.getTree());
205 private Content createWatchesContent(@NotNull XDebugSessionImpl session) {
206 myWatchesView = new XWatchesViewImpl(session, myWatchesInVariables);
207 registerView(DebuggerContentInfo.WATCHES_CONTENT, myWatchesView);
208 Content watchesContent = myUi.createContent(DebuggerContentInfo.WATCHES_CONTENT, myWatchesView.getPanel(),
209 XDebuggerBundle.message("debugger.session.tab.watches.title"), AllIcons.Debugger.Watches, null);
210 watchesContent.setCloseable(false);
211 return watchesContent;
215 private Content createFramesContent() {
216 XFramesView framesView = new XFramesView(myProject);
217 registerView(DebuggerContentInfo.FRAME_CONTENT, framesView);
218 Content framesContent = myUi.createContent(DebuggerContentInfo.FRAME_CONTENT, framesView.getMainPanel(),
219 XDebuggerBundle.message("debugger.session.tab.frames.title"), AllIcons.Debugger.Frame, null);
220 framesContent.setCloseable(false);
221 return framesContent;
224 public void rebuildViews() {
225 AppUIUtil.invokeLaterIfProjectAlive(myProject, () -> {
226 for (XDebugView view : myViews.values()) {
227 view.processSessionEvent(XDebugView.SessionEvent.SETTINGS_CHANGED);
232 public XWatchesView getWatchesView() {
233 return myWatchesView;
236 private void attachToSession(@NotNull XDebugSessionImpl session) {
237 for (XDebugView view : myViews.values()) {
238 attachViewToSession(session, view);
241 XDebugTabLayouter layouter = session.getDebugProcess().createTabLayouter();
242 Content consoleContent = layouter.registerConsoleContent(myUi, myConsole);
243 attachNotificationTo(consoleContent);
245 layouter.registerAdditionalContent(myUi);
246 RunContentBuilder.addAdditionalConsoleEditorActions(myConsole, consoleContent);
248 if (ApplicationManager.getApplication().isUnitTestMode()) {
252 DefaultActionGroup leftToolbar = new DefaultActionGroup();
253 final Executor debugExecutor = DefaultDebugExecutor.getDebugExecutorInstance();
254 if (myEnvironment != null) {
255 leftToolbar.add(ActionManager.getInstance().getAction(IdeActions.ACTION_RERUN));
256 List<AnAction> additionalRestartActions = session.getRestartActions();
257 if (!additionalRestartActions.isEmpty()) {
258 leftToolbar.addAll(additionalRestartActions);
259 leftToolbar.addSeparator();
261 leftToolbar.addAll(session.getExtraActions());
263 leftToolbar.addAll(getCustomizedActionGroup(XDebuggerActions.TOOL_WINDOW_LEFT_TOOLBAR_GROUP));
265 for (AnAction action : session.getExtraStopActions()) {
266 leftToolbar.add(action, new Constraints(Anchor.AFTER, IdeActions.ACTION_STOP_PROGRAM));
269 //group.addSeparator();
270 //addAction(group, DebuggerActions.EXPORT_THREADS);
271 leftToolbar.addSeparator();
273 leftToolbar.add(myUi.getOptions().getLayoutActions());
274 final AnAction[] commonSettings = myUi.getOptions().getSettingsActionsList();
275 DefaultActionGroup settings = new DefaultActionGroup(ActionsBundle.message("group.XDebugger.settings.text"), true);
276 settings.getTemplatePresentation().setIcon(myUi.getOptions().getSettingsActions().getTemplatePresentation().getIcon());
277 settings.addAll(commonSettings);
278 leftToolbar.add(settings);
280 leftToolbar.addSeparator();
282 leftToolbar.add(PinToolwindowTabAction.getPinAction());
283 leftToolbar.add(new CloseAction(myEnvironment != null ? myEnvironment.getExecutor() : debugExecutor, myRunContentDescriptor, myProject));
284 leftToolbar.add(new ContextHelpAction(debugExecutor.getHelpId()));
286 DefaultActionGroup topToolbar = new DefaultActionGroup();
287 topToolbar.addAll(getCustomizedActionGroup(XDebuggerActions.TOOL_WINDOW_TOP_TOOLBAR_GROUP));
289 session.getDebugProcess().registerAdditionalActions(leftToolbar, topToolbar, settings);
290 myUi.getOptions().setLeftToolbar(leftToolbar, ActionPlaces.DEBUGGER_TOOLBAR);
291 myUi.getOptions().setTopToolbar(topToolbar, ActionPlaces.DEBUGGER_TOOLBAR);
293 if (myEnvironment != null) {
294 initLogConsoles(myEnvironment.getRunProfile(), myRunContentDescriptor, myConsole);
298 private static void attachViewToSession(@NotNull XDebugSessionImpl session, @Nullable XDebugView view) {
300 session.addSessionListener(new XDebugViewSessionListener(view), view);
304 public void detachFromSession() {
305 assert mySession != null;
310 public RunContentDescriptor getRunContentDescriptor() {
311 return myRunContentDescriptor;
314 public boolean isWatchesInVariables() {
315 return myWatchesInVariables;
318 public void setWatchesInVariables(boolean watchesInVariables) {
319 if (myWatchesInVariables != watchesInVariables) {
320 myWatchesInVariables = watchesInVariables;
321 Registry.get("debugger.watches.in.variables").setValue(watchesInVariables);
322 if (mySession != null) {
323 removeContent(DebuggerContentInfo.VARIABLES_CONTENT);
324 removeContent(DebuggerContentInfo.WATCHES_CONTENT);
325 addVariablesAndWatches(mySession);
326 attachViewToSession(mySession, myViews.get(DebuggerContentInfo.VARIABLES_CONTENT));
327 attachViewToSession(mySession, myViews.get(DebuggerContentInfo.WATCHES_CONTENT));
333 public static void showWatchesView(@NotNull XDebugSessionImpl session) {
334 XDebugSessionTab tab = session.getSessionTab();
336 tab.toFront(false, null);
337 // restore watches tab if minimized
338 JComponent component = tab.getUi().getComponent();
339 if (component instanceof DataProvider) {
340 RunnerContentUi ui = RunnerContentUi.KEY.getData(((DataProvider)component));
342 ui.restoreContent(tab.getWatchesContentId());
349 private String getWatchesContentId() {
350 return myWatchesInVariables ? DebuggerContentInfo.VARIABLES_CONTENT : DebuggerContentInfo.WATCHES_CONTENT;
353 private void registerView(String contentId, @NotNull XDebugView view) {
354 myViews.put(contentId, view);
355 Disposer.register(myRunContentDescriptor, view);
358 private void removeContent(String contentId) {
359 myUi.removeContent(myUi.findContent(contentId), true);
360 XDebugView view = myViews.remove(contentId);
362 Disposer.dispose(view);
366 private static class ToggleSortValuesAction extends SortValuesToggleAction {
367 private final boolean myShowIcon;
369 private ToggleSortValuesAction(boolean showIcon) {
370 copyFrom(ActionManager.getInstance().getAction(XDebuggerActions.TOGGLE_SORT_VALUES));
371 myShowIcon = showIcon;
375 public void update(@NotNull AnActionEvent e) {
378 e.getPresentation().setIcon(null);