From 822f63e0e574b9485ffa3df0f4e6833170b69d4e Mon Sep 17 00:00:00 2001 From: "andrey.zaytsev" Date: Wed, 23 May 2012 21:15:40 +0400 Subject: [PATCH] breakpoints ui: list replaced with tree. --- .../debugger/ui/JavaDebuggerSupport.java | 14 +- .../debugger/ui/XBreakpointCategoryGroup.java | 59 +++ .../ui/XBreakpointGroupingByCategoryRule.java | 53 +++ .../ui/breakpoints/JavaBreakpointItem.java | 11 +- .../intellij/ui/popup/util/ItemWrapper.java | 3 + .../intellij/ui/popup/util/SplitterItem.java | 6 + .../intellij/ide/bookmarks/BookmarkItem.java | 7 + .../bookmarks/actions/BookmarksAction.java | 5 + .../ui/popup/util/DetailViewImpl.java | 22 +- .../popup/util/MasterDetailPopupBuilder.java | 355 ++++++++++-------- .../openapi/ui/popup/PopupChooserBuilder.java | 7 - .../breakpoints/ui/BreakpointItem.java | 25 +- .../ui/XBreakpointGroupingByTypeRule.java | 49 +++ .../ui/XBreakpointGroupingRule.java | 3 +- .../breakpoints/ui/XBreakpointTypeGroup.java | 52 +++ .../impl/breakpoints/XBreakpointItem.java | 20 +- .../breakpoints/XBreakpointPanelProvider.java | 23 +- .../breakpoints/ui/BreakpointItemsTree.java | 271 +++++++++++++ .../ui/BreakpointPanelProvider.java | 7 +- .../BreakpointsMasterDetailPopupFactory.java | 204 +++++----- .../grouping/XBreakpointFileGroupingRule.java | 8 +- 21 files changed, 878 insertions(+), 326 deletions(-) create mode 100644 java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointCategoryGroup.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointGroupingByCategoryRule.java create mode 100644 platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/XBreakpointGroupingByTypeRule.java create mode 100644 platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/XBreakpointTypeGroup.java create mode 100644 platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/BreakpointItemsTree.java diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java b/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java index 33cf0e16f211..675ba7211a7a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java @@ -33,9 +33,9 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Key; -import com.intellij.ui.popup.util.ItemWrapper; -import com.intellij.ui.popup.util.SplitterItem; import com.intellij.xdebugger.AbstractDebuggerSession; +import com.intellij.xdebugger.breakpoints.ui.BreakpointItem; +import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule; import com.intellij.xdebugger.impl.DebuggerSupport; import com.intellij.xdebugger.impl.actions.DebuggerActionHandler; import com.intellij.xdebugger.impl.actions.DebuggerToggleActionHandler; @@ -213,6 +213,11 @@ public class JavaDebuggerSupport extends DebuggerSupport { return result.toArray(new AnAction[result.size()]); } + @Override + public void provideBreakpointsGroupingRules(Collection rules) { + rules.add(new XBreakpointGroupingByCategoryRule()); + } + @Override public void addListener(final BreakpointsListener listener, Project project) { final MyBreakpointManagerListener listener1 = new MyBreakpointManagerListener(listener); @@ -254,13 +259,10 @@ public class JavaDebuggerSupport extends DebuggerSupport { } @Override - public void provideBreakpointItems(Project project, Collection items) { + public void provideBreakpointItems(Project project, Collection items) { for (BreakpointFactory breakpointFactory : BreakpointFactory.getBreakpointFactories()) { Key category = breakpointFactory.getBreakpointCategory(); Breakpoint[] breakpoints = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager().getBreakpoints(category); - if (breakpoints.length > 0) { - items.add(new SplitterItem(breakpointFactory.getDisplayName())); - } for (Breakpoint breakpoint : breakpoints) { items.add(breakpointFactory.createBreakpointItem(breakpoint)); } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointCategoryGroup.java b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointCategoryGroup.java new file mode 100644 index 000000000000..bfb59e0a412b --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointCategoryGroup.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2012 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.ui.breakpoints.Breakpoint; +import com.intellij.debugger.ui.breakpoints.BreakpointFactory; +import com.intellij.openapi.util.Key; +import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroup; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +/** + * Created with IntelliJ IDEA. + * User: zajac + * Date: 23.05.12 + * Time: 16:22 + * To change this template use File | Settings | File Templates. + */ +public class XBreakpointCategoryGroup extends XBreakpointGroup { + private Key myCategory; + private Icon myIcon; + private final String myName; + + public XBreakpointCategoryGroup(BreakpointFactory factory) { + myCategory = factory.getBreakpointCategory(); + myIcon = factory.getIcon(); + final String name = factory.getDisplayName(); + myName = name != null ? name : "UNKNOWN"; + } + + public Key getCategory() { + return myCategory; + } + + @Override + public Icon getIcon(boolean isOpen) { + return myIcon; + } + + @NotNull + @Override + public String getName() { + return myName; + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointGroupingByCategoryRule.java b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointGroupingByCategoryRule.java new file mode 100644 index 000000000000..477417f54ee7 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/ui/XBreakpointGroupingByCategoryRule.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2012 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.ui.breakpoints.Breakpoint; +import com.intellij.debugger.ui.breakpoints.BreakpointFactory; +import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** +* Created with IntelliJ IDEA. +* User: zajac +* Date: 23.05.12 +* Time: 16:24 +* To change this template use File | Settings | File Templates. +*/ +class XBreakpointGroupingByCategoryRule extends XBreakpointGroupingRule { + XBreakpointGroupingByCategoryRule() { + super("XBreakpointGroupingByCategoryRule", "Type"); + } + + @Override + public XBreakpointCategoryGroup getGroup(@NotNull B b, @NotNull Collection groups) { + if (b instanceof Breakpoint) { + final Breakpoint breakpoint = (Breakpoint)b; + for (XBreakpointCategoryGroup group : groups) { + if (group.getCategory().equals(breakpoint.getCategory())) { + return group; + } + } + final BreakpointFactory factory = BreakpointFactory.getInstance(breakpoint.getCategory()); + if (factory != null) { + return new XBreakpointCategoryGroup(factory); + } + } + return null; + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaBreakpointItem.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaBreakpointItem.java index 74febadb6647..a186926b531b 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaBreakpointItem.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaBreakpointItem.java @@ -18,9 +18,9 @@ package com.intellij.debugger.ui.breakpoints; import com.intellij.debugger.DebuggerManagerEx; import com.intellij.debugger.SourcePosition; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.SimpleColoredComponent; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.popup.util.DetailView; import com.intellij.xdebugger.breakpoints.ui.BreakpointItem; @@ -43,12 +43,11 @@ class JavaBreakpointItem extends BreakpointItem { } @Override - public void setupRenderer(ColoredListCellRenderer renderer, Project project, boolean selected) { - renderer.setIcon(myBreakpoint.getIcon()); - renderer.append(myBreakpoint.getShortName()); + protected void setupGenericRenderer(SimpleColoredComponent renderer) { + //renderer.setIcon(myBreakpoint.getIcon()); + renderer.append(myBreakpoint.getShortName(), isEnabled() ? SimpleTextAttributes.REGULAR_ATTRIBUTES : SimpleTextAttributes.GRAY_ATTRIBUTES); } - @Override public String speedSearchText() { return myBreakpoint.getDisplayName(); diff --git a/platform/lang-api/src/com/intellij/ui/popup/util/ItemWrapper.java b/platform/lang-api/src/com/intellij/ui/popup/util/ItemWrapper.java index 331bcad134c2..a84bac7f454d 100644 --- a/platform/lang-api/src/com/intellij/ui/popup/util/ItemWrapper.java +++ b/platform/lang-api/src/com/intellij/ui/popup/util/ItemWrapper.java @@ -18,6 +18,7 @@ package com.intellij.ui.popup.util; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.ColoredTreeCellRenderer; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -32,6 +33,8 @@ import javax.swing.*; public interface ItemWrapper { void setupRenderer(ColoredListCellRenderer renderer, Project project, boolean selected); + void setupRenderer(ColoredTreeCellRenderer renderer); + void updateAccessoryView(JComponent label); void execute(Project project, JBPopup popup); diff --git a/platform/lang-api/src/com/intellij/ui/popup/util/SplitterItem.java b/platform/lang-api/src/com/intellij/ui/popup/util/SplitterItem.java index 2f9e5f96c660..4c038b103861 100644 --- a/platform/lang-api/src/com/intellij/ui/popup/util/SplitterItem.java +++ b/platform/lang-api/src/com/intellij/ui/popup/util/SplitterItem.java @@ -18,6 +18,7 @@ package com.intellij.ui.popup.util; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.ColoredTreeCellRenderer; import javax.swing.*; @@ -45,6 +46,11 @@ public class SplitterItem implements ItemWrapper { //To change body of implemented methods use File | Settings | File Templates. } + @Override + public void setupRenderer(ColoredTreeCellRenderer renderer) { + //To change body of implemented methods use File | Settings | File Templates. + } + @Override public void updateAccessoryView(JComponent label) { //To change body of implemented methods use File | Settings | File Templates. diff --git a/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkItem.java b/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkItem.java index 41c51522bf43..68151a1dfc01 100644 --- a/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkItem.java +++ b/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkItem.java @@ -28,6 +28,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.ColoredTreeCellRenderer; import com.intellij.ui.FileColorManager; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.popup.util.DetailView; @@ -89,6 +90,11 @@ public class BookmarkItem implements ItemWrapper { } } + @Override + public void setupRenderer(ColoredTreeCellRenderer renderer) { + //bookmarks tree view not supported + } + public void updateAccessoryView(JComponent component) { JLabel label = (JLabel)component; final char mnemonic = myBookmark.getMnemonic(); @@ -107,6 +113,7 @@ public class BookmarkItem implements ItemWrapper { public void execute(Project project, JBPopup popup) { popup.cancel(); myBookmark.navigate(); + popup.cancel(); } public String footerText() { diff --git a/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java b/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java index 99d3c7be6b7e..3bdb5b8f8fe5 100644 --- a/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java +++ b/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java @@ -113,6 +113,11 @@ public class BookmarksAction extends AnAction implements DumbAware, MasterDetail return mnemonicLabel; } + @Override + public Object[] getSelectedItemsInTree() { + return new Object[0]; //To change body of implemented methods use File | Settings | File Templates. + } + private static DefaultListModel buildModel(Project project) { final DefaultListModel model = new DefaultListModel(); diff --git a/platform/lang-impl/src/com/intellij/ui/popup/util/DetailViewImpl.java b/platform/lang-impl/src/com/intellij/ui/popup/util/DetailViewImpl.java index 269c38ca500b..b230f482cad1 100644 --- a/platform/lang-impl/src/com/intellij/ui/popup/util/DetailViewImpl.java +++ b/platform/lang-impl/src/com/intellij/ui/popup/util/DetailViewImpl.java @@ -17,6 +17,7 @@ package com.intellij.ui.popup.util; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; @@ -51,6 +52,12 @@ public class DetailViewImpl extends JPanel implements DetailView { private JLabel myNothingToShowInEditor = new JLabel("Nothing to show"); private RangeHighlighter myHighlighter; + public void setScheme(EditorColorsScheme scheme) { + myScheme = scheme; + } + + private EditorColorsScheme myScheme = EditorColorsManager.getInstance().getGlobalScheme(); + public DetailViewImpl(Project project) { super(new BorderLayout()); myProject = project; @@ -112,10 +119,13 @@ public class DetailViewImpl extends JPanel implements DetailView { clearEditor(); remove(myNothingToShowInEditor); setEditor(EditorFactory.getInstance().createViewer(document, project)); - EditorHighlighter highlighter = EditorHighlighterFactory.getInstance() - .createEditorHighlighter(file, EditorColorsManager.getInstance().getGlobalScheme(), project); - ((EditorEx)getEditor()).setHighlighter(highlighter); + + final EditorColorsScheme scheme = getScheme(); + + EditorHighlighter highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(file, scheme, project); + ((EditorEx)getEditor()).setFile(file); + ((EditorEx)getEditor()).setHighlighter(highlighter); getEditor().getSettings().setAnimatedScrolling(false); getEditor().getSettings().setRefrainFromScrolling(false); @@ -145,6 +155,12 @@ public class DetailViewImpl extends JPanel implements DetailView { } } + public EditorColorsScheme getScheme() { + return myScheme; + } + + + private void clearHightlighting() { if (myHighlighter != null) { getEditor().getMarkupModel().removeHighlighter(myHighlighter); diff --git a/platform/lang-impl/src/com/intellij/ui/popup/util/MasterDetailPopupBuilder.java b/platform/lang-impl/src/com/intellij/ui/popup/util/MasterDetailPopupBuilder.java index 4df48c798aa1..257197ead096 100644 --- a/platform/lang-impl/src/com/intellij/ui/popup/util/MasterDetailPopupBuilder.java +++ b/platform/lang-impl/src/com/intellij/ui/popup/util/MasterDetailPopupBuilder.java @@ -15,14 +15,12 @@ */ package com.intellij.ui.popup.util; -import com.intellij.ide.bookmarks.BookmarkItem; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionToolbar; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.PopupChooserBuilder; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.Gray; @@ -37,6 +35,8 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; @@ -54,10 +54,21 @@ public class MasterDetailPopupBuilder { private static final Color BORDER_COLOR = Gray._135; private Project myProject; private ActionGroup myActions; - private JBList myList; private Delegate myDelegate; private boolean myCloseOnEnter; + public DetailViewImpl getDetailView() { + return myDetailView; + } + + private DetailViewImpl myDetailView; + private JLabel myPathLabel; + private JBPopup myPopup; + private Alarm myUpdateAlarm; + + + private JComponent myChooserComponent; + public ActionToolbar getActionToolbar() { return myActionToolbar; } @@ -69,114 +80,49 @@ public class MasterDetailPopupBuilder { myProject = project; } + private String getTitle2Text(String fullText) { + int labelWidth = myPathLabel.getWidth(); + if (fullText == null || fullText.length() == 0) return " "; + while (myPathLabel.getFontMetrics(myPathLabel.getFont()).stringWidth(fullText) > labelWidth) { + int sep = fullText.indexOf(File.separatorChar, 4); + if (sep < 0) return fullText; + fullText = "..." + fullText.substring(sep); + } + + return fullText; + } + + private void doUpdateDetailView() { + final Object[] values = getSelectedItems(); + ItemWrapper wrapper = null; + if (values != null && values.length == 1) { + wrapper = (ItemWrapper)values[0]; + myPathLabel.setText(getTitle2Text(wrapper.footerText())); + } + else { + myPathLabel.setText(" "); + } + final ItemWrapper wrapper1 = wrapper; + myUpdateAlarm.cancelAllRequests(); + myUpdateAlarm.addRequest(new Runnable() { + public void run() { + myDetailView.updateWithItem(wrapper1); + } + }, 100); + } public JBPopup createMasterDetailPopup() { - final JLabel pathLabel = new JLabel(" "); - pathLabel.setHorizontalAlignment(SwingConstants.RIGHT); - final Font font = pathLabel.getFont(); - pathLabel.setFont(font.deriveFont((float)10)); + setupRenderer(); - final Alarm updateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); - final DetailViewImpl detailView = new DetailViewImpl(myProject); + myPathLabel = new JLabel(" "); + myPathLabel.setHorizontalAlignment(SwingConstants.RIGHT); - myList.setCellRenderer(new ItemRenderer(myDelegate, myProject)); + final Font font = myPathLabel.getFont(); + myPathLabel.setFont(font.deriveFont((float)10)); - final ListSelectionModel selectionModel = myList.getSelectionModel(); - selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - - final boolean directionUp[] = new boolean[1]; - - selectionModel.addListSelectionListener(new ListSelectionListener() { - private String getTitle2Text(String fullText) { - int labelWidth = pathLabel.getWidth(); - if (fullText == null || fullText.length() == 0) return " "; - while (pathLabel.getFontMetrics(pathLabel.getFont()).stringWidth(fullText) > labelWidth) { - int sep = fullText.indexOf(File.separatorChar, 4); - if (sep < 0) return fullText; - fullText = "..." + fullText.substring(sep); - } - - return fullText; - } - - public void valueChanged(final ListSelectionEvent e) { - //noinspection SSBasedInspection - SwingUtilities.invokeLater(new Runnable() { - public void run() { - updatePathLabel(); - } - }); - } - - private void updatePreviewPanel(final ItemWrapper wrapper) { - updateAlarm.cancelAllRequests(); - updateAlarm.addRequest(new Runnable() { - public void run() { - detailView.updateWithItem(wrapper); - } - }, 100); - } - - private void updatePathLabel() { - final Object[] values = myList.getSelectedValues(); - ItemWrapper wrapper = null; - if (values != null && values.length == 1) { - wrapper = (ItemWrapper)values[0]; - if (wrapper instanceof SplitterItem) { - if (!directionUp[0]) { - final int index = myList.getSelectedIndex(); - if (myList.getItemsCount() >= index + 1) { - myList.setSelectedIndex(index + 1); - } - else { - myList.setSelectedIndex(0); - } - } - else { - final int index = myList.getSelectedIndex(); - if (index - 1 > 0) { - myList.setSelectedIndex(index - 1); - } - else { - myList.setSelectedIndex(myList.getItemsCount() - 1); - } - } - } - pathLabel.setText(getTitle2Text(wrapper.footerText())); - } - else { - pathLabel.setText(" "); - } - updatePreviewPanel(wrapper); - } - }); - final Ref popupRef = Ref.create(null); - - Runnable runnable = new Runnable() { - public void run() { - IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(new Runnable() { - public void run() { - Object[] values = myList.getSelectedValues(); - - if (values.length == 1) { - ((ItemWrapper)values[0]).execute(myProject, popupRef.get()); - } - else { - for (Object value : values) { - if (value instanceof BookmarkItem) { - ((ItemWrapper)value).execute(myProject, popupRef.get()); - } - } - } - } - }); - } - }; - - if (myList.getModel().getSize() == 0) { - myList.clearSelection(); - } + myUpdateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); + myDetailView = new DetailViewImpl(myProject); JPanel footerPanel = new JPanel(new BorderLayout()) { @Override @@ -187,8 +133,30 @@ public class MasterDetailPopupBuilder { } }; + + Runnable runnable = new Runnable() { + public void run() { + IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(new Runnable() { + public void run() { + Object[] values = getSelectedItems(); + + if (values.length == 1) { + ((ItemWrapper)values[0]).execute(myProject, myPopup); + } + else { + for (Object value : values) { + if (value instanceof ItemWrapper) { + ((ItemWrapper)value).execute(myProject, myPopup); + } + } + } + } + }); + } + }; + footerPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); - footerPanel.add(pathLabel); + footerPanel.add(myPathLabel); myActionToolbar = ActionManager.getInstance().createActionToolbar("", myActions, true); myActionToolbar.setReservePlaceAutoPopupIcon(false); @@ -196,14 +164,15 @@ public class MasterDetailPopupBuilder { final JComponent toolBar = myActionToolbar.getComponent(); toolBar.setOpaque(false); - final JBPopup popup = new PopupChooserBuilder(myList). + final PopupChooserBuilder builder = createInnerBuilder(); + myPopup = builder. setTitle(myDelegate.getTitle()). setMovable(true). setResizable(true). setAutoselectOnMouseMove(false). setSettingButton(toolBar). setSouthComponent(footerPanel). - setEastComponent(detailView). + setEastComponent(myDetailView). setItemChoosenCallback(runnable). setCloseOnEnter(myCloseOnEnter). setMayBeParent(true). @@ -213,29 +182,46 @@ public class MasterDetailPopupBuilder { return ((ItemWrapper)o).speedSearchText(); } }).createPopup(); - - popupRef.set(popup); - - myList.addKeyListener(new KeyAdapter() { - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_DELETE) { - removeSelectedItems(MasterDetailPopupBuilder.this.myList, MasterDetailPopupBuilder.this.myProject); - } - else if (e.getKeyCode() == KeyEvent.VK_UP) { - directionUp[0] = true; - } else if (e.getKeyCode() == KeyEvent.VK_DOWN) { - directionUp[0] = false; - } - else if (e.getModifiersEx() == 0) { - myDelegate.handleMnemonic(e, myProject, popup); - } - } - }); - return popup; + return myPopup; } - public static boolean allowedToRemoveSelectedItem(JBList list, Project project) { - Object[] values = list.getSelectedValues(); + private void setupRenderer() { + if (myChooserComponent instanceof JList) { + final JList list = (JList)myChooserComponent; + list.setCellRenderer(new ListItemRenderer(myDelegate, myProject)); + } + } + + private PopupChooserBuilder createInnerBuilder() { + if (myChooserComponent instanceof JList) { + return new PopupChooserBuilder((JList)myChooserComponent); + } + else if (myChooserComponent instanceof JTree) { + return new PopupChooserBuilder((JTree)myChooserComponent); + } + return null; + } + + public Object[] getSelectedItems() { + if (myChooserComponent instanceof JList) { + return ((JList)myChooserComponent).getSelectedValues(); + } + else if (myChooserComponent instanceof JTree) { + return myDelegate.getSelectedItemsInTree(); + } + return new Object[0]; + } + + private void updateDetailViewLater() { + //noinspection SSBasedInspection + SwingUtilities.invokeLater(new Runnable() { + public void run() { + doUpdateDetailView(); + } + }); + } + + public static boolean allowedToRemoveItems(Object[] values) { for (Object value : values) { ItemWrapper item = (ItemWrapper)value; if (!item.allowedToRemove()) { @@ -245,33 +231,42 @@ public class MasterDetailPopupBuilder { return values.length > 0; } - public static void removeSelectedItems(JBList list, Project project) { - int index = list.getSelectedIndex(); - if (index == -1 || index >= list.getModel().getSize()) { - return; + public void removeSelectedItems(Project project) { + if (myChooserComponent instanceof JList) { + final JList list = (JList)myChooserComponent; + int index = list.getSelectedIndex(); + if (index == -1 || index >= list.getModel().getSize()) { + return; + } + Object[] values = list.getSelectedValues(); + for (Object value : values) { + ItemWrapper item = (ItemWrapper)value; + + DefaultListModel model = list.getModel() instanceof DefaultListModel + ? (DefaultListModel)list.getModel() + : (DefaultListModel)((FilteringListModel)list.getModel()).getOriginalModel(); + if (item.allowedToRemove()) { + model.removeElement(item); + + if (model.getSize() > 0) { + if (model.getSize() == index) { + list.setSelectedIndex(model.getSize() - 1); + } + else if (model.getSize() > index) { + list.setSelectedIndex(index); + } + } + else { + list.clearSelection(); + } + item.removed(project); + } + } } - Object[] values = list.getSelectedValues(); - for (Object value : values) { - ItemWrapper item = (ItemWrapper)value; - - DefaultListModel model = list.getModel() instanceof DefaultListModel - ? (DefaultListModel)list.getModel() - : (DefaultListModel)((FilteringListModel)list.getModel()).getOriginalModel(); - if (item.allowedToRemove()) { - model.removeElement(item); - - if (model.getSize() > 0) { - if (model.getSize() == index) { - list.setSelectedIndex(model.getSize() - 1); - } - else if (model.getSize() > index) { - list.setSelectedIndex(index); - } - } - else { - list.clearSelection(); - } - item.removed(project); + else { + final Object[] items = getSelectedItems(); + for (Object item : items) { + ((ItemWrapper)item).removed(project); } } } @@ -281,11 +276,51 @@ public class MasterDetailPopupBuilder { return this; } - public MasterDetailPopupBuilder setList(JBList list) { - myList = list; + public MasterDetailPopupBuilder setTree(final JTree tree) { + setChooser(tree); + + tree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { + @Override + public void valueChanged(TreeSelectionEvent event) { + updateDetailViewLater(); + } + }); + return this; } + public MasterDetailPopupBuilder setList(final JBList list) { + setChooser(list); + final ListSelectionModel listSelectionModel = list.getSelectionModel(); + listSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + listSelectionModel.addListSelectionListener(new ListSelectionListener() { + public void valueChanged(final ListSelectionEvent e) { + updateDetailViewLater(); + } + }); + + + if (list.getModel().getSize() == 0) { + list.clearSelection(); + } + return this; + } + + private void setChooser(JComponent list) { + myChooserComponent = list; + list.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_DELETE) { + removeSelectedItems(myProject); + } + else if (e.getModifiersEx() == 0) { + myDelegate.handleMnemonic(e, myProject, myPopup); + } + } + }); + } + public MasterDetailPopupBuilder setDelegate(Delegate delegate) { myDelegate = delegate; return this; @@ -303,14 +338,16 @@ public class MasterDetailPopupBuilder { @Nullable JComponent createAccessoryView(Project project); + + Object[] getSelectedItemsInTree(); } - public static class ItemRenderer extends JPanel implements ListCellRenderer { + public static class ListItemRenderer extends JPanel implements ListCellRenderer { private final Project myProject; private final ColoredListCellRenderer myRenderer; private Delegate myDelegate; - private ItemRenderer(Delegate delegate, Project project) { + private ListItemRenderer(Delegate delegate, Project project) { super(new BorderLayout()); myProject = project; setBackground(UIUtil.getListBackground()); diff --git a/platform/platform-api/src/com/intellij/openapi/ui/popup/PopupChooserBuilder.java b/platform/platform-api/src/com/intellij/openapi/ui/popup/PopupChooserBuilder.java index 3345439bed79..1d7ddf517976 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/popup/PopupChooserBuilder.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/popup/PopupChooserBuilder.java @@ -202,16 +202,9 @@ public class PopupChooserBuilder { } - final JList finalList = list; (list != null ? list : myChooserComponent).addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { - - final Point onScreen = e.getLocationOnScreen(); - SwingUtilities.convertPointFromScreen(onScreen, finalList); - - final int index = finalList.locationToIndex(onScreen); - if (finalList.getSelectedIndex() != index) return; if (UIUtil.isActionClick(e, MouseEvent.MOUSE_RELEASED) && !UIUtil.isSelectionButtonDown(e) && !e.isConsumed()) { if (myCloseOnEnter) { closePopup(true, e, true); diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/BreakpointItem.java b/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/BreakpointItem.java index 769366f94787..3dbc5b0541a4 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/BreakpointItem.java +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/BreakpointItem.java @@ -21,12 +21,15 @@ import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.ColorUtil; +import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.ColoredTreeCellRenderer; +import com.intellij.ui.SimpleColoredComponent; import com.intellij.ui.popup.util.DetailView; import com.intellij.ui.popup.util.ItemWrapper; import com.intellij.xdebugger.ui.DebuggerColors; import javax.swing.*; -import java.awt.*; /** * Created with IntelliJ IDEA. @@ -43,16 +46,11 @@ public abstract class BreakpointItem implements ItemWrapper { public abstract void setEnabled(boolean state); protected void showInEditor(DetailView panel, VirtualFile virtualFile, int line) { - final TextAttributes attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes( - DebuggerColors.BREAKPOINT_ATTRIBUTES).clone(); - final Color color = attributes.getBackgroundColor(); - attributes.setBackgroundColor(new Color(color.getRed(), color.getGreen()-100, color.getBlue()-100)); - panel.navigateInPreviewEditor(virtualFile, new LogicalPosition(line, 0), attributes); + panel.navigateInPreviewEditor(virtualFile, new LogicalPosition(line, 0), null); } @Override public void execute(Project project, JBPopup popup) { - setEnabled(!isEnabled()); } @Override @@ -60,4 +58,17 @@ public abstract class BreakpointItem implements ItemWrapper { final JCheckBox checkBox = (JCheckBox)component; checkBox.setSelected(isEnabled()); } + + @Override + public void setupRenderer(ColoredListCellRenderer renderer, Project project, boolean selected) { + setupGenericRenderer(renderer); + } + + @Override + public void setupRenderer(ColoredTreeCellRenderer renderer) { + setupGenericRenderer(renderer); + } + + protected abstract void setupGenericRenderer(SimpleColoredComponent renderer); + } diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/XBreakpointGroupingByTypeRule.java b/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/XBreakpointGroupingByTypeRule.java new file mode 100644 index 000000000000..ec64306ea25a --- /dev/null +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/XBreakpointGroupingByTypeRule.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2012 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.breakpoints.ui; + +import com.intellij.xdebugger.breakpoints.XBreakpoint; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * Created with IntelliJ IDEA. + * User: zajac + * Date: 23.05.12 + * Time: 15:57 + * To change this template use File | Settings | File Templates. + */ +public class XBreakpointGroupingByTypeRule extends XBreakpointGroupingRule { + + public XBreakpointGroupingByTypeRule() { + super("XBreakpointGroupingByTypeRule", "Type"); + } + + @Override + public XBreakpointTypeGroup getGroup(@NotNull B b, @NotNull Collection groups) { + if (b instanceof XBreakpoint) { + final XBreakpoint breakpoint = (XBreakpoint)b; + for (XBreakpointTypeGroup group : groups) { + if (group.getBreakpointType() == breakpoint.getType()) { + return group; + } + } + return new XBreakpointTypeGroup(breakpoint.getType()); + } + return null; + } +} diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/XBreakpointGroupingRule.java b/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/XBreakpointGroupingRule.java index 9732460e44ab..b152b869d3e7 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/XBreakpointGroupingRule.java +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/XBreakpointGroupingRule.java @@ -15,7 +15,6 @@ */ package com.intellij.xdebugger.breakpoints.ui; -import com.intellij.xdebugger.breakpoints.XBreakpoint; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -26,7 +25,7 @@ import java.util.Collection; /** * @author nik */ -public abstract class XBreakpointGroupingRule, G extends XBreakpointGroup> { +public abstract class XBreakpointGroupingRule { private final String myId; private final String myPresentableName; diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/XBreakpointTypeGroup.java b/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/XBreakpointTypeGroup.java new file mode 100644 index 000000000000..aa4879ef1781 --- /dev/null +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/ui/XBreakpointTypeGroup.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2012 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.breakpoints.ui; + +import com.intellij.xdebugger.breakpoints.XBreakpointType; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +/** + * Created with IntelliJ IDEA. + * User: zajac + * Date: 23.05.12 + * Time: 15:55 + * To change this template use File | Settings | File Templates. + */ +public class XBreakpointTypeGroup extends XBreakpointGroup { + + private XBreakpointType myBreakpointType; + + public XBreakpointTypeGroup(XBreakpointType type) { + myBreakpointType = type; + } + + @NotNull + @Override + public String getName() { + return myBreakpointType.getTitle(); + } + + public XBreakpointType getBreakpointType() { + return myBreakpointType; + } + + @Override + public Icon getIcon(boolean isOpen) { + return myBreakpointType.getEnabledIcon(); + } +} diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointItem.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointItem.java index 96891234d401..3df95722ff41 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointItem.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointItem.java @@ -18,8 +18,10 @@ package com.intellij.xdebugger.impl.breakpoints; import com.intellij.openapi.application.Result; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.ColoredTreeCellRenderer; +import com.intellij.ui.SimpleColoredComponent; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.popup.util.DetailView; import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.breakpoints.XBreakpoint; @@ -44,15 +46,25 @@ class XBreakpointItem extends BreakpointItem { @Override public void setupRenderer(ColoredListCellRenderer renderer, Project project, boolean selected) { - renderer.setIcon(getIcon()); - renderer.append(XBreakpointUtil.getShortText(myBreakpoint)); + setupGenericRenderer(renderer); + } + + @Override + public void setupRenderer(ColoredTreeCellRenderer renderer) { + setupGenericRenderer(renderer); + } + + protected void setupGenericRenderer(SimpleColoredComponent renderer) { + //renderer.setIcon(getIcon()); + final SimpleTextAttributes attributes = + myBreakpoint.isEnabled() ? SimpleTextAttributes.SIMPLE_CELL_ATTRIBUTES : SimpleTextAttributes.GRAYED_ATTRIBUTES; + renderer.append(XBreakpointUtil.getShortText(myBreakpoint), attributes); } private Icon getIcon() { return ((XBreakpointBase)myBreakpoint).getIcon(); } - @Override public String speedSearchText() { return ((XBreakpointBase)myBreakpoint).getType().getDisplayText(myBreakpoint); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointPanelProvider.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointPanelProvider.java index eaabb8bc8e7d..5529ba0af3d4 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointPanelProvider.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointPanelProvider.java @@ -17,26 +17,28 @@ package com.intellij.xdebugger.impl.breakpoints; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.markup.GutterIconRenderer; import com.intellij.openapi.editor.markup.RangeHighlighter; +import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.popup.util.ItemWrapper; -import com.intellij.ui.popup.util.SplitterItem; -import com.intellij.xdebugger.breakpoints.*; import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.XDebuggerUtil; +import com.intellij.xdebugger.breakpoints.*; +import com.intellij.xdebugger.breakpoints.ui.BreakpointItem; +import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingByTypeRule; +import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule; import com.intellij.xdebugger.impl.breakpoints.ui.AbstractBreakpointPanel; import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointPanelProvider; import com.intellij.xdebugger.impl.breakpoints.ui.XBreakpointsPanel; +import com.intellij.xdebugger.impl.breakpoints.ui.grouping.XBreakpointFileGroupingRule; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collection; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -47,6 +49,12 @@ public class XBreakpointPanelProvider extends BreakpointPanelProvider myListeners = new CopyOnWriteArrayList(); + @Override + public void provideBreakpointsGroupingRules(Collection rules) { + rules.add(new XBreakpointGroupingByTypeRule()); + rules.add(new XBreakpointFileGroupingRule()); + } + @Override public void addListener(BreakpointsListener listener, Project project) { final MyXBreakpointListener listener1 = new MyXBreakpointListener(listener); @@ -129,13 +137,12 @@ public class XBreakpointPanelProvider extends BreakpointPanelProvider items) { + public void provideBreakpointItems(Project project, Collection items) { final XBreakpointType[] types = XBreakpointUtil.getBreakpointTypes(); final XBreakpointManager manager = XDebuggerManager.getInstance(project).getBreakpointManager(); for (XBreakpointType type : types) { final Collection> breakpoints = manager.getBreakpoints(type); if (breakpoints.isEmpty()) continue; - items.add(new SplitterItem(type.getTitle())); for (XBreakpoint breakpoint : breakpoints) { items.add(new XBreakpointItem(breakpoint)); } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/BreakpointItemsTree.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/BreakpointItemsTree.java new file mode 100644 index 000000000000..7392d9e24881 --- /dev/null +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/BreakpointItemsTree.java @@ -0,0 +1,271 @@ +/* + * Copyright 2000-2012 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.breakpoints.ui; + +import com.intellij.ide.util.treeView.TreeState; +import com.intellij.openapi.util.MultiValuesMap; +import com.intellij.ui.CheckboxTree; +import com.intellij.ui.CheckedTreeNode; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.util.ui.tree.TreeUtil; +import com.intellij.xdebugger.breakpoints.XBreakpoint; +import com.intellij.xdebugger.breakpoints.XBreakpointManager; +import com.intellij.xdebugger.breakpoints.XBreakpointType; +import com.intellij.xdebugger.breakpoints.ui.BreakpointItem; +import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroup; +import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import java.util.*; + +/** + * @author nik, zajac + */ +public class BreakpointItemsTree extends CheckboxTree { + //private final TreeNodeComparator myComparator; + private final CheckedTreeNode myRoot; + private final Map myNodes = new HashMap(); + private List myGroupingRules; + private final Map myGroupNodes = new HashMap(); + + private BreakpointItemsTreeDelegate myDelegate; + + private final MultiValuesMap myGroups = new MultiValuesMap(); + + private BreakpointItemsTree(final CheckedTreeNode root, + Collection groupingRules) { + super(new BreakpointsTreeCellRenderer(), root); + myRoot = root; + //myComparator = new TreeNodeComparator(type, breakpointManager); + setGroupingRulesInternal(groupingRules); + + getEmptyText().setText("No Breakpoints"); + } + + public void setDelegate(BreakpointItemsTreeDelegate delegate) { + myDelegate = delegate; + } + + private void setGroupingRulesInternal(final Collection groupingRules) { + myGroupingRules = new ArrayList(groupingRules); + setShowsRootHandles(!groupingRules.isEmpty()); + } + + public static BreakpointItemsTree createTree(final Collection groupingRules) { + return new BreakpointItemsTree(new CheckedTreeNode("root"), groupingRules); + } + + public void buildTree(@NotNull Collection breakpoints) { + final TreeState state = TreeState.createOn(this, myRoot); + myRoot.removeAllChildren(); + myNodes.clear(); + myGroupNodes.clear(); + myGroups.clear(); + for (BreakpointItem breakpoint : breakpoints) { + BreakpointItemNode node = new BreakpointItemNode(breakpoint); + CheckedTreeNode parent = getParentNode(breakpoint); + parent.add(node); + myNodes.put(breakpoint, node); + } + //TreeUtil.sort(myRoot, myComparator); + ((DefaultTreeModel)getModel()).nodeStructureChanged(myRoot); + expandPath(new TreePath(myRoot)); + state.applyTo(this, myRoot); + } + + + @NotNull + private CheckedTreeNode getParentNode(final BreakpointItem breakpoint) { + CheckedTreeNode parent = myRoot; + for (int i = 0; i < myGroupingRules.size(); i++) { + XBreakpointGroup group = getGroup(breakpoint.getBreakpoint(), myGroupingRules.get(i)); + if (group != null) { + parent = getOrCreateGroupNode(parent, group, i); + } + } + return parent; + } + + private BreakpointsGroupNode getOrCreateGroupNode(CheckedTreeNode parent, final G group, + final int level) { + //noinspection unchecked + BreakpointsGroupNode groupNode = (BreakpointsGroupNode)myGroupNodes.get(group); + if (groupNode == null) { + groupNode = new BreakpointsGroupNode(group, level); + myGroupNodes.put(group, groupNode); + parent.add(groupNode); + } + return groupNode; + } + + @Override + protected void onDoubleClick(CheckedTreeNode node) { + if (node instanceof BreakpointItemNode) { + myDelegate.execute(((BreakpointItemNode)node).getBreakpointItem()); + } + } + + @Nullable + private XBreakpointGroup getGroup(final Object breakpoint, final XBreakpointGroupingRule groupingRule) { + //noinspection unchecked + Collection groups = myGroups.get(groupingRule); + if (groups == null) { + groups = Collections.emptyList(); + } + XBreakpointGroup group = groupingRule.getGroup(breakpoint, groups); + if (group != null) { + myGroups.put(groupingRule, group); + } + return group; + } + + @Override + protected void onNodeStateChanged(final CheckedTreeNode node) { + if (node instanceof BreakpointItemNode) { + ((BreakpointItemNode)node).getBreakpointItem().setEnabled(node.isChecked()); + } + } + + public void setGroupingRules(List groupingRules) { + List selectedBreakpoints = getSelectedBreakpoints(); + List allBreakpoints = new ArrayList(myNodes.keySet()); + + setGroupingRulesInternal(groupingRules); + buildTree(allBreakpoints); + + if (selectedBreakpoints.size() > 0) { + selectBreakpointItem(selectedBreakpoints.get(0)); + } + } + + public List getSelectedBreakpoints() { + final ArrayList list = new ArrayList(); + TreePath[] selectionPaths = getSelectionPaths(); + if (selectionPaths == null || selectionPaths.length == 0) return list; + + for (TreePath selectionPath : selectionPaths) { + TreeUtil.traverseDepth((TreeNode)selectionPath.getLastPathComponent(), new TreeUtil.Traverse() { + public boolean accept(final Object node) { + if (node instanceof BreakpointItemNode) { + list.add(((BreakpointItemNode)node).getBreakpointItem()); + } + return true; + } + }); + } + + return list; + } + + public void selectBreakpointItem(final BreakpointItem breakpoint) { + BreakpointItemNode node = myNodes.get(breakpoint); + if (node != null) { + TreeUtil.selectNode(this, node); + } + } + + private static class BreakpointsTreeCellRenderer extends CheckboxTreeCellRenderer { + @Override + public void customizeRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { + if (value instanceof BreakpointItemNode) { + BreakpointItemNode node = (BreakpointItemNode)value; + BreakpointItem breakpoint = node.getBreakpointItem(); + breakpoint.setupRenderer(getTextRenderer()); + } + else if (value instanceof BreakpointsGroupNode) { + XBreakpointGroup group = ((BreakpointsGroupNode)value).getGroup(); + getTextRenderer().setIcon(group.getIcon(expanded)); + getTextRenderer().append(group.getName(), SimpleTextAttributes.SIMPLE_CELL_ATTRIBUTES); + } + } + } + + private static class BreakpointsGroupNode extends CheckedTreeNode { + private final G myGroup; + private final int myLevel; + + private BreakpointsGroupNode(G group, int level) { + super(group); + myLevel = level; + setChecked(false); + myGroup = group; + } + + public G getGroup() { + return myGroup; + } + + public int getLevel() { + return myLevel; + } + } + + private static class BreakpointItemNode extends CheckedTreeNode { + private final BreakpointItem myBreakpoint; + + private BreakpointItemNode(final BreakpointItem breakpoint) { + super(breakpoint); + myBreakpoint = breakpoint; + setChecked(breakpoint.isEnabled()); + } + + public BreakpointItem getBreakpointItem() { + return myBreakpoint; + } + } + + private static class TreeNodeComparator> implements Comparator { + private final Comparator myBreakpointComparator; + private final XBreakpointManager myBreakpointManager; + + public TreeNodeComparator(final XBreakpointType type, XBreakpointManager breakpointManager) { + myBreakpointManager = breakpointManager; + myBreakpointComparator = type.getBreakpointComparator(); + } + + public int compare(final TreeNode o1, final TreeNode o2) { + if (o1 instanceof BreakpointItemNode && o2 instanceof BreakpointItemNode) { + //noinspection unchecked + B b1 = (B)((BreakpointItemNode)o1).getBreakpointItem(); + //noinspection unchecked + B b2 = (B)((BreakpointItemNode)o2).getBreakpointItem(); + boolean default1 = myBreakpointManager.isDefaultBreakpoint(b1); + boolean default2 = myBreakpointManager.isDefaultBreakpoint(b2); + if (default1 && !default2) return -1; + if (!default1 && default2) return 1; + return myBreakpointComparator.compare(b1, b2); + } + if (o1 instanceof BreakpointsGroupNode && o2 instanceof BreakpointsGroupNode) { + final BreakpointsGroupNode group1 = (BreakpointsGroupNode)o1; + final BreakpointsGroupNode group2 = (BreakpointsGroupNode)o2; + if (group1.getLevel() != group2.getLevel()) { + return group1.getLevel() - group2.getLevel(); + } + return group1.getGroup().compareTo(group2.getGroup()); + } + return o1 instanceof BreakpointsGroupNode ? -1 : 1; + } + } + + public interface BreakpointItemsTreeDelegate { + void execute(BreakpointItem item); + } +} diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/BreakpointPanelProvider.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/BreakpointPanelProvider.java index b069579512b1..954a4ddcb564 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/BreakpointPanelProvider.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/BreakpointPanelProvider.java @@ -20,7 +20,8 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.markup.GutterIconRenderer; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.ui.popup.util.ItemWrapper; +import com.intellij.xdebugger.breakpoints.ui.BreakpointItem; +import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -31,6 +32,8 @@ import java.util.Collection; */ public abstract class BreakpointPanelProvider { + public abstract void provideBreakpointsGroupingRules(Collection rules); + public interface BreakpointsListener { void breakpointsChanged(); } @@ -54,5 +57,5 @@ public abstract class BreakpointPanelProvider { public abstract void onDialogClosed(final Project project); - public abstract void provideBreakpointItems(Project project, Collection items); + public abstract void provideBreakpointItems(Project project, Collection items); } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/BreakpointsMasterDetailPopupFactory.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/BreakpointsMasterDetailPopupFactory.java index e845a7539448..e8ddf05d8e57 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/BreakpointsMasterDetailPopupFactory.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/BreakpointsMasterDetailPopupFactory.java @@ -17,30 +17,33 @@ package com.intellij.xdebugger.impl.breakpoints.ui; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CustomShortcutSet; import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.actionSystem.ex.CheckboxAction; import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.editor.colors.ex.DefaultColorSchemesManager; +import com.intellij.openapi.editor.colors.impl.EditorColorsSchemeImpl; +import com.intellij.openapi.editor.markup.EffectType; +import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.JBPopupListener; import com.intellij.openapi.ui.popup.LightweightWindowEvent; -import com.intellij.ui.CheckBoxListListener; -import com.intellij.ui.components.JBList; -import com.intellij.ui.popup.util.ItemWrapper; +import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.popup.util.MasterDetailPopupBuilder; import com.intellij.util.IconUtil; import com.intellij.util.PlatformIcons; import com.intellij.xdebugger.breakpoints.ui.BreakpointItem; +import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule; import com.intellij.xdebugger.impl.DebuggerSupport; +import com.intellij.xdebugger.ui.DebuggerColors; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.awt.*; import java.awt.event.KeyEvent; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; +import java.util.*; import java.util.List; /** @@ -50,12 +53,10 @@ import java.util.List; * Time: 3:11 AM * To change this template use File | Settings | File Templates. */ -public class BreakpointsMasterDetailPopupFactory implements CheckBoxListListener { +public class BreakpointsMasterDetailPopupFactory { private final List myBreakpointPanelProviders; private Project myProject; - private BreakpointListModel myModel; - private MasterDetailPopupBuilder myPopupBuilder; public BreakpointsMasterDetailPopupFactory(Project project) { myProject = project; @@ -71,36 +72,43 @@ public class BreakpointsMasterDetailPopupFactory implements CheckBoxListListener }); } - @Override - public void checkBoxSelectionChanged(int index, boolean value) { - final Object o = myModel.get(index); - if (o instanceof BreakpointItem) { - ((BreakpointItem)o).setEnabled(value); - } - } - public static BreakpointsMasterDetailPopupFactory getInstance(Project project) { return ServiceManager.getService(project, BreakpointsMasterDetailPopupFactory.class); } public JBPopup createPopup(@Nullable Object initialBreakpoint) { - final DefaultListSelectionModel selectionModel = new DefaultListSelectionModel(); - myModel = createBreakpointsItemsList(selectionModel); - final JBList list = new JBList(myModel); + MasterDetailPopupBuilder popupBuilder = new MasterDetailPopupBuilder(myProject); - list.setSelectionModel(selectionModel); + DefaultActionGroup actions = getActions(popupBuilder); - selectInitial(initialBreakpoint, myModel, list); + final Collection rules = new ArrayList(); - list.getEmptyText().setText("No Breakpoints"); + for (BreakpointPanelProvider provider : myBreakpointPanelProviders) { + provider.provideBreakpointsGroupingRules(rules); + } - DefaultActionGroup actions = getActions(list); + final BreakpointItemsTree tree = BreakpointItemsTree.createTree(rules); - myPopupBuilder = new MasterDetailPopupBuilder(myProject); - final JBPopup popup = myPopupBuilder. + final ArrayList breakpoints = collectItems(); + tree.buildTree(breakpoints); + + + + final BreakpointPanelProvider.BreakpointsListener listener = new BreakpointPanelProvider.BreakpointsListener() { + @Override + public void breakpointsChanged() { + tree.buildTree(collectItems()); + } + }; + + for (BreakpointPanelProvider provider : myBreakpointPanelProviders) { + provider.addListener(listener, myProject); + } + + final JBPopup popup = popupBuilder. setActionsGroup(actions). - setList(list). + setTree(tree). setDelegate(new MasterDetailPopupBuilder.Delegate() { @Override public String getTitle() { @@ -115,8 +123,27 @@ public class BreakpointsMasterDetailPopupFactory implements CheckBoxListListener public JComponent createAccessoryView(Project project) { return new JCheckBox(); } + + @Override + public Object[] getSelectedItemsInTree() { + final List res = tree.getSelectedBreakpoints(); + return res.toArray(new Object[res.size()]); + } }).setCloseOnEnter(false).createMasterDetailPopup(); + tree.setBorder(IdeBorderFactory.createBorder()); + + popupBuilder.getDetailView().setScheme(createScheme()); + + tree.setDelegate(new BreakpointItemsTree.BreakpointItemsTreeDelegate() { + @Override + public void execute(BreakpointItem item) { + item.execute(myProject, popup); + } + }); + + initSelection(initialBreakpoint, tree, breakpoints); + popup.addListener(new JBPopupListener() { @Override public void beforeShown(LightweightWindowEvent event) { @@ -125,26 +152,39 @@ public class BreakpointsMasterDetailPopupFactory implements CheckBoxListListener @Override public void onClosed(LightweightWindowEvent event) { - myModel.unsubscribe(); + for (BreakpointPanelProvider provider : myBreakpointPanelProviders) { + provider.removeListener(listener); + } } }); return popup; } - private void selectInitial(Object initialBreakpoint, BreakpointListModel model, JBList list) { - for (int i = 0, l = model.size(); i < l; ++i) { - final ItemWrapper item = (ItemWrapper)model.get(i); - if (item instanceof BreakpointItem) { - if (((BreakpointItem)item).getBreakpoint() == initialBreakpoint) { - list.setSelectedIndex(i); - break; - } + private void initSelection(Object initialBreakpoint, BreakpointItemsTree tree, ArrayList breakpoints) { + boolean found = false; + for (BreakpointItem breakpoint : breakpoints) { + if (breakpoint.getBreakpoint() == initialBreakpoint) { + tree.selectBreakpointItem(breakpoint); + found = true; + break; } } + + if (!found && !breakpoints.isEmpty()) { + tree.selectBreakpointItem(breakpoints.get(0)); + } } - private DefaultActionGroup getActions(final JBList list) { + private EditorColorsScheme createScheme() { + final EditorColorsScheme scheme = + new EditorColorsSchemeImpl(EditorColorsManager.getInstance().getGlobalScheme(), DefaultColorSchemesManager.getInstance()); + scheme.setName("abc"); + scheme.setAttributes(DebuggerColors.BREAKPOINT_ATTRIBUTES, new TextAttributes(Color.black, Color.CYAN, null, EffectType.BOXED, Font.BOLD)); + return scheme; + } + + private DefaultActionGroup getActions(final MasterDetailPopupBuilder builder) { DefaultActionGroup actions = new DefaultActionGroup(); final DefaultActionGroup breakpointTypes = new DefaultActionGroup(); for (BreakpointPanelProvider provider : myBreakpointPanelProviders) { @@ -156,105 +196,29 @@ public class BreakpointsMasterDetailPopupFactory implements CheckBoxListListener JBPopupFactory.getInstance() .createActionGroupPopup(null, breakpointTypes, e.getDataContext(), JBPopupFactory.ActionSelectionAid.NUMBERING, false) - .showUnderneathOf(myPopupBuilder.getActionToolbar().getComponent()); + .showUnderneathOf(builder.getActionToolbar().getComponent()); } }); actions.add(new AnAction("Remove Breakpoint", null, PlatformIcons.DELETE_ICON) { @Override public void update(AnActionEvent e) { - e.getPresentation().setEnabled(MasterDetailPopupBuilder.allowedToRemoveSelectedItem(list, myProject)); + e.getPresentation().setEnabled(MasterDetailPopupBuilder.allowedToRemoveItems(builder.getSelectedItems())); } @Override public void actionPerformed(AnActionEvent e) { - MasterDetailPopupBuilder.removeSelectedItems(list, myProject); + builder.removeSelectedItems(myProject); } }); return actions; } - private BreakpointListModel createBreakpointsItemsList(DefaultListSelectionModel selectionModel) { - final BreakpointListModel model = new BreakpointListModel(); - final ArrayList items = collectItems(); - for (ItemWrapper item : items) { - model.addElement(item); - } - model.subscribe(selectionModel); - return model; - } - - - private ArrayList collectItems() { - ArrayList items = new ArrayList(); + private ArrayList collectItems() { + ArrayList items = new ArrayList(); for (BreakpointPanelProvider panelProvider : myBreakpointPanelProviders) { panelProvider.provideBreakpointItems(myProject, items); } return items; } - - private class BreakpointListModel extends DefaultListModel { - List myListeners = new ArrayList(); - - private void subscribe(DefaultListSelectionModel selectionModel) { - for (BreakpointPanelProvider panelProvider : myBreakpointPanelProviders) { - final BreakpointPanelProvider.BreakpointsListener listener = new MyBreakpointsListener(this, selectionModel); - panelProvider.addListener(listener, myProject); - myListeners.add(listener); - } - } - - public void unsubscribe() { - for (int i = 0, size = myListeners.size(); i < size; i++) { - BreakpointPanelProvider.BreakpointsListener listener = myListeners.get(i); - myBreakpointPanelProviders.get(i).removeListener(listener); - } - } - - private class MyBreakpointsListener implements BreakpointPanelProvider.BreakpointsListener { - private final DefaultListModel myModel; - private DefaultListSelectionModel mySelectionModel; - - public MyBreakpointsListener(DefaultListModel model, DefaultListSelectionModel selectionModel) { - myModel = model; - mySelectionModel = selectionModel; - } - - @Override - public void breakpointsChanged() { - final ArrayList items = collectItems(); - if (!reallyChanged(items, myModel)) { - return; - } - rebuildModel(items, mySelectionModel, myModel); - } - - } - } - - private static void rebuildModel(ArrayList items, ListSelectionModel model, DefaultListModel model1) { - final int index = model.getLeadSelectionIndex(); - model1.removeAllElements(); - for (ItemWrapper item : items) { - model1.addElement(item); - } - model.setLeadSelectionIndex(index); - } - - private static boolean reallyChanged(List items, DefaultListModel model) { - if (items.size() != model.size()) return true; - for (int i = 0; i < model.size(); i++) { - final ItemWrapper item1 = items.get(i); - final ItemWrapper item2 = (ItemWrapper)model.get(i); - if (item1.getClass() != item2.getClass()) { - return true; - } - if (item1 instanceof BreakpointItem) { - if (((BreakpointItem)item1).getBreakpoint() != ((BreakpointItem)item2).getBreakpoint()) { - return true; - } - } - } - return false; - } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/grouping/XBreakpointFileGroupingRule.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/grouping/XBreakpointFileGroupingRule.java index c0318d811356..6c09cd0a2883 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/grouping/XBreakpointFileGroupingRule.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/grouping/XBreakpointFileGroupingRule.java @@ -27,13 +27,17 @@ import java.util.Collection; /** * @author nik */ -public class XBreakpointFileGroupingRule> extends XBreakpointGroupingRule { +public class XBreakpointFileGroupingRule extends XBreakpointGroupingRule { public XBreakpointFileGroupingRule() { super("by-file", XDebuggerBundle.message("rule.name.group.by.file")); } public XBreakpointFileGroup getGroup(@NotNull final B breakpoint, @NotNull final Collection groups) { - XSourcePosition position = breakpoint.getSourcePosition(); + if (!(breakpoint instanceof XLineBreakpoint)) { + return null; + } + XSourcePosition position = ((XLineBreakpoint)breakpoint).getSourcePosition(); + if (position == null) return null; VirtualFile file = position.getFile();