/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.properties; import com.rapidminer.gui.tools.ExtendedJScrollPane; import com.rapidminer.gui.tools.FilterTextField; import com.rapidminer.gui.tools.FilterableListModel; import com.rapidminer.gui.tools.FilterableListModel.FilterCondition; import com.rapidminer.gui.tools.ResourceAction; import com.rapidminer.gui.tools.dialogs.InputDialog; import com.rapidminer.parameter.ParameterTypeAttributeOrderingRules; import com.rapidminer.tools.I18N; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.swing.Action; import javax.swing.DefaultListCellRenderer; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.JToggleButton; import javax.swing.ListCellRenderer; /** * A dialog to generate rules for attribute ordering. * * @author Nils Woehler */ public class AttributeOrderingDialog extends PropertyDialog { private static final long serialVersionUID = 5396725165122306231L; private final ArrayList<String> attributes; private final ArrayList<String> selectedRules; private final FilterTextField attributeSearchField; private final JTextField addRuleTextField; private final FilterableListModel<String> attributeListModel; private final DefaultListModel<String> selectedRulesListModel; private final JList<String> attributeList; private final JList<String> selectedRulesList; private FilterCondition currentTextFieldCondition; private final Map<String, FilterCondition> ruleToConditionMap = new HashMap<String, FilterCondition>(); private final Action selectAttributesAction = new ResourceAction(true, "attribute_ordering.attributes_select") { private static final long serialVersionUID = -3046621278306353077L; @Override public void actionPerformed(ActionEvent e) { int[] indices = attributeList.getSelectedIndices(); attributeList.setSelectedIndices(new int[] {}); List<String> selectedItems = new LinkedList<String>(); for (int i = 0; i < indices.length; i++) { selectedItems.add(attributeListModel.getElementAt(indices[i]).toString()); } for (String item : selectedItems) { attributeListModel.removeElement(item); attributes.remove(item); selectedRulesListModel.addElement(item); selectedRules.add(item); } addFilterConditions(selectedItems); } }; private final Action deselectAttributesAction = new ResourceAction(true, "attribute_ordering.attributes_deselect") { private static final long serialVersionUID = -3046621278306353077L; @Override public void actionPerformed(ActionEvent e) { int[] indices = selectedRulesList.getSelectedIndices(); selectedRulesList.setSelectedIndices(new int[] {}); List<String> selectedItems = new LinkedList<String>(); for (int i = 0; i < indices.length; i++) { selectedItems.add(selectedRulesListModel.getElementAt(indices[i]).toString()); } for (String item : selectedItems) { selectedRulesListModel.removeElement(item); selectedRules.remove(item); if (!attributes.contains(item)) { attributes.add(item); attributeListModel.addElement(item); } } removeFilterConditions(selectedItems); } }; private final Action addOrderingRuleAction = new ResourceAction(true, "attribute_ordering.add") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { final String newRule = addRuleTextField.getText(); // clear text field addRuleTextField.setText(""); addRuleTextField.requestFocusInWindow(); if (newRule != null && newRule.trim().length() != 0 && !(selectedRules.contains(newRule))) { // add rule to list selectedRules.add(newRule); selectedRulesListModel.addElement(newRule); attributes.remove(newRule); attributeListModel.removeElement(newRule); // apply filter to attributes if button is pressed addFilterCondition(newRule); // remove old condition currentTextFieldCondition = null; } } }; private final Action moveRuleUpAction = new ResourceAction(true, "attribute_ordering.up") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { int[] selectedIndices = selectedRulesList.getSelectedIndices(); if (selectedIndices.length != 0) { int firstIndex = selectedIndices[0]; if (firstIndex > 0) { for (int i = 0; i < selectedIndices.length; i++) { // bubble sort rules list model int currentIndex = selectedIndices[i]; String movedDown = selectedRulesListModel.get(currentIndex - 1).toString(); String movedUp = selectedRulesListModel.get(currentIndex).toString(); selectedRulesListModel.set(currentIndex, movedDown); selectedRulesListModel.set(currentIndex - 1, movedUp); // for arrays same selectedRules.set(currentIndex, movedDown); selectedRules.set(currentIndex - 1, movedUp); selectedIndices[i] = currentIndex - 1; } selectedRulesList.setSelectedIndices(selectedIndices); } } } }; private final Action moveRuleDownAction = new ResourceAction(true, "attribute_ordering.down") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { int[] selectedIndices = selectedRulesList.getSelectedIndices(); if (selectedIndices.length != 0) { int lastIndex = selectedIndices[selectedIndices.length - 1]; if (lastIndex < selectedRulesListModel.size() - 1) { for (int i = selectedIndices.length - 1; i >= 0; i--) { // bubble sort rules list model int currentIndex = selectedIndices[i] + 1; String movedDown = selectedRulesListModel.get(currentIndex - 1).toString(); String movedUp = selectedRulesListModel.get(currentIndex).toString(); selectedRulesListModel.set(currentIndex, movedDown); selectedRulesListModel.set(currentIndex - 1, movedUp); // for arrays same selectedRules.set(currentIndex, movedDown); selectedRules.set(currentIndex - 1, movedUp); selectedIndices[i] = currentIndex; } selectedRulesList.setSelectedIndices(selectedIndices); } } } }; private final Action editRuleAction = new ResourceAction(true, "attribute_ordering.rename") { private static final long serialVersionUID = 1L; @SuppressWarnings("deprecation") @Override public void actionPerformed(ActionEvent e) { int[] selectedIndices = selectedRulesList.getSelectedIndices(); if (selectedIndices.length != 0) { int currentIndex = selectedIndices[0]; String oldRule = selectedRules.get(currentIndex); InputDialog inputDialog = new InputDialog("attribute_ordering", oldRule); inputDialog.setVisible(true); if (inputDialog.wasConfirmed()) { String newRule = inputDialog.getInputText(); selectedRules.set(currentIndex, newRule); selectedRulesListModel.set(currentIndex, newRule); ruleToConditionMap.remove(oldRule); addFilterCondition(newRule); } } } }; private final Action hideMatchedAction = new ResourceAction(true, "attribute_ordering.hide_unmatched") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { applyFilterConditions(); } }; private JToggleButton hideMatchedButton; public AttributeOrderingDialog(final ParameterTypeAttributeOrderingRules type, Collection<String> preselectedItems, boolean useRegExp) { super(type, "attribute_ordering"); JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); attributes = new ArrayList<String>(); selectedRules = new ArrayList<String>(); attributeListModel = new FilterableListModel<>(); selectedRulesListModel = new DefaultListModel<>(); for (String item : type.getAttributeNames()) { if (item != null && item.trim().length() != 0 && !preselectedItems.contains(item)) { attributes.add(item); attributeListModel.addElement(item); } } if (!preselectedItems.isEmpty()) { for (String item : preselectedItems) { if (item != null && item.trim().length() != 0) { selectedRules.add(item); selectedRulesListModel.addElement(item); } } } // --------------------- LEFT SIDE --------------------------------------- attributeSearchField = new FilterTextField(); attributeSearchField.addFilterListener(attributeListModel); JButton itemSearchFieldClearButton = new JButton(new ResourceAction(true, "attribute_ordering.clear") { private static final long serialVersionUID = -3046621278306353077L; @Override public void actionPerformed(ActionEvent e) { attributeSearchField.clearFilter(); attributeSearchField.requestFocusInWindow(); } }); JPanel itemSearchFieldPanel = new JPanel(new GridBagLayout()); c.insets = new Insets(0, 0, 0, 0); c.gridx = 0; c.gridy = 0; c.weightx = 1; c.weighty = 0; c.fill = GridBagConstraints.BOTH; itemSearchFieldPanel.add(attributeSearchField, c); c.gridx = 1; c.weightx = 0; itemSearchFieldPanel.add(itemSearchFieldClearButton, c); hideMatchedButton = new JToggleButton(hideMatchedAction); hideMatchedButton.setSize(20, 20); hideMatchedButton.setPreferredSize(new Dimension(30, 25)); hideMatchedButton.setEnabled(useRegExp); hideMatchedButton.setSelected(useRegExp); c.gridx = 2; c.weightx = 0; itemSearchFieldPanel.add(hideMatchedButton, c); attributeList = new JList<>(attributeListModel); attributeList.addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { selectAttributesAction.actionPerformed(null); } } @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} @Override public void mousePressed(MouseEvent e) {} @Override public void mouseReleased(MouseEvent e) {} }); attributeList.setCellRenderer(new ListCellRenderer<String>() { DefaultListCellRenderer renderer = new DefaultListCellRenderer(); @Override public Component getListCellRendererComponent(JList<? extends String> list, String value, int index, boolean isSelected, boolean cellHasFocus) { Component renderComp = renderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (currentTextFieldCondition != null && currentTextFieldCondition.matches(value.toString())) { renderComp.setForeground(Color.red); } else { renderComp.setForeground(Color.black); } return renderComp; } }); JScrollPane attributeListPane = new ExtendedJScrollPane(attributeList); attributeListPane.setBorder(createBorder()); JPanel attributeListPanel = new JPanel(new GridBagLayout()); c.insets = new Insets(4, 4, 4, 4); c.gridx = 0; c.gridy = 0; c.weightx = 1; c.weighty = 0; c.fill = GridBagConstraints.BOTH; attributeListPanel.add(itemSearchFieldPanel, c); c.gridy = 1; c.weighty = 1; attributeListPanel.add(attributeListPane, c); attributeListPanel .setBorder(createTitledBorder(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".attributes.border"))); // ----------------------------- RIGHT SIDE --------------------------------------- JPanel selectedRulesListPanel = new JPanel(new GridBagLayout()); JPanel addRulePanel = new JPanel(new GridBagLayout()); c.insets = new Insets(0, 0, 0, 0); c.gridx = 0; c.gridy = 0; c.weightx = 1; c.weighty = 0; c.fill = GridBagConstraints.BOTH; addRuleTextField = new JTextField(); addRuleTextField.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) {} @Override public void keyReleased(KeyEvent e) { if (hideMatchedButton.isSelected()) { String text = addRuleTextField.getText(); if (text != null && text.trim().length() != 0) { currentTextFieldCondition = createNewCondition(text); } else { currentTextFieldCondition = null; } attributeList.repaint(); } } @Override public void keyPressed(KeyEvent e) {} }); addRulePanel.add(addRuleTextField, c); JButton addRuleButton = new JButton(addOrderingRuleAction); c.gridx = 1; c.weightx = 0; addRulePanel.add(addRuleButton, c); c.insets = new Insets(4, 4, 4, 4); c.gridx = 0; c.gridy = 0; c.weighty = 0; c.weightx = 1; c.fill = GridBagConstraints.BOTH; selectedRulesListPanel.add(addRulePanel, c); JPanel orderingListAndButtonContainer = new JPanel(new GridBagLayout()); selectedRulesList = new JList<>(selectedRulesListModel); selectedRulesList.addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { deselectAttributesAction.actionPerformed(null); } } @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} @Override public void mousePressed(MouseEvent e) {} @Override public void mouseReleased(MouseEvent e) {} }); JScrollPane selectedRulesListPane = new ExtendedJScrollPane(selectedRulesList); selectedRulesListPane.setBorder(createBorder()); c.insets = new Insets(4, 4, 4, 4); c.gridx = 0; c.gridy = 0; c.weightx = 1; c.weighty = 1; c.fill = GridBagConstraints.BOTH; orderingListAndButtonContainer.add(selectedRulesListPane, c); // add right buttons JPanel rightButtonPanel = new JPanel(new GridLayout(3, 1)); JButton moveUpButton = new JButton(moveRuleUpAction); JButton moveDownButton = new JButton(moveRuleDownAction); JButton renameButton = new JButton(editRuleAction); rightButtonPanel.add(moveUpButton, 0, 0); rightButtonPanel.add(moveDownButton, 1, 1); rightButtonPanel.add(renameButton, 2, 1); c.insets = new Insets(4, 4, 4, 4); c.gridx = 1; c.gridy = 0; c.weightx = 0; c.weighty = 1; c.fill = GridBagConstraints.NONE; orderingListAndButtonContainer.add(rightButtonPanel, c); c.insets = new Insets(4, 4, 4, 4); c.gridx = 0; c.gridy = 1; c.weightx = 1; c.weighty = 1; c.fill = GridBagConstraints.BOTH; selectedRulesListPanel.add(orderingListAndButtonContainer, c); selectedRulesListPanel.setBorder(createTitledBorder(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".selected_attributes.border"))); // add available attributes panel c.insets = new Insets(0, 0, 0, 0); c.gridx = 0; c.gridy = 0; c.weightx = 0.5; c.weighty = 1; c.fill = GridBagConstraints.BOTH; panel.add(attributeListPanel, c); // add middle buttons JPanel midButtonPanel = new JPanel(new GridLayout(2, 1)); JButton selectButton = new JButton(selectAttributesAction); JButton deselectButton = new JButton(deselectAttributesAction); midButtonPanel.add(deselectButton, 0, 0); midButtonPanel.add(selectButton, 1, 0); c.insets = new Insets(4, 4, 4, 4); c.gridx = 1; c.weightx = 0; c.weighty = 1; c.fill = GridBagConstraints.NONE; panel.add(midButtonPanel, c); // add selected rules panel c.insets = new Insets(0, 0, 0, 0); c.gridx = 2; c.gridy = 0; c.weightx = 0.5; c.fill = GridBagConstraints.BOTH; panel.add(selectedRulesListPanel, c); Dimension d = panel.getPreferredSize(); d.setSize(d.getWidth() / 2, d.getHeight()); attributeListPanel.setPreferredSize(d); selectedRulesListPanel.setPreferredSize(d); layoutDefault(panel, NORMAL, makeOkButton("attribute_ordering_dialog_order"), makeCancelButton()); addRuleTextField.requestFocusInWindow(); // finally add conditions addFilterConditions(selectedRules); } public Collection<String> getSelectedAttributeNames() { return selectedRules; } private FilterCondition createNewCondition(final String rule) { return new FilterCondition() { @Override public String toString() { return "Matching rule: '" + rule + "'"; } @Override public boolean matches(Object o) { try { return o.toString().matches(rule); } catch (Exception e) { return false; } } }; } private void addFilterConditions(List<String> rules) { for (String rule : rules) { ruleToConditionMap.put(rule, createNewCondition(rule)); } applyFilterConditions(); } private void addFilterCondition(final String rule) { ruleToConditionMap.put(rule, createNewCondition(rule)); applyFilterConditions(); } private void removeFilterConditions(List<String> rules) { for (String rule : rules) { ruleToConditionMap.remove(rule); } applyFilterConditions(); } private void applyFilterConditions() { attributeListModel.removeAllConditions(); if (hideMatchedButton.isSelected()) { attributeListModel.addConditions(ruleToConditionMap.values()); } } }