/*
* 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.gui.properties.celleditors.value;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.AbstractCellEditor;
import javax.swing.ComboBoxEditor;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import com.rapidminer.gui.properties.PropertyPanel;
import com.rapidminer.operator.Operator;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeTupel;
/**
* An editor for a tuple of parameters.
*
* @author Simon Fischer, Nils Woehler, Marius Helf
*/
public class ParameterTupelCellEditor extends AbstractCellEditor implements PropertyValueCellEditor {
private static final long serialVersionUID = -2387465714767785072L;
private JPanel panel;
/**
* The parameter types of the columns
*/
private ParameterType[] types;
private PropertyValueCellEditor[] editors;
private Operator operator;
private final FocusListener focusListener;
public ParameterTupelCellEditor(ParameterTypeTupel type) {
types = type.getParameterTypes();
focusListener = new FocusListener() {
@Override
public void focusLost(FocusEvent e) {
// fire only if the focus didn't move to another descendant of the containing panel. If this check
// would not be included, fireEditingStopped() would prevent switching between tuple components.
// Additionally, the event is only fired if the focus loss is permanently,
// i.e. it is not fired if the user e.g. just switched to another window.
// Otherwise any changes made after switching back to RapidMiner would
// not be saved for the same reasons as stated above.
Component oppositeComponent = e.getOppositeComponent();
if ((oppositeComponent == null || (oppositeComponent != panel && !SwingUtilities.isDescendingFrom(oppositeComponent, panel))) && !e.isTemporary()) {
fireEditingStopped();
}
}
@Override
public void focusGained(FocusEvent e) {
// if focus is passed to the root panel and we have at least one
// subcomponent (editor), then we pass the focus on to the first
// editor
if (e.getComponent() == panel && panel.getComponentCount() > 0) {
panel.getComponent(0).requestFocusInWindow();
}
}
};
}
@Override
public Object getCellEditorValue() {
String[] values = new String[editors.length];
for (int i = 0; i < editors.length; i++) {
if (editors[i].getCellEditorValue() != null)
values[i] = editors[i].getCellEditorValue().toString();
}
return ParameterTypeTupel.transformTupel2String(values);
}
@Override
public boolean rendersLabel() {
return false;
}
@Override
public void setOperator(Operator operator) {
this.operator = operator;
}
@Override
public boolean useEditorAsRenderer() {
return true;
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
String[] tupel;
if (value instanceof String) {
tupel = ParameterTypeTupel.transformString2Tupel((String) value);
} else {
tupel = (String[]) value;
}
if (panel == null) {
constructPanel(tupel);
}
for (int i = 0; i < editors.length; i++) {
editors[i].getTableCellEditorComponent(null, tupel[i], false, 0, 0);
}
return panel;
}
private void constructPanel(String[] values) {
// constructing editors
editors = new PropertyValueCellEditor[types.length];
for (int i = 0; i < types.length; i++) {
editors[i] = PropertyPanel.instantiateValueCellEditor(types[i], operator);
}
// building panel
panel = new JPanel();
panel.setFocusable(true);
panel.setLayout(new GridLayout(1, editors.length));
for (int i = 0; i < types.length; i++) {
Component editorComponent = editors[i].getTableCellEditorComponent(null, values[i], false, 0, 0);
if (editorComponent instanceof JComboBox && ((JComboBox) editorComponent).isEditable()) {
if ( ((JComboBox) editorComponent).isEditable() ) {
ComboBoxEditor editor = ((JComboBox) editorComponent).getEditor();
if (editor instanceof BasicComboBoxEditor) {
editor.getEditorComponent().addFocusListener(focusListener);
}
} else {
editorComponent.addFocusListener(focusListener);
}
} else if (editorComponent instanceof JPanel) {
JPanel editorPanel = (JPanel) editorComponent;
Component[] components = editorPanel.getComponents();
for (Component comp : components) {
comp.addFocusListener(focusListener);
}
} else {
editorComponent.addFocusListener(focusListener);
}
panel.add(editorComponent);
panel.addFocusListener(focusListener);
}
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
String[] tupel;
if (value instanceof String) {
tupel = ParameterTypeTupel.transformString2Tupel((String) value);
} else {
tupel = (String[]) value;
}
if (panel == null) {
constructPanel(tupel);
}
for (int i = 0; i < editors.length; i++) {
editors[i].getTableCellEditorComponent(null, tupel[i], false, 0, 0);
}
return panel;
}
/**
* Loops the sub-editors and calls stopCellEditing() on them.
* Returns false as soon as one editor; i.e. does not call
* the function on editors appearing further down in the list
* than the one returning false.
*/
@Override
public boolean stopCellEditing() {
for (int i = 0; i < editors.length; ++i) {
if (!editors[i].stopCellEditing()) {
return false;
}
}
return super.stopCellEditing();
}
/**
* Cancels editing of all sub-editors
*/
@Override
public void cancelCellEditing() {
for (int i = 0; i < editors.length; ++i) {
editors[i].cancelCellEditing();
}
super.cancelCellEditing();
}
}