Rewrite filters panel

This commit is contained in:
Natalia Ukhorskaya
2015-06-05 15:59:43 +03:00
parent 14f90f15e5
commit b1f9341ab4
3 changed files with 572 additions and 2 deletions
@@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinBreakpointFiltersPanel">
<grid id="27dc6" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="5736" binding="myConditionsPanel" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="7">
<margin top="2" left="2" bottom="5" right="5"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<clientProperties>
<BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithoutIndent"/>
</clientProperties>
<border type="etched" title-resource-bundle="messages/DebuggerBundle" title-key="label.breakpoint.properties.panel.group.conditions"/>
<children>
<grid id="8e867" binding="myInstanceFiltersPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="17e11" class="javax.swing.JCheckBox" binding="myInstanceFiltersCheckBox">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<margin top="2" left="2" bottom="0" right="2"/>
<text resource-bundle="messages/DebuggerBundle" key="breakpoint.properties.panel.option.instance.filters"/>
</properties>
</component>
<grid id="5231f" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<hspacer id="eeee7">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="15" height="-1"/>
</grid>
</constraints>
</hspacer>
<xy id="28068" binding="myInstanceFiltersFieldPanel" layout-manager="XYLayout" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</xy>
</children>
</grid>
</children>
</grid>
<grid id="25884" binding="myClassFiltersPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="729d5" class="javax.swing.JCheckBox" binding="myClassFiltersCheckBox">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<margin top="2" left="2" bottom="0" right="2"/>
<text resource-bundle="messages/DebuggerBundle" key="breakpoint.properties.panel.option.class.filters"/>
</properties>
</component>
<grid id="9bef6" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<xy id="e3d10" binding="myClassFiltersFieldPanel" layout-manager="XYLayout" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</xy>
<hspacer id="ec2a">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="15" height="-1"/>
</grid>
</constraints>
</hspacer>
</children>
</grid>
</children>
</grid>
<grid id="275ca" binding="myPassCountPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="27bbc" class="javax.swing.JCheckBox" binding="myPassCountCheckbox">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<margin top="2" left="2" bottom="0" right="2"/>
<text resource-bundle="messages/DebuggerBundle" key="breakpoint.properties.panel.option.pass.count"/>
</properties>
</component>
<grid id="71095" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="33fef" class="javax.swing.JTextField" binding="myPassCountField">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="15" height="-1"/>
</grid>
</constraints>
<properties>
<enabled value="false"/>
<horizontalAlignment value="10"/>
</properties>
</component>
<hspacer id="28e6f">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="15" height="-1"/>
</grid>
</constraints>
</hspacer>
</children>
</grid>
</children>
</grid>
<vspacer id="d1c29">
<constraints>
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
</children>
</grid>
</children>
</grid>
</form>
@@ -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<T extends KotlinPropertyBreakpointProperties, B extends XBreakpoint<T>> extends XBreakpointCustomPropertiesPanel<B> {
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<String> filters = new ArrayList<String>();
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<ClassFilter> classFilters = new ArrayList<ClassFilter>();
ArrayList<ClassFilter> exclusionFilters = new ArrayList<ClassFilter>();
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<InstanceFilter> idxs = new ArrayList<InstanceFilter>();
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<String> filters = new ArrayList<String>();
for (ClassFilter classFilter : myClassFilters) {
if (classFilter.isEnabled()) {
filters.add(classFilter.getPattern());
}
}
List<String> excludeFilters = new ArrayList<String>();
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<String> 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());
}
}
@@ -229,8 +229,7 @@ public class KotlinFieldBreakpointType : JavaBreakpointType<KotlinPropertyBreakp
}
override fun createCustomRightPropertiesPanel(project: Project): XBreakpointCustomPropertiesPanel<XLineBreakpoint<KotlinPropertyBreakpointProperties>>? {
//TODO unsafe cast
return delegate.createCustomRightPropertiesPanel(project) as XBreakpointCustomPropertiesPanel<XLineBreakpoint<KotlinPropertyBreakpointProperties>>
return KotlinBreakpointFiltersPanel(project)
}
override fun isSuspendThreadSupported(): Boolean {