/*
* RapidMiner
*
* Copyright (C) 2001-2014 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.tools.config.gui;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import com.rapidminer.gui.tools.ExtendedJScrollPane;
import com.rapidminer.gui.tools.FilterableListModel;
import com.rapidminer.gui.tools.ResourceAction;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.gui.tools.dialogs.ButtonDialog;
import com.rapidminer.gui.tools.dialogs.ConfirmDialog;
import com.rapidminer.gui.tools.dialogs.MultiConfirmDialog;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.tools.I18N;
import com.rapidminer.tools.config.Configurable;
import com.rapidminer.tools.config.ConfigurationException;
import com.rapidminer.tools.config.ConfigurationManager;
import com.rapidminer.tools.config.Configurator;
/**
* A dialog, providing access to {@link Configurable}s of a specified typeID. Offers options to create, delete or modify {Configurable}s.
*
* The I18N Keys for this dialog can be found at the {@link Configurable} interface.
*
* @author Dominik Halfkann
*
* @param <T> A subclass of {@link Configurable} which should be configured through the dialog.
*/
public class ConfigurationDialog<T extends Configurable> extends ButtonDialog {
private static final long serialVersionUID = 1L;
public static Action getOpenWindowAction(String typeID) {
final String finalTypeID = typeID;
Action OPEN_WINDOW = new ResourceAction(true, "configuration." + ConfigurationManager.getInstance().getConfigurator(typeID).getI18NBaseKey()) {
{
setCondition(EDIT_IN_PROGRESS, DONT_CARE);
}
private static final long serialVersionUID = 1L;
@SuppressWarnings("unchecked")
@Override
public void actionPerformed(ActionEvent e) {
Configurator config = ConfigurationManager.getInstance().getConfigurator(finalTypeID);
new ConfigurationDialog(config).setVisible(true);
}
};
return OPEN_WINDOW;
}
private static final Color TEXT_SELECTED_COLOR = UIManager.getColor("Tree.selectionForeground");
private static final Color TEXT_NON_SELECTED_COLOR = UIManager.getColor("Tree.textForeground");
private Icon entryIcon = null;
private Icon entryReadOnlyIcon = null;
private String lastSelectedName = "";
private final FilterableListModel model = new FilterableListModel();
{
Comparator<Object> comparator = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
if (!(o1 instanceof Configurable) || !(o2 instanceof Configurable)) {
return o1.toString().compareTo(o2.toString());
}
// sort lexicographically, but make sure read only connections are listed below all normal connections
Configurable co1 = (Configurable)o1;
Configurable co2 = (Configurable)o2;
/*if (co1.isReadOnly() && !co2.isReadOnly()) {
return 1;
} else if (!co1.isReadOnly() && co2.isReadOnly()) {
return -1;
} else {*/
return co1.getName().compareTo(co2.getName());
//}
}
};
model.setComparator(comparator);
}
private String I18NKey = "";
private Configurator<T> configurator;
/** this is a clone of the entry which is currently being edited */
private T currentlyEditedEntry = null;
private boolean hideListChangeConfirmationDialog = false;
private boolean entrySaved = false;
private boolean isNewEntry = false;
private ConfigurationPanel<? super T> configurationPanel = null;
public ConfigurationDialog(Configurator<T> configurator) {
super("configuration."+configurator.getI18NBaseKey());
Collection<AbstractButton> buttons = makeButtons();
this.I18NKey = "configuration."+configurator.getI18NBaseKey();
this.configurator = configurator;
this.configurationPanel = configurator.createConfigurationPanel();
setI18NVariables(this.I18NKey);
this.setModal(true);
JPanel allButtonPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
JPanel entryButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, GAP, GAP));
for (AbstractButton button : buttons) {
if (button != null) {
entryButtonPanel.add(button);
}
}
JPanel generalButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, GAP, GAP));
JButton closeButton = makeCloseButton();
closeButton.setAction(new ResourceAction("close") {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
try {
if (checkUnsavedChanges()) {
close();
}
} catch (ConfigurationException e1) {
SwingTools.showSimpleErrorMessage("configuration.dialog.general", e1, e1.getMessage());
close();
}
}
});
generalButtonPanel.add(closeButton);
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.weightx = 0;
allButtonPanel.add(entryButtonPanel, gbc);
gbc.gridx = 1;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1;
allButtonPanel.add(new JLabel(), gbc);
gbc.gridx = 2;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.weightx = 0;
allButtonPanel.add(generalButtonPanel, gbc);
layoutDefault(makeConfigurableManagementPanel(), allButtonPanel, LARGE);
//model.addElement(configurator.getParameterTypes().get(0));
setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
try {
if (checkUnsavedChanges()) {
close();
}
} catch (ConfigurationException e1) {
SwingTools.showSimpleErrorMessage("configuration.dialog.general", e1, e1.getMessage());
close();
}
}
});
// load entries
loadEntries();
CLONE_ENTRY_ACTION.setEnabled(false);
DELETE_ENTRY_ACTION.setEnabled(false);
}
private void loadEntries() {
List<String> entryNames = ConfigurationManager.getInstance().getAllConfigurableNames(configurator.getTypeId());
for (String entryName : entryNames) {
try {
addEntries(ConfigurationManager.getInstance().lookup(configurator.getTypeId(), entryName, null));
} catch (ConfigurationException e) {
SwingTools.showSimpleErrorMessage("configuration.dialog.general", e, e.getMessage());
return;
}
}
}
private void setI18NVariables(String baseKey) {
//entryIcon = SwingTools.createIcon("16/" + I18N.getMessage(I18N.getGUIBundle(), "gui.dialog." + baseKey + ".connection_entry.icon"));
entryIcon = SwingTools.createIcon("16/" + configurator.getIconName());
entryReadOnlyIcon = SwingTools.createIcon("16/lock.png");
}
public void addEntries(Configurable configurable) {
model.addElement(configurable);
}
public JPanel makeConfigurableManagementPanel() {
JPanel panel = new JPanel(createGridLayout(1, 2));
JScrollPane configurableListPane = new ExtendedJScrollPane(configurableList);
configurableListPane.setBorder(createTitledBorder(I18N.getMessage(I18N.getGUIBundle(), "gui.border." + I18NKey + ".list")));
panel.add(configurableListPane);
//panel.add(new JPanel());
JComponent configPanel = configurationPanel.getComponent();
configPanel.setBorder(createTitledBorder(I18N.getMessage(I18N.getGUIBundle(), "gui.border." + I18NKey + ".configuration")));
panel.add(configPanel);
return panel;
}
private final JList configurableList = new JList(model);
{
configurableList.setCellRenderer(new DefaultListCellRenderer() {
private static final long serialVersionUID = 4616183160018529751L;
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (isSelected) {
label.setForeground(TEXT_SELECTED_COLOR);
} else {
label.setForeground(TEXT_NON_SELECTED_COLOR);
}
if (value instanceof Configurable) {
Configurable entry = (Configurable) value;
String remoteSource = (entry.getSource() != null) ? "<br/>Taken from: " + entry.getSource().toHtmlString() : "";
String shortInfo = (entry.getShortInfo() != null) ? " (" + entry.getShortInfo() + ")" : "";
label.setText("<html>" + entry.getName() + "<small>" + shortInfo + remoteSource + "</small></html>");
label.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
if (entry.getSource() != null) {
label.setIcon(entryReadOnlyIcon);
} else {
label.setIcon(entryIcon);
}
}
return label;
}
});
configurableList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
boolean selected = configurableList.getSelectedValue() != null;
if (selected) {
if (hideListChangeConfirmationDialog) {
// Directly open the selected value
openEntry(getSelectedEntry());
} else if (currentlyEditedEntry == null) {
// The first time the user selects an entry
try {
if (hasUnsavedChanges()) {
SwingTools.showVerySimpleErrorMessage("configuration.dialog.demandsave", getConfigurableFromInputFields().getName());
configurableList.clearSelection();
return;
} else {
openEntry(getSelectedEntry());
}
} catch (ConfigurationException e1) {
SwingTools.showSimpleErrorMessage("configuration.dialog.general", e1, e1.getMessage());
configurableList.clearSelection();
return;
}
} else if (!lastSelectedName.equals(((Configurable)configurableList.getSelectedValue()).getName()) && !currentlyEditedEntry.equals((Configurable) configurableList.getSelectedValue())) {
// The user selected an entry which differs from the previous selected entry
if (isNewEntry) {
// The user wants to switch from an unsaved, new entry. Demand a save!
try {
if (hasUnsavedChanges()) {
int saveBeforeOpen = SwingTools.showConfirmDialog("configuration.dialog.savecurrent", ConfirmDialog.YES_NO_CANCEL_OPTION, currentlyEditedEntry.getName());
if (saveBeforeOpen == ConfirmDialog.YES_OPTION) {
// YES: Save entry, then open the new selected entry
T newSelectEntry = getSelectedEntry();
entrySaved = false;
SAVE_ENTRY_ACTION.actionPerformed(null);
if (entrySaved) {
openEntry(newSelectEntry);
configurableList.setSelectedValue(newSelectEntry, true);
} else {
configurableList.setSelectedValue(currentlyEditedEntry, true);
return;
}
} else if (saveBeforeOpen == ConfirmDialog.NO_OPTION) {
// NO: Delete entry, then open the new selected entry
T tempEntry = currentlyEditedEntry;
openEntry(getSelectedEntry());
model.removeElement(tempEntry);
isNewEntry = false;
} else if (saveBeforeOpen == ConfirmDialog.CANCEL_OPTION) {
configurableList.setSelectedValue(currentlyEditedEntry, true);
return;
}
} else {
openEntry(getSelectedEntry());
}
} catch (ConfigurationException e2) {
SwingTools.showSimpleErrorMessage("configuration.dialog.general", e2, e2.getMessage());
configurableList.setSelectedValue(currentlyEditedEntry, true);
return;
}
} else {
// In case the entry is already existing
try {
if (hasUnsavedChanges()) {
int saveBeforeOpen = SwingTools.showConfirmDialog("configuration.dialog.savecurrent", ConfirmDialog.YES_NO_CANCEL_OPTION, currentlyEditedEntry.getName());
if (saveBeforeOpen == ConfirmDialog.YES_OPTION) {
// YES: Save entry, then open the new selected entry
T newSelectEntry = getSelectedEntry();
entrySaved = false;
SAVE_ENTRY_ACTION.actionPerformed(null);
if (entrySaved) {
openEntry(newSelectEntry);
configurableList.setSelectedValue(newSelectEntry, true);
} else {
configurableList.setSelectedValue(currentlyEditedEntry, true);
return;
}
} else if (saveBeforeOpen == ConfirmDialog.NO_OPTION) {
// NO: Discard changed, then open the new selected entry
openEntry(getSelectedEntry());
} else if (saveBeforeOpen == ConfirmDialog.CANCEL_OPTION) {
// Cancel: Do nothing and set the selection back to the previous selected entry
configurableList.setSelectedValue(currentlyEditedEntry, true);
return;
}
} else {
openEntry(getSelectedEntry());
}
} catch (ConfigurationException e2) {
SwingTools.showSimpleErrorMessage("configuration.dialog.general", e2, e2.getMessage());
configurableList.setSelectedValue(currentlyEditedEntry, true);
return;
}
}
} else {
// same element, do nothing
return;
}
}
NEW_ENTRY_ACTION.setEnabled(selected);
CLONE_ENTRY_ACTION.setEnabled(selected);
DELETE_ENTRY_ACTION.setEnabled(selected);
}
});
}
public Collection<AbstractButton> makeButtons() {
Collection<AbstractButton> list = new LinkedList<AbstractButton>();
list.add(new JButton(SAVE_ENTRY_ACTION));
list.add(new JButton(NEW_ENTRY_ACTION));
list.add(new JButton(CLONE_ENTRY_ACTION));
list.add(new JButton(DELETE_ENTRY_ACTION));
return list;
}
/** Opens a new entry, updates the configuration panel with the new entry's values **/
private void openEntry(T entry) {
configurationPanel.updateComponents(entry);
SAVE_ENTRY_ACTION.setEnabled(entry.getSource() == null);
// Update currently edited entry
currentlyEditedEntry = entry;
//isNewEntry = false;
}
protected final Action SAVE_ENTRY_ACTION = new ResourceAction("configuration.dialog.save") {
private static final long serialVersionUID = -8477647509533859436L;
@Override
public void actionPerformed(ActionEvent e) {
// First let the ConfigurationPanel check the fields and display error messages
if (configurationPanel.checkFields()) {
T inputFieldEntry = null;
try {
inputFieldEntry = getConfigurableFromInputFields();
} catch (ConfigurationException e2) {
SwingTools.showSimpleErrorMessage("configuration.dialog.general", e2, e2.getMessage());
return;
}
// check if entry with same name already exists
Configurable sameNameEntry = null;
for (int i = 0; i < model.getSize(); i++) {
Configurable compareEntry = (Configurable) model.getElementAt(i);
if (compareEntry.getName().equals(inputFieldEntry.getName())) {
sameNameEntry = compareEntry;
break;
}
}
if (sameNameEntry == null || sameNameEntry.equals(currentlyEditedEntry)) {
// unique or unchanged name, overwrite currently edited entry if applicable
if (currentlyEditedEntry != null) {
model.removeElement(currentlyEditedEntry);
ConfigurationManager.getInstance().removeConfigurable(configurator.getTypeId(), currentlyEditedEntry.getName());
}
Configurable newItem = null;
try {
newItem = ConfigurationManager.getInstance().create(configurator.getTypeId(), inputFieldEntry.getName());
} catch (ConfigurationException e1) {
SwingTools.showSimpleErrorMessage("configuration.dialog.general", e1, e1.getMessage());
return;
}
for (ParameterType parameter : configurator.getParameterTypes()) {
newItem.setParameter(parameter.getKey(), inputFieldEntry.getParameters().get(parameter.getKey()));
}
model.addElement(inputFieldEntry);
configurableList.clearSelection();
hideListChangeConfirmationDialog = true;
configurableList.setSelectedValue(inputFieldEntry, true);
hideListChangeConfirmationDialog = false;
entrySaved = true;
isNewEntry = false;
} else {
// name already in use by another connection, ask for overwrite and then remove the overwritten entry
if (SwingTools.showConfirmDialog("configuration.dialog.overwrite", ConfirmDialog.YES_NO_OPTION, inputFieldEntry.getName()) == ConfirmDialog.YES_OPTION) {
model.removeElement(sameNameEntry);
ConfigurationManager.getInstance().removeConfigurable(configurator.getTypeId(), sameNameEntry.getName());
if (currentlyEditedEntry != null) {
model.removeElement(currentlyEditedEntry);
ConfigurationManager.getInstance().removeConfigurable(configurator.getTypeId(), currentlyEditedEntry.getName());
}
Configurable newItem = null;
try {
newItem = ConfigurationManager.getInstance().create(configurator.getTypeId(), inputFieldEntry.getName());
} catch (ConfigurationException e1) {
SwingTools.showSimpleErrorMessage("configuration.dialog.general", e1, e1.getMessage());
return;
}
for (ParameterType parameter : configurator.getParameterTypes()) {
newItem.setParameter(parameter.getKey(), inputFieldEntry.getParameters().get(parameter.getKey()));
}
model.addElement(inputFieldEntry);
configurableList.clearSelection();
hideListChangeConfirmationDialog = true;
configurableList.setSelectedValue(inputFieldEntry, true);
hideListChangeConfirmationDialog = false;
entrySaved = true;
isNewEntry = false;
}
}
// after all is done, save the new configuration
save();
NEW_ENTRY_ACTION.setEnabled(true);
}
}
};
private final Action NEW_ENTRY_ACTION = new ResourceAction("configuration.dialog.new") {
private static final long serialVersionUID = 7979548709619302219L;
@Override
public void actionPerformed(ActionEvent e) {
String alias = "New item ";
boolean unique = false;
int appendIndex = 1;
do {
for (int i = 0; i < model.getSize(); i++) {
unique = true;
Configurable compareEntry = (Configurable) model.getElementAt(i);
if (compareEntry.getName().equals(alias + appendIndex)) {
unique = false;
appendIndex++;
break;
}
}
// do as often as needed until we have a unique name (model must have elements otherwise we have an infinite loop
} while(!unique && model.getSize() > 0);
T newEntry = null;
try {
newEntry = configurator.create(alias + appendIndex, Collections.<String,String>emptyMap());
} catch (ConfigurationException e1) {
SwingTools.showSimpleErrorMessage("configuration.dialog.general", e1, e1.getMessage());
return;
}
model.addElement(newEntry);
configurableList.setSelectedValue(newEntry, true);
isNewEntry = true;
openEntry(newEntry);
NEW_ENTRY_ACTION.setEnabled(false);
CLONE_ENTRY_ACTION.setEnabled(false);
}
};
private final Action CLONE_ENTRY_ACTION = new ResourceAction("configuration.dialog.clone") {
private static final long serialVersionUID = -6286464201049577441L;
@Override
public void actionPerformed(ActionEvent e) {
Object value = configurableList.getSelectedValue();
if (value instanceof Configurable) {
Configurable selectedEntry = (Configurable) value;
String alias = "Copy of " + selectedEntry.getName();
boolean unique = false;
int copyIndex = 0;
do {
for (int i = 0; i < model.getSize(); i++) {
unique = true;
Configurable compareEntry = (Configurable) model.getElementAt(i);
if (compareEntry.getName().equals(alias)) {
unique = false;
copyIndex++;
alias = "Copy(" + copyIndex + ") of " + selectedEntry.getName();
break;
}
}
} while(!unique);
Configurable newEntry;
try {
newEntry = ConfigurationManager.getInstance().create(configurator.getTypeId(), alias);
} catch (ConfigurationException e1) {
SwingTools.showSimpleErrorMessage("configuration.dialog.general", e1, e1.getMessage());
return;
}
for (Map.Entry<String, String> parameter : selectedEntry.getParameters().entrySet()) {
newEntry.setParameter(parameter.getKey(), parameter.getValue());
}
model.addElement(newEntry);
save();
configurableList.setSelectedValue(newEntry, true);
}
}
};
private final Action DELETE_ENTRY_ACTION = new ResourceAction("configuration.dialog.delete") {
private static final long serialVersionUID = 1155260480975020776L;
@Override
public void actionPerformed(ActionEvent e) {
boolean deletedEntry = false;
Object[] selectedValues = configurableList.getSelectedValues();
boolean applyToAll = false;
int returnOption = ConfirmDialog.CANCEL_OPTION;
for (int i = 0; i < selectedValues.length; i++) {
Configurable entry = (Configurable) selectedValues[i];
if (!applyToAll) {
MultiConfirmDialog dialog = new MultiConfirmDialog("configuration.dialog.delete", ConfirmDialog.YES_NO_CANCEL_OPTION, entry.getName());
dialog.setVisible(true);
applyToAll = dialog.applyToAll();
returnOption = dialog.getReturnOption();
}
if (returnOption == ConfirmDialog.CANCEL_OPTION) {
break;
}
if (returnOption == ConfirmDialog.YES_OPTION) {
model.removeElement(entry);
ConfigurationManager.getInstance().removeConfigurable(configurator.getTypeId(), entry.getName());
isNewEntry = false;
NEW_ENTRY_ACTION.setEnabled(true);
deletedEntry = true;
configurableList.clearSelection();
for (int j = 0; j < selectedValues.length; j++) {
int index = model.indexOf(selectedValues[j]);
configurableList.getSelectionModel().addSelectionInterval(index, index);
}
}
}
if (deletedEntry) {
if (configurableList.getModel().getSize() > 0) {
hideListChangeConfirmationDialog = true;
configurableList.setSelectedIndex(0);
hideListChangeConfirmationDialog = false;
} else {
openEntry(null);
}
save();
}
}
};
/** Checks if the user changed any values or have an unsaved Item opened and asks him to save those changes before closing the dialog.
* Returns true if the dialog can be closed, false otherwise.
* @throws ConfigurationException **/
protected boolean checkUnsavedChanges() throws ConfigurationException {
//T inputFieldEntry = getConfigurableFromInputFields();
if (hasUnsavedChanges()) {
// If the user has changed any values / new unsaved entry
int saveBeforeOpen = SwingTools.showConfirmDialog("configuration.dialog.savecurrent", ConfirmDialog.YES_NO_CANCEL_OPTION, getConfigurableFromInputFields().getName());
if (saveBeforeOpen == ConfirmDialog.YES_OPTION) {
// YES: Speichere alles
entrySaved = false;
SAVE_ENTRY_ACTION.actionPerformed(null);
if (entrySaved) {
return true;
} else {
return false;
}
} else if (saveBeforeOpen == ConfirmDialog.NO_OPTION) {
// NO: Verwerfe alles
return true;
} else if (saveBeforeOpen == ConfirmDialog.CANCEL_OPTION) {
return false;
}
}
return true;
}
/** Returns true if the user has changed any values and hasn't saved them, false otherwise **/
private boolean hasUnsavedChanges() throws ConfigurationException {
if (currentlyEditedEntry == null) {
// The first time the user selects an entry, make sure everything is empty or the default value
if (getConfigurableFromInputFields().isEmptyOrDefault(configurator)) {
return false;
} else {
return true;
}
} else {
if (isNewEntry) {
// If it's a new entry, it needs to be saved
return true;
} else {
// If it's an entry which existed before, check if the values have changed
T inputFieldsEntry = getConfigurableFromInputFields();
if (currentlyEditedEntry.hasSameValues(inputFieldsEntry)) {
return false;
} else {
return true;
}
}
}
}
/** Resolves the Configurable which derives from the input field values **/
protected T getConfigurableFromInputFields() throws ConfigurationException {
T tempEntry = configurator.create("Temp", Collections.<String,String>emptyMap());;
configurationPanel.updateConfigurable(tempEntry);
return tempEntry;
//SwingTools.showSimpleErrorMessage("configuration.dialog.general", e1, e1.getMessage());
}
private T getSelectedEntry() {
return configurator.getConfigurableClass().cast(configurableList.getSelectedValue());
}
private void save() {
try {
ConfigurationManager.getInstance().saveConfiguration(configurator.getTypeId());
} catch (ConfigurationException e) {
SwingTools.showSimpleErrorMessage("configuration.dialog.failed_to_save_configurable", e, configurator.getName(), e.getMessage());
}
}
}