/* * ------------------------------------------------------------------------ * * 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.node.dialog; import java.util.ArrayList; import java.util.List; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JPanel; import org.knime.core.node.DataAwareNodeDialogPane; import org.knime.core.node.InvalidSettingsException; import org.knime.core.node.NodeSettingsRO; import org.knime.core.node.NodeSettingsWO; import org.knime.core.node.NotConfigurableException; import org.knime.core.node.defaultnodesettings.DialogComponent; import org.knime.core.node.port.PortObject; import org.knime.core.node.port.PortObjectSpec; /** * Implementation of DefaultNodeSettingsPane for DataAware Dialogs Note that the loadSettingsFrom / * loadAdditionalSettingsFrom methods have changed to suit the needs for DataAwareNodeDialogs * * @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 DataAwareDefaultNodeSettingsPane extends DataAwareNodeDialogPane { /* The tabs' names. */ private static final String TAB_TITLE = "Options"; private final List<DialogComponent> m_dialogComponents; private JPanel m_compositePanel; private JPanel m_currentPanel; private String m_defaultTabTitle = TAB_TITLE; private Box m_currentBox; private boolean m_horizontal = false; /** * Constructor for DefaultNodeDialogPane. */ public DataAwareDefaultNodeSettingsPane() { super(); m_dialogComponents = new ArrayList<DialogComponent>(); createNewPanels(); super.addTab(m_defaultTabTitle, m_compositePanel); } private void createNewPanels() { m_compositePanel = new JPanel(); m_compositePanel.setLayout(new BoxLayout(m_compositePanel, BoxLayout.Y_AXIS)); m_currentPanel = m_compositePanel; m_currentBox = createBox(m_horizontal); m_currentPanel.add(m_currentBox); } /** * Sets the title of the default tab that is created and used until you call {@link #createNewTab}. * * @param tabTitle the new title of the first tab. Can't be null or empty. * @throws IllegalArgumentException if the title is already used by another tab, or if the specified title is null * or empty. */ public void setDefaultTabTitle(final String tabTitle) { if ((tabTitle == null) || (tabTitle.length() == 0)) { throw new IllegalArgumentException("The title of a tab can't be " + "null or empty."); } if (tabTitle.equals(m_defaultTabTitle)) { return; } // check if we already have a tab with the new title if (super.getTab(tabTitle) != null) { throw new IllegalArgumentException("A tab with the specified new" + " name (" + tabTitle + ") already exists."); } super.renameTab(m_defaultTabTitle, tabTitle); m_defaultTabTitle = tabTitle; } /** * Creates a new tab in the dialog. All components added from now on are placed in that new tab. After creating a * new tab the previous tab is no longer accessible. If a tab with the same name was created before an Exception is * thrown. The new panel in the new tab has no group set (i.e. has no border). The new tab is placed at the * specified position (or at the right most position, if the index is too big). * * @param tabTitle the title of the new tab to use from now on. Can't be null or empty. * @param index the index to place the new tab at. Can't be negative. * @throws IllegalArgumentException if you specify a title that is already been used by another tab. Or if the * specified title is null or empty. * @see #setDefaultTabTitle(String) */ public void createNewTabAt(final String tabTitle, final int index) { if ((tabTitle == null) || (tabTitle.length() == 0)) { throw new IllegalArgumentException("The title of a tab can't be " + "null nor empty."); } // check if we already have a tab with the new title if (super.getTab(tabTitle) != null) { throw new IllegalArgumentException("A tab with the specified new" + " name (" + tabTitle + ") already exists."); } createNewPanels(); super.addTabAt(index, tabTitle, m_compositePanel,false); } /** * Creates a new tab in the dialog. All components added from now on are placed in that new tab. After creating a * new tab the previous tab is no longer accessible. If a tab with the same name was created before an Exception is * thrown. The new panel in the new tab has no group set (i.e. has no border). The tab is placed at the right most * position. * * @param tabTitle the title of the new tab to use from now on. Can't be null or empty. * @throws IllegalArgumentException if you specify a title that is already been used by another tab. Or if the * specified title is null or empty. * @see #setDefaultTabTitle(String) */ public void createNewTab(final String tabTitle) { createNewTabAt(tabTitle, Integer.MAX_VALUE); } /** * Brings the specified tab to front and shows its components. * * @param tabTitle the title of the tab to select. If the specified title doesn't exist, this method does nothing. */ public void selectTab(final String tabTitle) { setSelected(tabTitle); } /** * Creates a new dialog component group and closes the current one. From now on the dialog components added with the * addDialogComponent method are added to the current group. The group is a bordered and titled panel. * * @param title - the title of the new group. */ public void createNewGroup(final String title) { checkForEmptyBox(); m_currentPanel = createSubPanel(title); m_currentBox = createBox(m_horizontal); m_currentPanel.add(m_currentBox); } private JPanel createSubPanel(final String title) { JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), title)); m_compositePanel.add(panel); return panel; } /** * Closes the current group. Further added dialog components are added to the default panel outside any border. * */ public void closeCurrentGroup() { checkForEmptyBox(); if (m_currentPanel.getComponentCount() == 0) { m_compositePanel.remove(m_currentPanel); } m_currentPanel = m_compositePanel; m_currentBox = createBox(m_horizontal); m_currentPanel.add(m_currentBox); } /** * Add a new DialogComponent to the underlying dialog. It will automatically be added in the dialog and saved/loaded * from/to the config. * * @param diaC component to be added */ public void addDialogComponent(final DialogComponent diaC) { m_dialogComponents.add(diaC); m_currentBox.add(diaC.getComponentPanel()); addGlue(m_currentBox, m_horizontal); } /** * Changes the orientation the components get placed in the dialog. * * @param horizontal <code>true</code> if the next components should be placed next to each other or * <code>false</code> if the next components should be placed below each other. */ public void setHorizontalPlacement(final boolean horizontal) { if (m_horizontal != horizontal) { m_horizontal = horizontal; checkForEmptyBox(); m_currentBox = createBox(m_horizontal); m_currentPanel.add(m_currentBox); } } /** * Save settings of all registered <code>DialogComponents</code> into the configuration object. * * @param settings the <code>NodeSettings</code> to write into * @throws InvalidSettingsException if the user has entered wrong values */ @Override public void saveSettingsTo(final NodeSettingsWO settings) throws InvalidSettingsException { for (DialogComponent comp : m_dialogComponents) { comp.saveSettingsTo(settings); } saveAdditionalSettingsTo(settings); } /** * Load settings for all registered components. * * @param settings the <code>NodeSettings</code> to read from * @param input data at the input port * @throws NotConfigurableException if the node can currently not be configured */ @Override public void loadSettingsFrom(final NodeSettingsRO settings, final PortObject[] input) throws NotConfigurableException { assert settings != null; assert input != null; //extract specs from input data PortObjectSpec[] specs = new PortObjectSpec[input.length]; for (int i = 0; i < specs.length; i++) { specs[i] = input[i].getSpec(); } for (DialogComponent comp : m_dialogComponents) { comp.loadSettingsFrom(settings, specs); } loadAdditionalSettingsFrom(settings, input); } /** * Override hook to load additional settings. * * @param settings The settings of the node * @param input The input data. * @throws NotConfigurableException If not configurable */ public void loadAdditionalSettingsFrom(final NodeSettingsRO settings, final PortObject[] input) throws NotConfigurableException { } /** * This method can be overridden to save additional settings to the given settings object. * * @param settings the <code>NodeSettings</code> to write into * @throws InvalidSettingsException if the user has entered wrong values */ public void saveAdditionalSettingsTo(final NodeSettingsWO settings) throws InvalidSettingsException { assert settings != null; } private void checkForEmptyBox() { if (m_currentBox.getComponentCount() == 0) { m_currentPanel.remove(m_currentBox); } } /** * @param currentBox * @param horizontal */ private static void addGlue(final Box box, final boolean horizontal) { if (horizontal) { box.add(Box.createVerticalGlue()); } else { box.add(Box.createHorizontalGlue()); } } /** * @param horizontal <code>true</code> if the layout is horizontal * @return the box */ private static 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; } }