2 * Copyright 2000-2015 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.breakpoints.ui;
18 import com.intellij.icons.AllIcons;
19 import com.intellij.ide.DataManager;
20 import com.intellij.ide.util.treeView.TreeState;
21 import com.intellij.openapi.Disposable;
22 import com.intellij.openapi.actionSystem.*;
23 import com.intellij.openapi.project.DumbAware;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.ui.DialogWrapper;
26 import com.intellij.openapi.ui.Messages;
27 import com.intellij.openapi.ui.popup.JBPopupFactory;
28 import com.intellij.openapi.util.Condition;
29 import com.intellij.openapi.util.Disposer;
30 import com.intellij.ui.*;
31 import com.intellij.ui.popup.util.DetailController;
32 import com.intellij.ui.popup.util.DetailViewImpl;
33 import com.intellij.ui.popup.util.ItemWrapper;
34 import com.intellij.ui.popup.util.MasterController;
35 import com.intellij.util.Function;
36 import com.intellij.util.SingleAlarm;
37 import com.intellij.util.containers.ContainerUtil;
38 import com.intellij.util.containers.HashSet;
39 import com.intellij.util.ui.tree.TreeUtil;
40 import com.intellij.xdebugger.XDebuggerManager;
41 import com.intellij.xdebugger.breakpoints.XBreakpoint;
42 import com.intellij.xdebugger.breakpoints.XBreakpointType;
43 import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule;
44 import com.intellij.xdebugger.impl.breakpoints.XBreakpointBase;
45 import com.intellij.xdebugger.impl.breakpoints.XBreakpointManagerImpl;
46 import com.intellij.xdebugger.impl.breakpoints.XBreakpointUtil;
47 import com.intellij.xdebugger.impl.breakpoints.XBreakpointsDialogState;
48 import com.intellij.xdebugger.impl.breakpoints.ui.grouping.XBreakpointCustomGroup;
49 import com.intellij.xdebugger.impl.breakpoints.ui.tree.BreakpointItemNode;
50 import com.intellij.xdebugger.impl.breakpoints.ui.tree.BreakpointItemsTreeController;
51 import com.intellij.xdebugger.impl.breakpoints.ui.tree.BreakpointsCheckboxTree;
52 import com.intellij.xdebugger.impl.breakpoints.ui.tree.BreakpointsGroupNode;
53 import org.jetbrains.annotations.NotNull;
54 import org.jetbrains.annotations.Nullable;
57 import javax.swing.tree.DefaultMutableTreeNode;
58 import javax.swing.tree.TreePath;
61 import java.util.List;
63 public class BreakpointsDialog extends DialogWrapper {
64 @NotNull private Project myProject;
66 private Object myInitialBreakpoint;
67 private List<BreakpointPanelProvider> myBreakpointsPanelProviders;
69 private BreakpointItemsTreeController myTreeController;
71 JLabel temp = new JLabel();
73 private MasterController myMasterController = new MasterController() {
75 public ItemWrapper[] getSelectedItems() {
76 final List<BreakpointItem> res = myTreeController.getSelectedBreakpoints();
77 return res.toArray(new ItemWrapper[res.size()]);
81 public JLabel getPathLabel() {
86 private final DetailController myDetailController = new DetailController(myMasterController);
88 private final Collection<BreakpointItem> myBreakpointItems = new ArrayList<BreakpointItem>();
90 private final SingleAlarm myRebuildAlarm = new SingleAlarm(new Runnable() {
94 myTreeController.rebuildTree(myBreakpointItems);
95 myDetailController.doUpdateDetailView(true);
97 }, 100, myDisposable);
99 private final List<XBreakpointGroupingRule> myRulesAvailable = new ArrayList<XBreakpointGroupingRule>();
101 private final Set<XBreakpointGroupingRule> myRulesEnabled = new TreeSet<XBreakpointGroupingRule>(XBreakpointGroupingRule.PRIORITY_COMPARATOR);
102 private final Disposable myListenerDisposable = Disposer.newDisposable();
103 private final List<ToggleActionButton> myToggleRuleActions = new ArrayList<ToggleActionButton>();
105 private XBreakpointManagerImpl getBreakpointManager() {
106 return (XBreakpointManagerImpl)XDebuggerManager.getInstance(myProject).getBreakpointManager();
109 protected BreakpointsDialog(@NotNull Project project, Object breakpoint, @NotNull List<BreakpointPanelProvider> providers) {
112 myBreakpointsPanelProviders = providers;
113 myInitialBreakpoint = breakpoint;
115 collectGroupingRules();
119 setTitle("Breakpoints");
122 setOKButtonText("Done");
125 private String getSplitterProportionKey() {
126 return getDimensionServiceKey() + ".splitter";
131 protected JComponent createCenterPanel() {
132 JPanel mainPanel = new JPanel(new BorderLayout());
134 JBSplitter splitPane = new JBSplitter(0.3f);
135 splitPane.setSplitterProportionKey(getSplitterProportionKey());
137 splitPane.setFirstComponent(createMasterView());
138 splitPane.setSecondComponent(createDetailView());
140 mainPanel.add(splitPane, BorderLayout.CENTER);
145 private JComponent createDetailView() {
146 DetailViewImpl detailView = new DetailViewImpl(myProject);
147 myDetailController.setDetailView(detailView);
152 void collectItems() {
153 if (!myBreakpointsPanelProviders.isEmpty()) {
155 myBreakpointItems.clear();
156 for (BreakpointPanelProvider panelProvider : myBreakpointsPanelProviders) {
157 panelProvider.provideBreakpointItems(myProject, myBreakpointItems);
162 void initSelection(Collection<BreakpointItem> breakpoints) {
163 XBreakpointsDialogState settings = (getBreakpointManager()).getBreakpointsDialogSettings();
164 if (settings != null && settings.getTreeState() != null) {
165 settings.getTreeState().applyTo(myTreeController.getTreeView());
168 TreeUtil.expandAll(myTreeController.getTreeView());
170 selectBreakpoint(myInitialBreakpoint);
175 protected String getDimensionServiceKey() {
176 return getClass().getName();
181 protected Action[] createActions() {
182 return new Action[]{getOKAction(), getHelpAction()};
185 private class ToggleBreakpointGroupingRuleEnabledAction extends ToggleActionButton {
186 private XBreakpointGroupingRule myRule;
188 public ToggleBreakpointGroupingRuleEnabledAction(XBreakpointGroupingRule rule) {
189 super(rule.getPresentableName(), rule.getIcon());
191 getTemplatePresentation().setText(rule.getPresentableName());
195 public boolean isSelected(AnActionEvent e) {
196 return myRulesEnabled.contains(myRule);
200 public void setSelected(AnActionEvent e, boolean state) {
202 myRulesEnabled.add(myRule);
205 myRulesEnabled.remove(myRule);
207 myTreeController.setGroupingRules(myRulesEnabled);
211 private JComponent createMasterView() {
212 myTreeController = new BreakpointItemsTreeController(myRulesEnabled) {
214 public void nodeStateWillChangeImpl(CheckedTreeNode node) {
215 if (node instanceof BreakpointItemNode) {
216 ((BreakpointItemNode)node).getBreakpointItem().saveState();
218 super.nodeStateWillChangeImpl(node);
222 public void nodeStateDidChangeImpl(CheckedTreeNode node) {
223 super.nodeStateDidChangeImpl(node);
224 if (node instanceof BreakpointItemNode) {
225 myDetailController.doUpdateDetailView(true);
230 protected void selectionChangedImpl() {
231 super.selectionChangedImpl();
233 myDetailController.updateDetailView();
236 final JTree tree = new BreakpointsCheckboxTree(myProject, myTreeController) {
238 protected void onDoubleClick(CheckedTreeNode node) {
239 if (node instanceof BreakpointsGroupNode) {
240 TreePath path = TreeUtil.getPathFromRoot(node);
241 if (isExpanded(path)) {
254 PopupHandler.installPopupHandler(tree, new ActionGroup() {
257 public AnAction[] getChildren(@Nullable AnActionEvent e) {
258 ActionGroup group = new ActionGroup("Move to group", true) {
261 public AnAction[] getChildren(@Nullable AnActionEvent e) {
262 Set<String> groups = getBreakpointManager().getAllGroups();
263 AnAction[] res = new AnAction[groups.size()+3];
265 res[i++] = new MoveToGroupAction(null);
266 for (String group : groups) {
267 res[i++] = new MoveToGroupAction(group);
269 res[i++] = new Separator();
270 res[i] = new MoveToGroupAction();
274 List<AnAction> res = new ArrayList<AnAction>();
276 Object component = tree.getLastSelectedPathComponent();
277 if (tree.getSelectionCount() == 1 && component instanceof BreakpointsGroupNode &&
278 ((BreakpointsGroupNode)component).getGroup() instanceof XBreakpointCustomGroup) {
279 res.add(new SetAsDefaultGroupAction((XBreakpointCustomGroup)((BreakpointsGroupNode)component).getGroup()));
281 if (tree.getSelectionCount() == 1 && component instanceof BreakpointItemNode) {
282 res.add(new EditDescriptionAction((XBreakpointBase)((BreakpointItemNode)component).getBreakpointItem().getBreakpoint()));
284 return res.toArray(new AnAction[res.size()]);
286 }, ActionPlaces.UNKNOWN, ActionManager.getInstance());
288 new AnAction("BreakpointDialog.GoToSource") {
290 public void actionPerformed(AnActionEvent e) {
294 }.registerCustomShortcutSet(CommonShortcuts.ENTER, tree);
296 new AnAction("BreakpointDialog.ShowSource") {
298 public void actionPerformed(AnActionEvent e) {
302 }.registerCustomShortcutSet(ActionManager.getInstance().getAction(IdeActions.ACTION_EDIT_SOURCE).getShortcutSet(), tree);
304 final DefaultActionGroup breakpointTypes = new DefaultActionGroup();
305 for (XBreakpointType<?, ?> type : XBreakpointUtil.getBreakpointTypes()) {
306 if (type.isAddBreakpointButtonVisible()) {
307 breakpointTypes.addAll(new AddXBreakpointAction(type));
311 ToolbarDecorator decorator = ToolbarDecorator.createDecorator(tree).
312 setAddAction(new AnActionButtonRunnable() {
314 public void run(AnActionButton button) {
315 JBPopupFactory.getInstance()
316 .createActionGroupPopup(null, breakpointTypes, DataManager.getInstance().getDataContext(button.getContextComponent()),
317 JBPopupFactory.ActionSelectionAid.NUMBERING, false)
318 .show(button.getPreferredPopupPoint());
321 setRemoveAction(new AnActionButtonRunnable() {
323 public void run(AnActionButton button) {
324 myTreeController.removeSelectedBreakpoints(myProject);
327 setRemoveActionUpdater(new AnActionButtonUpdater() {
329 public boolean isEnabled(AnActionEvent e) {
330 boolean enabled = false;
331 final ItemWrapper[] items = myMasterController.getSelectedItems();
332 for (ItemWrapper item : items) {
333 if (item.allowedToRemove()) {
340 setToolbarPosition(ActionToolbarPosition.TOP).
341 setToolbarBorder(IdeBorderFactory.createEmptyBorder());
343 tree.setBorder(IdeBorderFactory.createBorder());
345 for (ToggleActionButton action : myToggleRuleActions) {
346 decorator.addExtraAction(action);
349 JPanel decoratedTree = decorator.createPanel();
350 decoratedTree.setBorder(IdeBorderFactory.createEmptyBorder());
352 myTreeController.setTreeView(tree);
354 myTreeController.buildTree(myBreakpointItems);
356 initSelection(myBreakpointItems);
358 final BreakpointPanelProvider.BreakpointsListener listener = new BreakpointPanelProvider.BreakpointsListener() {
360 public void breakpointsChanged() {
361 myRebuildAlarm.cancelAndRequest();
365 for (BreakpointPanelProvider provider : myBreakpointsPanelProviders) {
366 provider.addListener(listener, myProject, myListenerDisposable);
369 return decoratedTree;
372 private void navigate(final boolean requestFocus) {
373 List<BreakpointItem> breakpoints = myTreeController.getSelectedBreakpoints();
374 if (!breakpoints.isEmpty()) {
375 breakpoints.get(0).navigate(requestFocus);
381 public JComponent getPreferredFocusedComponent() {
382 return myTreeController.getTreeView();
385 private void collectGroupingRules() {
386 for (BreakpointPanelProvider provider : myBreakpointsPanelProviders) {
387 provider.createBreakpointsGroupingRules(myRulesAvailable);
389 Collections.sort(myRulesAvailable, XBreakpointGroupingRule.PRIORITY_COMPARATOR);
391 myRulesEnabled.clear();
392 XBreakpointsDialogState settings = (getBreakpointManager()).getBreakpointsDialogSettings();
394 for (XBreakpointGroupingRule rule : myRulesAvailable) {
395 if (rule.isAlwaysEnabled() || (settings != null && settings.getSelectedGroupingRules().contains(rule.getId()) ) ) {
396 myRulesEnabled.add(rule);
400 for (XBreakpointGroupingRule rule : myRulesAvailable) {
401 if (!rule.isAlwaysEnabled()) {
402 myToggleRuleActions.add(new ToggleBreakpointGroupingRuleEnabledAction(rule));
407 private void saveBreakpointsDialogState() {
408 final XBreakpointsDialogState dialogState = new XBreakpointsDialogState();
409 saveTreeState(dialogState);
410 final List<XBreakpointGroupingRule> rulesEnabled = ContainerUtil.filter(myRulesEnabled, new Condition<XBreakpointGroupingRule>() {
412 public boolean value(XBreakpointGroupingRule rule) {
413 return !rule.isAlwaysEnabled();
417 dialogState.setSelectedGroupingRules(new HashSet<String>(ContainerUtil.map(rulesEnabled, new Function<XBreakpointGroupingRule, String>() {
419 public String fun(XBreakpointGroupingRule rule) {
423 getBreakpointManager().setBreakpointsDialogSettings(dialogState);
426 private void saveTreeState(XBreakpointsDialogState state) {
427 JTree tree = myTreeController.getTreeView();
428 state.setTreeState(TreeState.createOn(tree, (DefaultMutableTreeNode)tree.getModel().getRoot()));
432 protected void dispose() {
434 Disposer.dispose(myListenerDisposable);
435 saveBreakpointsDialogState();
440 private void disposeItems() {
441 for (BreakpointItem item : myBreakpointItems) {
448 protected String getHelpId() {
449 return "reference.dialogs.breakpoints";
452 private void saveCurrentItem() {
453 ItemWrapper item = myDetailController.getSelectedItem();
454 if (item instanceof BreakpointItem) {
455 ((BreakpointItem)item).saveState();
459 private class AddXBreakpointAction extends AnAction implements DumbAware {
460 private final XBreakpointType<?, ?> myType;
462 public AddXBreakpointAction(XBreakpointType<?, ?> type) {
464 getTemplatePresentation().setIcon(type.getEnabledIcon());
465 getTemplatePresentation().setText(type.getTitle());
469 public void actionPerformed(AnActionEvent e) {
471 XBreakpoint<?> breakpoint = myType.addBreakpoint(myProject, null);
472 if (breakpoint != null) {
473 selectBreakpoint(breakpoint);
478 private boolean selectBreakpoint(Object breakpoint) {
479 if (breakpoint != null) {
480 for (BreakpointItem item : myBreakpointItems) {
481 if (item.getBreakpoint() == breakpoint) {
482 myTreeController.selectBreakpointItem(item, null);
490 private class MoveToGroupAction extends AnAction {
491 private final String myGroup;
492 private final boolean myNewGroup;
494 private MoveToGroupAction(String group) {
495 super(group == null ? "<no group>" : group);
500 private MoveToGroupAction() {
501 super("Create new...");
507 public void actionPerformed(AnActionEvent e) {
508 String groupName = myGroup;
510 groupName = Messages.showInputDialog("New group name", "New Group", AllIcons.Nodes.NewFolder);
511 if (groupName == null) {
515 for (BreakpointItem item : myTreeController.getSelectedBreakpoints()) {
516 Object breakpoint = item.getBreakpoint();
517 if (breakpoint instanceof XBreakpointBase) {
518 ((XBreakpointBase)breakpoint).setGroup(groupName);
521 myTreeController.rebuildTree(myBreakpointItems);
525 private class SetAsDefaultGroupAction extends AnAction {
526 private final String myName;
528 private SetAsDefaultGroupAction(XBreakpointCustomGroup group) {
529 super(group.isDefault() ? "Unset as default" : "Set as default");
530 myName = group.isDefault() ? null : group.getName();
534 public void actionPerformed(AnActionEvent e) {
535 getBreakpointManager().setDefaultGroup(myName);
536 myTreeController.rebuildTree(myBreakpointItems);
540 private class EditDescriptionAction extends AnAction {
541 private final XBreakpointBase myBreakpoint;
543 private EditDescriptionAction(XBreakpointBase breakpoint) {
544 super("Edit description");
545 myBreakpoint = breakpoint;
549 public void actionPerformed(AnActionEvent e) {
550 String description = Messages.showInputDialog("", "Edit Description", null, myBreakpoint.getUserDescription(), null);
551 if (description == null) {
554 myBreakpoint.setUserDescription(description);
555 myTreeController.rebuildTree(myBreakpointItems);