/* * ------------------------------------------------------------------------ * * 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.proc.spotdetection; import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import org.knime.core.node.InvalidSettingsException; import org.knime.core.node.NotConfigurableException; import org.knime.core.node.defaultnodesettings.DialogComponent; import org.knime.core.node.defaultnodesettings.SettingsModel; import org.knime.core.node.port.PortObjectSpec; /** * A dialog component for the selection of wavelet levels (scales) in the wavelet spot detection plugin. This component * has to save two values per added level the enabled status and a user defined threshold factor. Internally both values * get mapped to a {@link SettingsModelDoubleArray}. For convenience use provided the static methods to read write to * the settings model. * * @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 DialogComponentScaleConfig extends DialogComponent { /** * creates a double[] that holds a configuration for this component specifically the enabled status and threshold * level for each generated wavelete level. The array can be used with {@link SettingsModelDoubleArray}. Note that * both parameter arrays have to be of the same length. * * @param enabled enabled status (per wavelet level) * @param thresholdValue (per wavelete level) * @return a double[] that can be used in a {@link SettingsModelDoubleArray} as {@link SettingsModel} for this * component. */ static double[] createModelArray(final boolean enabled[], final double thresholdValue[]) { final double[] values = new double[enabled.length * 2]; for (int i = 0; i < enabled.length; i++) { if (enabled[i]) { values[(i * 2)] = 1.0; } else { values[(i * 2)] = 0.0; } values[((i * 2) + 1)] = thresholdValue[i]; } return values; } /** * @param model {@link SettingsModel} which contains the enabled status and threshold factor for the generated * wavelet levels of this component. * @return the enabled stauts for each wavelet level */ static boolean[] getEnabledState(final SettingsModelDoubleArray model) { final double[] values = model.getDoubleArrayValue(); final boolean[] ret = new boolean[values.length / 2]; for (int i = 0; i < values.length; i += 2) { if (values[i] == 1.0) { ret[i / 2] = true; } } return ret; } /** * @param model {@link SettingsModel} which contains the enabled status and threshold factor for the generated * wavelet levels of this component. * @return the threshold factor for each wavelet level */ static double[] getThresholdValues(final SettingsModelDoubleArray model) { final double[] values = model.getDoubleArrayValue(); final double[] ret = new double[values.length / 2]; for (int i = 1; i < values.length; i += 2) { ret[(i - 1) / 2] = values[i]; } return ret; } private final String CHECK_BOX_TEXT = "enable"; private ArrayList<JCheckBox> m_enabledBoxes; // converter methods for easier usage of the missues double array private JPanel m_scalePanel; private ArrayList<JTextField> m_threshFactorFields; private final int TEXT_COLUMNS = 5; // constructor /** * Creates a component that allows to set the wavelet level parameters for the Wavelet Spot Detection plugin. Note * that the enabled status and threshold factor of the levels are encoded in one {@link SettingsModelDoubleArray}. * Use the provided static methods to read write to the model. * * @param model * @param label component headline */ public DialogComponentScaleConfig(final SettingsModelDoubleArray model, final String label) { super(model); createComponent(getComponentPanel(), label); } @Override protected void checkConfigurabilityBeforeLoad(final PortObjectSpec[] specs) throws NotConfigurableException { // we're always good - independent of the incoming spec } private synchronized void createComponent(final JPanel componentPanel, final String label) { componentPanel.setLayout(new BorderLayout()); m_scalePanel = new JPanel(); m_threshFactorFields = new ArrayList<JTextField>(); m_enabledBoxes = new ArrayList<JCheckBox>(); final JLabel labelComp = new JLabel(label); labelComp.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); componentPanel.add(labelComp, BorderLayout.NORTH); final JScrollPane scroll = new JScrollPane(m_scalePanel); componentPanel.add(scroll, BorderLayout.CENTER); final JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 10)); { final JButton addScale = new JButton("add scale"); final JButton removeScale = new JButton("remove scale"); buttonPanel.add(addScale); buttonPanel.add(removeScale); addScale.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { final JTextField newField = new JTextField(); newField.setText("1.0"); newField.setColumns(TEXT_COLUMNS); m_threshFactorFields.add(newField); m_enabledBoxes.add(new JCheckBox(CHECK_BOX_TEXT, true)); rebuildScalePanel(); } }); removeScale.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { final int size = m_threshFactorFields.size(); if (size > 1) { // cannot remove the first level m_threshFactorFields.remove(size - 1); m_enabledBoxes.remove(size - 1); rebuildScalePanel(); } } }); } componentPanel.add(buttonPanel, BorderLayout.SOUTH); } private synchronized void gui2Model() throws InvalidSettingsException { final double[] values = new double[m_enabledBoxes.size()]; final boolean[] enabled = new boolean[m_enabledBoxes.size()]; // testing at least one selection boolean selected = false; for (final JCheckBox box : m_enabledBoxes) { selected = selected | box.isSelected(); } if (!selected) { throw new InvalidSettingsException("at least one wavelet level has to be selected"); } // testing the values for (final JTextField text : m_threshFactorFields) { try { Double.parseDouble(text.getText()); } catch (final NumberFormatException nfe) { throw new InvalidSettingsException("threshold factor " + text.getText() + " is not a double", nfe); } } // saving for (int i = 0; i < m_enabledBoxes.size(); i++) { if (m_enabledBoxes.get(i).isSelected()) { enabled[i] = true; } values[i] = Double.valueOf(m_threshFactorFields.get(i).getText()); } ((SettingsModelDoubleArray)getModel()).setDoubleArrayValue(createModelArray(enabled, values)); } private synchronized void model2Gui() { m_threshFactorFields.clear(); m_enabledBoxes.clear(); final boolean[] enabled = getEnabledState(((SettingsModelDoubleArray)getModel())); final double[] values = getThresholdValues(((SettingsModelDoubleArray)getModel())); for (int i = 0; i < enabled.length; i++) { m_enabledBoxes.add(new JCheckBox(CHECK_BOX_TEXT, enabled[i])); final JTextField newField = new JTextField(String.valueOf(values[i])); newField.setColumns(TEXT_COLUMNS); m_threshFactorFields.add(newField); } rebuildScalePanel(); } private synchronized void rebuildScalePanel() { m_scalePanel.removeAll(); m_scalePanel.setLayout(new BorderLayout()); final JPanel holderPanel = new JPanel(new GridBagLayout()); final GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = 1.0; gbc.anchor = GridBagConstraints.NORTHWEST; gbc.insets = new Insets(0, 20, 0, 0); for (int i = 0; i < m_threshFactorFields.size(); i++) { final JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 20, 10)); panel.add(m_enabledBoxes.get(i)); panel.add(new JLabel(" wavelet level " + i + " with threshold factor ")); panel.add(m_threshFactorFields.get(i)); holderPanel.add(panel, gbc); gbc.gridy++; } m_scalePanel.add(holderPanel, BorderLayout.NORTH); m_scalePanel.revalidate(); m_scalePanel.repaint(); } @Override protected void setEnabledComponents(final boolean enabled) { throw new UnsupportedOperationException(); } @Override public void setToolTipText(final String text) { throw new UnsupportedOperationException(); } @Override protected void updateComponent() { model2Gui(); } @Override protected void validateSettingsBeforeSave() throws InvalidSettingsException { gui2Model(); } }