/*
* ------------------------------------------------------------------------
*
* Copyright (C) 2003 - 2013
* University of Konstanz, Germany and
* KNIME GmbH, Konstanz, Germany
* Website: http://www.knime.org; Email: contact@knime.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, Version 3, as
* published by the Free Software Foundation.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses>.
*
* Additional permission under GNU GPL version 3 section 7:
*
* KNIME interoperates with ECLIPSE solely via ECLIPSE's plug-in APIs.
* Hence, KNIME and ECLIPSE are both independent programs and are not
* derived from each other. Should, however, the interpretation of the
* GNU GPL Version 3 ("License") under any applicable laws result in
* KNIME and ECLIPSE being a combined program, KNIME GMBH herewith grants
* you the additional permission to use and propagate KNIME together with
* ECLIPSE with only the license terms in place for ECLIPSE applying to
* ECLIPSE and the GNU GPL Version 3 applying for KNIME, provided the
* license terms of ECLIPSE themselves allow for the respective use and
* propagation of ECLIPSE together with KNIME.
*
* Additional permission relating to nodes for KNIME that extend the Node
* Extension (and in particular that are based on subclasses of NodeModel,
* NodeDialog, and NodeView) and that only interoperate with KNIME through
* standard APIs ("Nodes"):
* Nodes are deemed to be separate and independent programs and to not be
* covered works. Notwithstanding anything to the contrary in the
* License, the License does not apply to Nodes, you are not required to
* license Nodes under the License, and you are granted a license to
* prepare and propagate Nodes, in each case even if such Nodes are
* propagated with or for interoperation with KNIME. The owner of a Node
* may freely choose the license terms applicable to such Node, including
* when such Node is propagated with or for interoperation with KNIME.
* --------------------------------------------------------------------- *
*
*/
package org.knime.knip.base.nodes.features;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.AbstractTableModel;
import org.knime.core.data.DataTableSpec;
import org.knime.core.node.InvalidSettingsException;
import org.knime.core.node.NodeDialogPane;
import org.knime.core.node.NodeSettingsRO;
import org.knime.core.node.NodeSettingsWO;
import org.knime.core.node.NotConfigurableException;
import org.knime.core.node.defaultnodesettings.DefaultNodeSettingsPane;
import org.knime.core.node.defaultnodesettings.DialogComponent;
import org.knime.core.node.defaultnodesettings.SettingsModel;
import org.knime.core.node.defaultnodesettings.SettingsModelStringArray;
/**
* Dialog component collection which arranges the {@link DialogComponent} the one hand as usual (e.g. as be done in the
* {@link DefaultNodeSettingsPane}. But some specific dialog components (@see
* {@link FeatureSetDialogComponentCollection#addDialogComponent(String, DialogComponent)} will be added to a
* configuration pane of a feature set (identified by a String).
*
* PENDING CODE. Subject to changes!
*
*
* @author <a href="mailto:dietzc85@googlemail.com">Christian Dietz</a>
* @author <a href="mailto:horn_martin@gmx.de">Martin Horn</a>
* @author <a href="mailto:michael.zinsmaier@googlemail.com">Michael Zinsmaier</a>
*/
public class FeatureSetDialogComponentCollection {
private final SettingsModelStringArray m_activeFeatureSetSettings;
private final List<DialogComponent> m_allDialogComponents;
private final HashMap<String, HashMap<String, List<DialogComponent>>> m_dialogComponents;
private final HashMap<String, String> m_featIdNameMap;
private final HashMap<String, List<DialogComponent>> m_featureSetProvidersDialogComponents;
private JTable m_featureSetTable;
/**
* @param activeFeatureSetSettings settings to store the activated feature sets
*/
public FeatureSetDialogComponentCollection(final SettingsModelStringArray activeFeatureSetSettings) {
m_activeFeatureSetSettings = activeFeatureSetSettings;
m_dialogComponents = new HashMap<String, HashMap<String, List<DialogComponent>>>();
m_featureSetProvidersDialogComponents = new HashMap<String, List<DialogComponent>>();
m_featIdNameMap = new HashMap<String, String>();
m_allDialogComponents = new ArrayList<DialogComponent>();
}
/**
* Adds the Dialog Component to the feature list.
*
* @param featureName the name of the feature set
* @param featureId a unique id of the feature set
* @param dc
*/
public void
addFeatureSetDialogComponent(final String featureId, final String featureName, final DialogComponent dc) {
if (!m_featureSetProvidersDialogComponents.containsKey(featureId)) {
m_featureSetProvidersDialogComponents.put(featureId, new ArrayList<DialogComponent>());
}
m_featureSetProvidersDialogComponents.get(featureId).add(dc);
m_featIdNameMap.put(featureId, featureName);
}
/**
* Adds the {@link DialogComponent} to the specified tab and group.
*
* @param tab
* @param group
* @param dc
*/
public void addDialogComponent(final String tab, final String group, final DialogComponent dc) {
if (!m_dialogComponents.containsKey(tab)) {
m_dialogComponents.put(tab, new LinkedHashMap<String, List<DialogComponent>>());
}
final HashMap<String, List<DialogComponent>> groups = m_dialogComponents.get(tab);
if (!groups.containsKey(group)) {
groups.put(group, new ArrayList<DialogComponent>());
}
groups.get(group).add(dc);
}
/**
* Checks whether the settings model of the dialog components is different to the given ones.
*
* @param settingsModels
* @return
*/
public boolean checkConsistency(final List<SettingsModel> settingsModels) {
for (final SettingsModel sm : settingsModels) {
for (final DialogComponent dc : m_allDialogComponents) {
if (sm == dc.getModel()) {
return false;
}
}
}
return true;
}
/**
* Creates the dialog from the previously added {@link DialogComponent}s.
*
* @return the newly created dialog
*/
public NodeDialogPane getDialog() {
final int maxCheckBoxColumnWidth = 12;
return new NodeDialogPane() {
private Set<String> m_active;
private JPanel m_featurePanel;
private JPanel m_featureSetComponents;
{
// add standard dialog components
for (final String tab : m_dialogComponents.keySet()) {
final JPanel tabPanel = createNewPanel();
super.addTab(tab, tabPanel);
for (final String group : m_dialogComponents.get(tab).keySet()) {
final JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), group));
for (final DialogComponent dc : m_dialogComponents.get(tab).get(group)) {
panel.add(dc.getComponentPanel());
}
tabPanel.add(panel);
}
}
m_active = new HashSet<String>();
// add feature dialog components
m_featurePanel = new JPanel(new BorderLayout());
m_featureSetComponents = createNewPanel();
m_featureSetComponents.setPreferredSize(new Dimension(400, 150));
// setting up the table containing the features
// set list and the
// checkboxes to active/inactivate them
final List<String> featureSetNames = new ArrayList<String>();
final List<String> featureSetIds = new ArrayList<String>();
for (final String s : m_featureSetProvidersDialogComponents.keySet()) {
featureSetIds.add(s);
featureSetNames.add(m_featIdNameMap.get(s));
}
m_featureSetTable = new JTable(new AbstractTableModel() {
/**
*
*/
private static final long serialVersionUID = -7197084943688726222L;
@Override
public java.lang.Class<?> getColumnClass(final int arg0) {
if (arg0 == 0) {
return Boolean.class;
} else {
return String.class;
}
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public int getRowCount() {
return featureSetNames.size();
}
@Override
public Object getValueAt(final int rowIndex, final int columnIndex) {
if (columnIndex == 1) {
return featureSetNames.get(rowIndex);
} else {
return m_active.contains(featureSetIds.get(rowIndex));
}
}
});
m_featureSetTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
m_featureSetTable.getColumnModel().getColumn(0).setMaxWidth(maxCheckBoxColumnWidth);
m_featureSetTable.setShowGrid(false);
m_featureSetTable.setPreferredSize(new Dimension(150, 100));
m_featureSetTable.setTableHeader(null);
addTab("Features", m_featurePanel);
m_featurePanel.add(m_featureSetTable, BorderLayout.WEST);
m_featurePanel.add(new JScrollPane(m_featureSetComponents), BorderLayout.CENTER);
m_featureSetTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(final MouseEvent e) {
final String currentSet = featureSetIds.get(m_featureSetTable.getSelectedRow());
if (m_featureSetTable.getSelectedColumn() == 0) {
if (m_active.contains(currentSet)) {
m_active.remove(currentSet);
} else {
m_active.add(currentSet);
}
m_featureSetTable.updateUI();
}
m_featureSetComponents.removeAll();
for (final DialogComponent dc : m_featureSetProvidersDialogComponents.get(currentSet)) {
m_featureSetComponents.add(dc.getComponentPanel());
dc.getModel().setEnabled(m_active.contains(currentSet));
}
m_featureSetComponents.updateUI();
}
});
}
/**
* @param horizontal <code>true</code> if the layout is horizontal
* @return the box
*/
private Box createBox(final boolean horizontal) {
final Box box;
if (horizontal) {
box = new Box(BoxLayout.X_AXIS);
box.add(Box.createVerticalGlue());
} else {
box = new Box(BoxLayout.Y_AXIS);
box.add(Box.createHorizontalGlue());
}
return box;
}
private JPanel createNewPanel() {
final JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
final Box box = createBox(false);
panel.add(box);
return panel;
}
@Override
protected void loadSettingsFrom(final NodeSettingsRO settings, final DataTableSpec[] specs)
throws NotConfigurableException {
for (final String tab : m_dialogComponents.keySet()) {
for (final String group : m_dialogComponents.get(tab).keySet()) {
for (final DialogComponent dc : m_dialogComponents.get(tab).get(group)) {
dc.loadSettingsFrom(settings, specs);
}
}
}
for (final String feature : m_featureSetProvidersDialogComponents.keySet()) {
for (final DialogComponent dc : m_featureSetProvidersDialogComponents.get(feature)) {
dc.loadSettingsFrom(settings, specs);
}
}
try {
m_activeFeatureSetSettings.loadSettingsFrom(settings);
m_active.addAll(Arrays.asList(m_activeFeatureSetSettings.getStringArrayValue()));
for (final Entry<String, List<DialogComponent>> entry : m_featureSetProvidersDialogComponents
.entrySet()) {
for (final DialogComponent dc : entry.getValue()) {
dc.getModel().setEnabled(m_active.contains(entry.getKey()));
}
}
m_featureSetComponents.updateUI();
} catch (final InvalidSettingsException ex) {
ex.printStackTrace();
}
}
@Override
protected void saveSettingsTo(final NodeSettingsWO settings) throws InvalidSettingsException {
for (final String tab : m_dialogComponents.keySet()) {
for (final String group : m_dialogComponents.get(tab).keySet()) {
for (final DialogComponent dc : m_dialogComponents.get(tab).get(group)) {
dc.saveSettingsTo(settings);
}
}
}
for (final String feature : m_featureSetProvidersDialogComponents.keySet()) {
for (final DialogComponent dc : m_featureSetProvidersDialogComponents.get(feature)) {
dc.saveSettingsTo(settings);
}
}
m_activeFeatureSetSettings.setStringArrayValue(m_active.toArray(new String[m_active.size()]));
m_activeFeatureSetSettings.saveSettingsTo(settings);
}
};
}
}