From b1f9341ab4eaacf5496facb71217629a60d5302e Mon Sep 17 00:00:00 2001 From: Natalia Ukhorskaya Date: Fri, 5 Jun 2015 15:59:43 +0300 Subject: [PATCH] Rewrite filters panel --- .../KotlinBreakpointFiltersPanel.form | 168 ++++++++ .../KotlinBreakpointFiltersPanel.java | 403 ++++++++++++++++++ .../breakpoints/KotlinFieldBreakpointType.kt | 3 +- 3 files changed, 572 insertions(+), 2 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinBreakpointFiltersPanel.form create mode 100644 idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinBreakpointFiltersPanel.java diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinBreakpointFiltersPanel.form b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinBreakpointFiltersPanel.form new file mode 100644 index 00000000000..7e91a4050cc --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinBreakpointFiltersPanel.form @@ -0,0 +1,168 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinBreakpointFiltersPanel.java b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinBreakpointFiltersPanel.java new file mode 100644 index 00000000000..4b90576c184 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinBreakpointFiltersPanel.java @@ -0,0 +1,403 @@ +/* + * Copyright 2010-2015 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 org.jetbrains.kotlin.idea.debugger.breakpoints; + +import com.intellij.debugger.InstanceFilter; +import com.intellij.debugger.ui.breakpoints.EditClassFiltersDialog; +import com.intellij.debugger.ui.breakpoints.EditInstanceFiltersDialog; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiClass; +import com.intellij.ui.FieldPanel; +import com.intellij.ui.MultiLineTooltipUI; +import com.intellij.ui.classFilter.ClassFilter; +import com.intellij.xdebugger.XSourcePosition; +import com.intellij.xdebugger.breakpoints.XBreakpoint; +import com.intellij.xdebugger.breakpoints.ui.XBreakpointCustomPropertiesPanel; +import com.intellij.xdebugger.impl.breakpoints.XBreakpointBase; +import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.java.debugger.breakpoints.properties.JavaBreakpointProperties; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class KotlinBreakpointFiltersPanel> extends XBreakpointCustomPropertiesPanel { + private JPanel myConditionsPanel; + private JPanel myInstanceFiltersPanel; + private JCheckBox myInstanceFiltersCheckBox; + private JPanel myInstanceFiltersFieldPanel; + private JPanel myClassFiltersPanel; + private JCheckBox myClassFiltersCheckBox; + private JPanel myClassFiltersFieldPanel; + private JPanel myPassCountPanel; + private JCheckBox myPassCountCheckbox; + private JTextField myPassCountField; + + private final FieldPanel myInstanceFiltersField; + private final FieldPanel myClassFiltersField; + + private ClassFilter[] myClassFilters = ClassFilter.EMPTY_ARRAY; + private ClassFilter[] myClassExclusionFilters = ClassFilter.EMPTY_ARRAY; + private InstanceFilter[] myInstanceFilters = InstanceFilter.EMPTY_ARRAY; + protected final Project myProject; + + private PsiClass myBreakpointPsiClass; + + public KotlinBreakpointFiltersPanel(Project project) { + myProject = project; + myInstanceFiltersField = new FieldPanel(new MyTextField(), "", null, + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + reloadInstanceFilters(); + EditInstanceFiltersDialog _dialog = new EditInstanceFiltersDialog(myProject); + _dialog.setFilters(myInstanceFilters); + _dialog.show(); + if (_dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) { + myInstanceFilters = _dialog.getFilters(); + updateInstanceFilterEditor(true); + } + } + }, + null + ); + + myClassFiltersField = new FieldPanel(new MyTextField(), "", null, + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + reloadClassFilters(); + + com.intellij.ide.util.ClassFilter classFilter = createClassConditionFilter(); + + EditClassFiltersDialog _dialog = new EditClassFiltersDialog(myProject, classFilter); + _dialog.setFilters(myClassFilters, myClassExclusionFilters); + _dialog.show(); + if (_dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) { + myClassFilters = _dialog.getFilters(); + myClassExclusionFilters = _dialog.getExclusionFilters(); + updateClassFilterEditor(true); + } + } + }, + null + ); + + ActionListener updateListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + updateCheckboxes(); + } + }; + + myPassCountCheckbox.addActionListener(updateListener); + myInstanceFiltersCheckBox.addActionListener(updateListener); + myClassFiltersCheckBox.addActionListener(updateListener); + + ToolTipManager.sharedInstance().registerComponent(myClassFiltersField.getTextField()); + ToolTipManager.sharedInstance().registerComponent(myInstanceFiltersField.getTextField()); + + insert(myInstanceFiltersFieldPanel, myInstanceFiltersField); + insert(myClassFiltersFieldPanel, myClassFiltersField); + + DebuggerUIUtil.focusEditorOnCheck(myPassCountCheckbox, myPassCountField); + DebuggerUIUtil.focusEditorOnCheck(myInstanceFiltersCheckBox, myInstanceFiltersField.getTextField()); + DebuggerUIUtil.focusEditorOnCheck(myClassFiltersCheckBox, myClassFiltersField.getTextField()); + } + + @NotNull + @Override + public JComponent getComponent() { + return myConditionsPanel; + } + + @Override + public boolean isVisibleOnPopup(@NotNull B breakpoint) { + JavaBreakpointProperties properties = breakpoint.getProperties(); + if (properties != null) { + return properties.isCOUNT_FILTER_ENABLED() || properties.isCLASS_FILTERS_ENABLED() || properties.isINSTANCE_FILTERS_ENABLED(); + } + return false; + } + + @Override + public void saveTo(@NotNull B breakpoint) { + JavaBreakpointProperties properties = breakpoint.getProperties(); + if (properties == null) { + return; + } + + boolean changed = false; + try { + String text = myPassCountField.getText().trim(); + int filter = !text.isEmpty() ? Integer.parseInt(text) : 0; + if (filter < 0) filter = 0; + changed = properties.setCOUNT_FILTER(filter); + } + catch (Exception ignored) { + } + + changed = properties.setCOUNT_FILTER_ENABLED(properties.getCOUNT_FILTER() > 0 && myPassCountCheckbox.isSelected()) || changed; + reloadInstanceFilters(); + reloadClassFilters(); + updateInstanceFilterEditor(true); + updateClassFilterEditor(true); + + changed = properties.setINSTANCE_FILTERS_ENABLED(myInstanceFiltersField.getText().length() > 0 && myInstanceFiltersCheckBox.isSelected()) || changed; + changed = properties.setCLASS_FILTERS_ENABLED(myClassFiltersField.getText().length() > 0 && myClassFiltersCheckBox.isSelected()) || changed; + changed = properties.setClassFilters(myClassFilters) || changed; + changed = properties.setClassExclusionFilters(myClassExclusionFilters) || changed; + changed = properties.setInstanceFilters(myInstanceFilters) || changed; + if (changed) { + ((XBreakpointBase)breakpoint).fireBreakpointChanged(); + } + } + + private static void insert(JPanel panel, JComponent component) { + panel.setLayout(new BorderLayout()); + panel.add(component, BorderLayout.CENTER); + } + + @Override + public void loadFrom(@NotNull B breakpoint) { + JavaBreakpointProperties properties = breakpoint.getProperties(); + if (properties != null) { + if (properties.getCOUNT_FILTER() > 0) { + myPassCountField.setText(Integer.toString(properties.getCOUNT_FILTER())); + } + else { + myPassCountField.setText(""); + } + + myPassCountCheckbox.setSelected(properties.isCOUNT_FILTER_ENABLED()); + + myInstanceFiltersCheckBox.setSelected(properties.isINSTANCE_FILTERS_ENABLED()); + myInstanceFiltersField.setEnabled(properties.isINSTANCE_FILTERS_ENABLED()); + myInstanceFiltersField.getTextField().setEditable(properties.isINSTANCE_FILTERS_ENABLED()); + myInstanceFilters = properties.getInstanceFilters(); + updateInstanceFilterEditor(true); + + myClassFiltersCheckBox.setSelected(properties.isCLASS_FILTERS_ENABLED()); + myClassFiltersField.setEnabled(properties.isCLASS_FILTERS_ENABLED()); + myClassFiltersField.getTextField().setEditable(properties.isCLASS_FILTERS_ENABLED()); + myClassFilters = properties.getClassFilters(); + myClassExclusionFilters = properties.getClassExclusionFilters(); + updateClassFilterEditor(true); + + XSourcePosition position = breakpoint.getSourcePosition(); + // TODO: need to calculate psi class + //myBreakpointPsiClass = breakpoint.getPsiClass(); + } + updateCheckboxes(); + } + + private void updateInstanceFilterEditor(boolean updateText) { + List filters = new ArrayList(); + for (InstanceFilter instanceFilter : myInstanceFilters) { + if (instanceFilter.isEnabled()) { + filters.add(Long.toString(instanceFilter.getId())); + } + } + if (updateText) { + myInstanceFiltersField.setText(StringUtil.join(filters, " ")); + } + + String tipText = concatWithEx(filters, " ", (int)Math.sqrt(myInstanceFilters.length) + 1, "\n"); + myInstanceFiltersField.getTextField().setToolTipText(tipText); + } + + private class MyTextField extends JTextField { + public MyTextField() { + } + + @Override + public String getToolTipText(MouseEvent event) { + reloadClassFilters(); + updateClassFilterEditor(false); + reloadInstanceFilters(); + updateInstanceFilterEditor(false); + String toolTipText = super.getToolTipText(event); + return getToolTipText().length() == 0 ? null : toolTipText; + } + + @Override + public JToolTip createToolTip() { + JToolTip toolTip = new JToolTip(){{ + setUI(new MultiLineTooltipUI()); + }}; + toolTip.setComponent(this); + return toolTip; + } + } + + private void reloadClassFilters() { + String filtersText = myClassFiltersField.getText(); + + ArrayList classFilters = new ArrayList(); + ArrayList exclusionFilters = new ArrayList(); + int startFilter = -1; + for(int i = 0; i <= filtersText.length(); i++) { + if(i < filtersText.length() && !Character.isWhitespace(filtersText.charAt(i))){ + if(startFilter == -1) { + startFilter = i; + } + } + else { + if(startFilter >=0) { + if(filtersText.charAt(startFilter) == '-') { + exclusionFilters.add(new ClassFilter(filtersText.substring(startFilter + 1, i))); + } + else { + classFilters.add(new ClassFilter(filtersText.substring(startFilter, i))); + } + startFilter = -1; + } + } + } + for (ClassFilter classFilter : myClassFilters) { + if (!classFilter.isEnabled()) { + classFilters.add(classFilter); + } + } + for (ClassFilter classFilter : myClassExclusionFilters) { + if (!classFilter.isEnabled()) { + exclusionFilters.add(classFilter); + } + } + myClassFilters = classFilters .toArray(new ClassFilter[classFilters .size()]); + myClassExclusionFilters = exclusionFilters.toArray(new ClassFilter[exclusionFilters.size()]); + } + + private void reloadInstanceFilters() { + String filtersText = myInstanceFiltersField.getText(); + + ArrayList idxs = new ArrayList(); + int startNumber = -1; + for(int i = 0; i <= filtersText.length(); i++) { + if(i < filtersText.length() && Character.isDigit(filtersText.charAt(i))) { + if(startNumber == -1) { + startNumber = i; + } + } + else { + if(startNumber >=0) { + idxs.add(InstanceFilter.create(filtersText.substring(startNumber, i))); + startNumber = -1; + } + } + } + for (InstanceFilter instanceFilter : myInstanceFilters) { + if (!instanceFilter.isEnabled()) { + idxs.add(instanceFilter); + } + } + myInstanceFilters = idxs.toArray(new InstanceFilter[idxs.size()]); + } + + private void updateClassFilterEditor(boolean updateText) { + List filters = new ArrayList(); + for (ClassFilter classFilter : myClassFilters) { + if (classFilter.isEnabled()) { + filters.add(classFilter.getPattern()); + } + } + List excludeFilters = new ArrayList(); + for (ClassFilter classFilter : myClassExclusionFilters) { + if (classFilter.isEnabled()) { + excludeFilters.add("-" + classFilter.getPattern()); + } + } + if (updateText) { + String editorText = StringUtil.join(filters, " "); + if(!filters.isEmpty()) { + editorText += " "; + } + editorText += StringUtil.join(excludeFilters, " "); + myClassFiltersField.setText(editorText); + } + + int width = (int)Math.sqrt(myClassExclusionFilters.length + myClassFilters.length) + 1; + String tipText = concatWithEx(filters, " ", width, "\n"); + if(!filters.isEmpty()) { + tipText += "\n"; + } + tipText += concatWithEx(excludeFilters, " ", width, "\n"); + myClassFiltersField.getTextField().setToolTipText(tipText); + } + + private static String concatWithEx(List s, String concator, int N, String NthConcator) { + String result = ""; + int i = 1; + for (Iterator iterator = s.iterator(); iterator.hasNext(); i++) { + String str = (String) iterator.next(); + result += str; + if(iterator.hasNext()){ + if(i % N == 0){ + result += NthConcator; + } + else { + result += concator; + } + } + } + return result; + } + + protected com.intellij.ide.util.ClassFilter createClassConditionFilter() { + com.intellij.ide.util.ClassFilter classFilter; + if(myBreakpointPsiClass != null) { + classFilter = new com.intellij.ide.util.ClassFilter() { + @Override + public boolean isAccepted(PsiClass aClass) { + return myBreakpointPsiClass == aClass || aClass.isInheritor(myBreakpointPsiClass, true); + } + }; + } + else { + classFilter = null; + } + return classFilter; + } + + protected void updateCheckboxes() { + boolean passCountApplicable = true; + if (myInstanceFiltersCheckBox.isSelected() || myClassFiltersCheckBox.isSelected()) { + passCountApplicable = false; + } + myPassCountCheckbox.setEnabled(passCountApplicable); + + final boolean passCountSelected = myPassCountCheckbox.isSelected(); + myInstanceFiltersCheckBox.setEnabled(!passCountSelected); + myClassFiltersCheckBox.setEnabled(!passCountSelected); + + myPassCountField.setEditable(myPassCountCheckbox.isSelected()); + myPassCountField.setEnabled (myPassCountCheckbox.isSelected()); + + myInstanceFiltersField.setEnabled(myInstanceFiltersCheckBox.isSelected()); + myInstanceFiltersField.getTextField().setEditable(myInstanceFiltersCheckBox.isSelected()); + + myClassFiltersField.setEnabled(myClassFiltersCheckBox.isSelected()); + myClassFiltersField.getTextField().setEditable(myClassFiltersCheckBox.isSelected()); + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointType.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointType.kt index bf6fb3b73ad..ce3bd0cb309 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointType.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointType.kt @@ -229,8 +229,7 @@ public class KotlinFieldBreakpointType : JavaBreakpointType>? { - //TODO unsafe cast - return delegate.createCustomRightPropertiesPanel(project) as XBreakpointCustomPropertiesPanel> + return KotlinBreakpointFiltersPanel(project) } override fun isSuspendThreadSupported(): Boolean {