/*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2015 University of Dundee. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.agents.metadata.rnd;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.DefaultComboBoxModel;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.openmicroscopy.shoola.agents.metadata.IconManager;
import org.openmicroscopy.shoola.agents.metadata.MetadataViewerAgent;
import org.openmicroscopy.shoola.agents.util.ViewedByItem;
import org.openmicroscopy.shoola.agents.util.ui.ChannelButton;
import org.openmicroscopy.shoola.env.LookupNames;
import org.openmicroscopy.shoola.env.rnd.RndProxyDef;
import org.openmicroscopy.shoola.util.ui.ColorListRenderer;
import org.openmicroscopy.shoola.util.ui.SeparatorPane;
import org.openmicroscopy.shoola.util.ui.UIUtilities;
import org.openmicroscopy.shoola.util.ui.slider.OneKnobSlider;
import omero.gateway.model.ChannelData;
/**
* Pane displaying the controls used to define the transformation process
* of the pixels intensity values.
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author Andrea Falconi
* <a href="mailto:a.falconi@dundee.ac.uk">a.falconi@dundee.ac.uk</a>
* @author Donald MacDonald
* <a href="mailto:donald@lifesci.dundee.ac.uk">
* donald@lifesci.dundee.ac.uk</a>
* @version 3.0
* @since 3.0-Beta4
*/
public class DomainPane
extends ControlPane
implements ActionListener, ChangeListener, MouseWheelListener
{
/** Holds the last used location of the divider between
* preview and channel panel
*/
static int PREFERRED_DIVIDER_LOCATION = -1;
/**
* For slider control only. The minimum value for the curve coefficient.
* The real value is divided by 10.
*/
static final int MIN_GAMMA = 1;
/**
* For slider control only. The maximum value for the curve coefficient.
* The real value is divided by 10.
*/
static final int MAX_GAMMA = 40;
/**
* For slider control only. The default value for the curve coefficient.
* The real value is divided by 10.
*/
static final int DEFAULT_GAMMA = 10;
/** The minimum value of the bit resolution. */
static final int MIN_BIT_DEPTH = 1;
/** The maximum value of the bit resolution. */
static final int MAX_BIT_DEPTH = 8;
/** The default value of the bit resolution. */
static final int DEFAULT_BIT_DEPTH = 8;
/** The border of the selected channel. */
private static final Border SELECTION_BORDER =
BorderFactory.createLineBorder(Color.BLACK, 3);
/** The factor .*/
private static final int FACTOR = 10;
/** Identifies the <code>Family</code> selection. */
private static final int FAMILY = 0;
/** Identifies the <code>Channels</code> selection. */
private static final int CHANNEL = 1;
/** Dimension of the box between the channel buttons. */
private static final Dimension VBOX = new Dimension(1, 10);
/** Title of the advanced options. */
private static final String ADVANCED_OPTIONS = "Advanced";
/** The description of a z-sections selection slider. */
private static final String Z_SLIDER_DESCRIPTION = "Select a z-section.";
/** The description of a time-point selection slider. */
private static final String T_SLIDER_DESCRIPTION = "Select a timepoint.";
/** The description of a bin selection slider. */
private static final String LITEIME_SLIDER_DESCRIPTION = "Select a bin.";
/** The tipString of the {@link #zSlider}. */
private static final String Z_SLIDER_TIPSTRING = "Z";
/** The tipString of the {@link #tSlider}. */
private static final String T_SLIDER_TIPSTRING = "T";
/** The tipString of the {@link #lifetimeSlider}. */
private static final String LIFETIME_SLIDER_TIPSTRING = "t";
/** Text of the launch in full viewer button */
private static final String OPEN_BUTTON_TEXT = "Launch full viewer";
/** Box to select the family used in the mapping process. */
private JComboBox familyBox;
/**
* A collection of ColourButtons which represent the channel selected
* in the mapping process.
*/
private List<ChannelButton> channelList;
/** Slider to select a curve in the family. */
private OneKnobSlider gammaSlider;
/** Slider to select the bit resolution of the rendered image. */
private OneKnobSlider bitDepthSlider;
/** Field displaying the <code>Gamma</code> value. */
private JTextField gammaLabel;
/** Field displaying the <code>Bit Depth</code> value. */
private JTextField bitDepthLabel;
/** Box to select the mapping algorithm. */
private JCheckBox noiseReduction;
/** Button to bring up the histogram widget on screen. */
private JButton histogramButton;
/** The UI component hosting the interval selections. */
private GraphicsPane graphicsPane;
/** Select the lifetime bin. */
private OneKnobSlider lifetimeSlider;
/** Selects the z-section. */
private OneKnobSlider zSlider;
/** Selects the time-point. */
private OneKnobSlider tSlider;
/** The component displaying the preview image. */
private PreviewCanvas canvas;
/** The box hosting the channels. */
private JComboBox channelsBox;
/** Button for opening the full image viewer */
private JButton openButton;
/**
* Attaches listener to the passed slider and sets the default values.
*
* @param slider The slider to handle.
* @param max The maximum value.
* @param v The default value.
* @param toolTip The text displayed in the tool tip.
* @param endLabel The text displayed in the tool tip when
* slider changes value, as well as the label shown at
* the end of the text.
*/
private void initSlider(OneKnobSlider slider, int max, int v,
String toolTip, String endLabel)
{
slider.setEnabled(max > 0);
slider.setBackground(UIUtilities.BACKGROUND_COLOR);
slider.setVisible(max != 0);
slider.setMaximum(max);
slider.setValue(v);
slider.addChangeListener(this);
slider.addMouseWheelListener(this);
slider.setToolTipText(toolTip);
slider.setEndLabel(endLabel);
slider.setShowEndLabel(true);
slider.setShowTipLabel(true);
if (max > 0 && max <= Renderer.MAX_NO_TICKS) {
slider.setPaintTicks(true);
slider.setMajorTickSpacing(1);
}
}
/**
* Handles the event when the wheel is moved over the {@link #zSlider}
* or {@link #zSliderGrid}.
*
* @param e The event to handle.
*/
private void mouseWheelMovedZ(MouseWheelEvent e)
{
boolean up = true;
if (e.getWheelRotation() > 0) up = false;
if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
int v = model.getDefaultZ()-e.getWheelRotation();
if (up) {
if (v <= model.getMaxZ())
controller.setSelectedXYPlane(v, model.getDefaultT());
} else { //moving down
if (v >= 0)
controller.setSelectedXYPlane(v, model.getDefaultT());
}
}
graphicsPane.setSelectedPlane();
}
/**
* Handles the event when the wheel is moved over the {@link #tSlider}
* or {@link #tSliderGrid}.
*
* @param e The event to handle.
*/
private void mouseWheelMovedT(MouseWheelEvent e)
{
boolean up = true;
if (e.getWheelRotation() > 0) up = false;
if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
int v = model.getDefaultT()-e.getWheelRotation();
if (up) {
if (v <= model.getMaxT())
controller.setSelectedXYPlane(model.getDefaultZ(), v);
} else { //moving down
if (v >= 0)
controller.setSelectedXYPlane(model.getDefaultZ(), v);
}
}
graphicsPane.setSelectedPlane();
}
/** Initializes the components composing the display. */
private void initComponents()
{
graphicsPane = new GraphicsPane(model, controller);
familyBox = new JComboBox(model.getFamilies().toArray());
familyBox.setBackground(UIUtilities.BACKGROUND_COLOR);
String family = model.getFamily();
familyBox.setSelectedItem(family);
familyBox.addActionListener(this);
familyBox.setActionCommand(""+FAMILY);
boolean gammaEnabled = family.equals(RendererModel.EXPONENTIAL) ||
family.equals(RendererModel.POLYNOMIAL);
double k = model.getCurveCoefficient();
gammaSlider = new OneKnobSlider(JSlider.HORIZONTAL, MIN_GAMMA,
MAX_GAMMA, (int) (k*FACTOR));
gammaSlider.setBackground(UIUtilities.BACKGROUND_COLOR);
gammaSlider.setShowArrows(false);
gammaSlider.setEnabled(gammaEnabled);
gammaSlider.addChangeListener(this);
gammaSlider.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
double v = (double) gammaSlider.getValue()/FACTOR;
gammaLabel.setText(""+v);
int channel = channelsBox.getSelectedIndex();
controller.setCurveCoefficient(channel, v);
}
});
gammaLabel = new JTextField(""+k);
gammaLabel.setBackground(UIUtilities.BACKGROUND_COLOR);
gammaLabel.setEnabled(gammaEnabled);
gammaLabel.setEditable(true);
gammaLabel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
try {
double value = Double.parseDouble(gammaLabel.getText());
gammaSlider.setValue((int)(value*FACTOR));
int channel = channelsBox.getSelectedIndex();
controller.setCurveCoefficient(channel, value);
} catch (NumberFormatException e1) {
gammaLabel.setText(""+(double) gammaSlider.getValue()/FACTOR);
}
}
});
int v = model.getBitResolution();
bitDepthSlider = new OneKnobSlider(JSlider.HORIZONTAL, MIN_BIT_DEPTH,
MAX_BIT_DEPTH, convertBitResolution(v));
bitDepthSlider.setBackground(UIUtilities.BACKGROUND_COLOR);
bitDepthSlider.setShowArrows(false);
bitDepthSlider.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
int v = convertUIBitResolution(bitDepthSlider.getValue());
bitDepthLabel.setText(""+v);
firePropertyChange(BIT_RESOLUTION_PROPERTY,
Integer.valueOf(model.getBitResolution()),
Integer.valueOf(v));
}
});
bitDepthSlider.addChangeListener(this);
bitDepthLabel = new JTextField(""+v);
bitDepthLabel.setBackground(UIUtilities.BACKGROUND_COLOR);
bitDepthLabel.setEnabled(false);
bitDepthLabel.setEditable(false);
noiseReduction = new JCheckBox();
noiseReduction.setBackground(UIUtilities.BACKGROUND_COLOR);
noiseReduction.setSelected(model.isNoiseReduction());
noiseReduction.setAction(
controller.getAction(RendererControl.NOISE_REDUCTION));
histogramButton = new JButton(
controller.getAction(RendererControl.HISTOGRAM));
channelList = new ArrayList<ChannelButton>();
if (model.isGeneralIndex()) {
int maxZ = model.getMaxZ()-1;
zSlider = new OneKnobSlider(OneKnobSlider.VERTICAL, 0, 1, 0);
zSlider.setEnabled(false);
tSlider = new OneKnobSlider(OneKnobSlider.HORIZONTAL, 0, 1, 0);
tSlider.setEnabled(false);
initSlider(tSlider, model.getRealT()-1, model.getRealSelectedT(),
T_SLIDER_DESCRIPTION, T_SLIDER_TIPSTRING);
initSlider(zSlider, maxZ, model.getDefaultZ(),
Z_SLIDER_DESCRIPTION, Z_SLIDER_TIPSTRING);
canvas = new PreviewCanvas();
canvas.setBackground(UIUtilities.BACKGROUND_COLOR);
canvas.setInterpolate(Boolean.parseBoolean((String) MetadataViewerAgent.getRegistry().lookup(
LookupNames.INTERPOLATE)));
}
if (model.hasModuloT()) {
lifetimeSlider = new OneKnobSlider(OneKnobSlider.HORIZONTAL,
0, 1, 0);
lifetimeSlider.setEnabled(false);
int maxBin = model.getMaxLifetimeBin()-1;
initSlider(lifetimeSlider, maxBin, model.getSelectedBin(),
LITEIME_SLIDER_DESCRIPTION, LIFETIME_SLIDER_TIPSTRING);
lifetimeSlider.setPaintTicks(false);
} else {
if (model.isLifetimeImage()) {
lifetimeSlider = new OneKnobSlider(OneKnobSlider.HORIZONTAL,
0, 1, 0);
lifetimeSlider.setEnabled(false);
int maxBin = model.getMaxLifetimeBin()-1;
initSlider(lifetimeSlider, maxBin, model.getSelectedBin(),
LITEIME_SLIDER_DESCRIPTION, LIFETIME_SLIDER_TIPSTRING);
lifetimeSlider.setPaintTicks(false);
}
}
graphicsPane.setSelectedPlane();
channelsBox = new JComboBox();
populateChannels();
channelsBox.setRenderer(new ColorListRenderer());
channelsBox.setActionCommand(""+CHANNEL);
channelsBox.setVisible(model.getMaxC() > 1);
openButton = new JButton(controller.getAction(RendererControl.VIEW));
openButton.setIcon(null);
openButton.setText(OPEN_BUTTON_TEXT);
openButton.setToolTipText(null);
}
/** Populates the channels. */
private void populateChannels()
{
if (channelsBox == null) return;
List<ChannelData> channels = model.getChannelData();
Object[][] channelCols = new Object[channels.size()][2];
Iterator<ChannelData> i = channels.iterator();
ChannelData data;
int index = 0;
int selected = 0;
while (i.hasNext()) {
data = i.next();
channelCols[index] = new Object[]{
model.getChannelColor(data.getIndex()),
data.getChannelLabeling() };
if (data.getIndex() == model.getSelectedChannel())
selected = index;
index++;
}
channelsBox.setModel(new DefaultComboBoxModel(channelCols));
channelsBox.removeActionListener(this);
channelsBox.setSelectedIndex(selected);
channelsBox.addActionListener(this);
}
/** Resets the value of the bit resolution. */
private void resetBitResolution()
{
int v = model.getBitResolution();
bitDepthSlider.removeChangeListener(this);
bitDepthSlider.setValue(convertBitResolution(v));
bitDepthSlider.addChangeListener(this);
bitDepthLabel.setText(""+v);
bitDepthLabel.repaint();
}
/**
* Creates a panel showing the channel buttons and histogram.
*
* @return See above.
*/
private JComponent buildChannelGraphicsPanel()
{
JComponent result;
if (model.isGeneralIndex()) {
JPanel viewerPane = buildViewerPane();
// add grey borders, because the split pane divider is hardly visible on Mac
viewerPane.setBorder(BorderFactory.createLineBorder(UIUtilities.LIGHT_GREY, 1));
graphicsPane.setBorder(BorderFactory.createLineBorder(UIUtilities.LIGHT_GREY, 1));
final JSplitPane p = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
p.setTopComponent(viewerPane);
p.setBottomComponent(graphicsPane);
viewerPane.setMinimumSize(model.getPreviewDimension());
// restore the last used divider location
if (PREFERRED_DIVIDER_LOCATION > 0) {
p.setDividerLocation(PREFERRED_DIVIDER_LOCATION);
}
p.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY,
new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent pce) {
PREFERRED_DIVIDER_LOCATION = p.getDividerLocation();
}
});
result = p;
} else {
JPanel p = new JPanel();
p.setLayout(new GridBagLayout());
p.setBackground(UIUtilities.BACKGROUND_COLOR);
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.NORTHWEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 0;
c.gridy = 0;
c.weightx = 1;
c.weighty = 1;
c.fill = GridBagConstraints.BOTH;
p.add(graphicsPane,c);
c.gridy++;
c.weighty = 0;
c.insets = new Insets(10, 0, 0, 0);
c.fill = GridBagConstraints.HORIZONTAL;
p.add(buildControlsPane(), c);
result = new JScrollPane(p);
}
result.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
result.setBackground(UIUtilities.BACKGROUND_COLOR);
return result;
}
/**
* Builds and lays out the component displaying the preview.
*
* @return See above.
*/
private JPanel buildViewerPane()
{
JPanel p = new JPanel();
p.setBackground(UIUtilities.BACKGROUND_COLOR);
p.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.NORTHWEST;
c.insets = new Insets(2, 2, 2, 2);
c.gridy = 0;
c.gridx = 0;
c.weightx = 1;
c.weighty = 0;
c.gridwidth = GridBagConstraints.REMAINDER;
p.add(openButton, c);
c.gridy++;
c.fill = GridBagConstraints.VERTICAL;
c.gridwidth = 1;
c.weightx = 0;
c.weighty = 1;
p.add(zSlider, c);
c.gridx++;
c.fill = GridBagConstraints.BOTH;
c.weightx = 1;
c.weighty = 1;
c.insets = new Insets(5, 5, 5, 5);
p.add(canvas, c);
c.gridy++;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridwidth = GridBagConstraints.REMAINDER;
c.insets = new Insets(2, 2, 2, 2);
c.weightx = 1;
c.weighty = 0;
if (tSlider.isVisible()) {
p.add(tSlider, c);
c.gridy++;
}
if (lifetimeSlider != null) {
p.add(lifetimeSlider, c);
}
return p;
}
/**
* Sets the value of the specified slider when a propertyChange has been
* fired.
*
* @param slider The slider to update.
* @param v The selected value.
*/
private void updateSlider(JSlider slider, int v)
{
slider.removeChangeListener(this);
slider.setValue(v);
slider.addChangeListener(this);
graphicsPane.setSelectedPlane();
}
/**
* Lays out the slider and its corresponding text area.
*
* @param slider The slider to lay out.
* @param field The text area.
* @return A panel hosting the component.
*/
private JPanel buildSliderPane(JSlider slider, JTextField field)
{
JPanel p = new JPanel();
p.setBackground(UIUtilities.BACKGROUND_COLOR);
p.add(slider);
p.add(field);
return UIUtilities.buildComponentPanel(p);
}
/**
* Adds the specified component to the passed <code>Panel</code>.
*
* @param c The layout constraints for the component to be added.
* @param l The text corresponding to the component to be added.
* @param comp The component to be added.
* @param p The panel the component is added to.
*/
private void addComponent(GridBagConstraints c, String l, JComponent comp,
JPanel p)
{
c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last
c.fill = GridBagConstraints.NONE;
c.weightx = 0.0;
c.gridx = 0;
if (l != null && l.length() > 0) {
p.add(new JLabel(l), c);
c.gridx++;
p.add(Box.createHorizontalStrut(5), c);
c.gridx++;
}
c.gridwidth = GridBagConstraints.REMAINDER; //end row
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1.0;
p.add(comp, c);
}
/**
* Builds the pane hosting the main rendering controls.
*
* @return See above.
*/
private JPanel buildControlsPane()
{
JPanel p = new JPanel();
p.setBorder(BorderFactory.createTitledBorder(ADVANCED_OPTIONS));
p.setBackground(UIUtilities.BACKGROUND_COLOR);
p.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.WEST;
c.insets = new Insets(0, 2, 2, 0);
c.gridy = 0;
JPanel comp;
comp = buildSliderPane(bitDepthSlider, bitDepthLabel);
comp.setBackground(UIUtilities.BACKGROUND_COLOR);
addComponent(c, "Brightness", graphicsPane.getCodomainSlider(), p);
c.gridy++;
comp = buildSliderPane(bitDepthSlider, bitDepthLabel);
comp.setBackground(UIUtilities.BACKGROUND_COLOR);
addComponent(c, "Bit Depth", comp, p);
c.gridy++;
addComponent(c, "", noiseReduction, p);
c.gridx = 0;
c.gridy++;
comp = new SeparatorPane();
comp.setBackground(UIUtilities.BACKGROUND_COLOR);
p.add(comp, c);
c.gridy++;
if (channelsBox.isVisible()) {
comp = UIUtilities.buildComponentPanel(channelsBox);
comp.setBackground(UIUtilities.BACKGROUND_COLOR);
addComponent(c, "Channel", comp, p);
c.gridy++;
}
comp = UIUtilities.buildComponentPanel(familyBox);
comp.setBackground(UIUtilities.BACKGROUND_COLOR);
addComponent(c, "Map", comp, p);
c.gridy++;
comp = buildSliderPane(gammaSlider, gammaLabel);
comp.setBackground(UIUtilities.BACKGROUND_COLOR);
addComponent(c, "Gamma", comp, p);
return p;
}
/** Builds and lays out the UI. */
private void buildGUI()
{
setBackground(UIUtilities.BACKGROUND_COLOR);
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
c.weightx = 1;
c.weighty = 1;
c.fill = GridBagConstraints.BOTH;
add(buildChannelGraphicsPanel(), c);
}
/**
* Returns the bit resolution corresponding to the UI value.
*
* @param uiValue The UI value to convert.
* @return See above.
*/
private int convertUIBitResolution(int uiValue)
{
switch (uiValue) {
case 1: return RendererModel.DEPTH_1BIT;
case 2: return RendererModel.DEPTH_2BIT;
case 3: return RendererModel.DEPTH_3BIT;
case 4: return RendererModel.DEPTH_4BIT;
case 5: return RendererModel.DEPTH_5BIT;
case 6: return RendererModel.DEPTH_6BIT;
case 7: return RendererModel.DEPTH_7BIT;
case 8:
default: return RendererModel.DEPTH_8BIT;
}
}
/**
* Converts the bit resolution value into its corresponding UI value.
*
* @param value The value to convert.
* @return See above.
*/
private int convertBitResolution(int value)
{
switch (value) {
case RendererModel.DEPTH_1BIT: return 1;
case RendererModel.DEPTH_2BIT: return 2;
case RendererModel.DEPTH_3BIT: return 3;
case RendererModel.DEPTH_4BIT: return 4;
case RendererModel.DEPTH_5BIT: return 5;
case RendererModel.DEPTH_6BIT: return 6;
case RendererModel.DEPTH_7BIT: return 7;
case RendererModel.DEPTH_8BIT:
default: return 8;
}
}
/**
* Resets the value of the gamma slider and the gamma label.
*
* @param k The value to set.
*/
private void resetGamma(double k)
{
gammaSlider.removeChangeListener(this);
gammaSlider.setValue((int) (k*FACTOR));
gammaSlider.addChangeListener(this);
gammaLabel.setText(""+k);
}
/**
* Handles the event when the wheel is moved over the
* {@link #lifetimeSlider}.
*
* @param e The event to handle.
*/
private void mouseWheelMovedLifetime(MouseWheelEvent e)
{
boolean up = true;
if (e.getWheelRotation() > 0) up = false;
if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
int v = model.getRealSelectedT()-e.getWheelRotation();
int bin = lifetimeSlider.getValue();
if (up) {
if (v <= model.getMaxLifetimeBin()) {
controller.setSelectedXYPlane(model.getDefaultZ(),
model.getRealSelectedT(), bin);
}
} else { //moving down
if (v >= 0)
controller.setSelectedXYPlane(model.getDefaultZ(),
model.getRealSelectedT(), bin);
}
} else {
}
}
/**
* Returns the name of the component.
* @see ControlPane#getPaneName()
*/
protected String getPaneName() { return "Mapping"; }
/**
* Returns the icon attached to the component.
* @see ControlPane#getPaneIcon()
*/
protected Icon getPaneIcon()
{
IconManager icons = IconManager.getInstance();
return icons.getIcon(IconManager.DOMAIN);
}
/**
* Returns the brief description of the component.
* @see ControlPane#getPaneDescription()
*/
protected String getPaneDescription()
{
return "Define the mapping context for the pixels intensity values.";
}
/**
* Returns the index of the component.
* @see ControlPane#getPaneIndex()
*/
protected int getPaneIndex() { return ControlPane.DOMAIN_PANE_INDEX; }
/**
* Resets the default rendering settings.
* @see ControlPane#resetDefaultRndSettings()
*/
protected void resetDefaultRndSettings()
{
setInputInterval();
setInputRange(false);
setSelectedChannel();
setCodomainInterval();
resetBitResolution();
int n = model.getMaxC();
for (int i = 0; i < n; i++) {
setChannelColor(i);
}
resetGamma(model.getCurveCoefficient());
setZSection(model.getDefaultZ());
setTimepoint(model.getDefaultT());
setGreyScale(model.isGreyScale());
}
/**
* Resets the settings.
*
* @param settings the value to set.
*/
void resetViewedBy(RndProxyDef settings)
{
graphicsPane.resetViewedBy(settings);
}
/**
* Sets the enabled flag of the UI components.
* @see ControlPane#onStateChange(boolean)
*/
protected void onStateChange(boolean b)
{
if (familyBox != null) familyBox.setEnabled(b);
if (gammaSlider != null) {
String family = model.getFamily();
boolean enabled = family.equals(RendererModel.EXPONENTIAL) ||
family.equals(RendererModel.POLYNOMIAL);
gammaSlider.setEnabled(enabled);
gammaLabel.setEnabled(enabled);
}
if (bitDepthSlider != null) bitDepthSlider.setEnabled(b);
if (noiseReduction != null) noiseReduction.setEnabled(b);
if (channelList != null) {
Iterator<ChannelButton> i = channelList.iterator();
while (i.hasNext())
(i.next()).setEnabled(b);
}
graphicsPane.onStateChange(b);
}
/**
* Resets the value of the various controls when the user selects
* a new rendering control
* @see ControlPane#resetDefaultRndSettings()
*/
protected void switchRndControl() {}
/**
* Creates a new instance.
*
* @param model Reference to the Model.
* Mustn't be <code>null</code>.
* @param controller Reference to the Control.
* Mustn't be <code>null</code>.
*/
DomainPane(RendererModel model, RendererControl controller)
{
super(model, controller);
initComponents();
buildGUI();
}
/**
* Modifies the rendering controls depending on the currently selected
* channel.
*/
void setSelectedChannel()
{
graphicsPane.setSelectedChannel();
Iterator<ChannelButton> i = channelList.iterator();
ChannelButton btn;
List<Integer> active = model.getActiveChannels();
if (active == null) return;
int index;
int c = model.getSelectedChannel();
while (i.hasNext()) {
btn = i.next();
index = btn.getChannelIndex();
btn.setSelected(active.contains(index));
if (index == c && !model.isGeneralIndex())
btn.setBorder(SELECTION_BORDER);
btn.setColor(model.getChannelColor(index));
}
}
/** Sets the pixels intensity interval. */
void setInputInterval() { graphicsPane.setInputInterval(); }
/**
* Modifies the input range of the channel sliders.
*
* @param absolute Pass <code>true</code> to set it to the absolute value,
* <code>false</code> to the minimum and maximum.
*/
void setInputRange(boolean booleanValue)
{
graphicsPane.setInputRange(booleanValue);
}
/** Sets the value of the codomain interval. */
void setCodomainInterval() { graphicsPane.setCodomainInterval(); }
/**
* Sets the color of the passed channel.
*
* @param index The index of the channel.
*/
void setChannelColor(int index)
{
Iterator<ChannelButton> i = channelList.iterator();
ChannelButton btn;
while (i.hasNext()) {
btn = i.next();
if (index == btn.getChannelIndex()) {
btn.setColor(model.getChannelColor(index));
}
}
graphicsPane.setChannelColor(index);
if (channelsBox != null) populateChannels();
}
/** Toggles between color model and Greyscale. */
void setColorModelChanged()
{
ChannelButton btn;
int index;
int selected = model.getSelectedChannel();
for (int i = 0 ; i < channelList.size() ; i++) {
btn = channelList.get(i);
index = btn.getChannelIndex();
btn.setColor(model.getChannelColor(index));
btn.setSelected(model.isChannelActive(index));
if (index == selected && !model.isGeneralIndex())
btn.setBorder(SELECTION_BORDER);
}
graphicsPane.setColorModelChanged();
}
/**
* Updates the UI when a new curve is selected i.e. when a new family
* is selected or when a new gamma value is selected.
*/
void onCurveChange()
{
String f = model.getFamily();
boolean b = !(f.equals(RendererModel.LINEAR) ||
f.equals(RendererModel.LOGARITHMIC));
double k = 1;
if (b) k = model.getCurveCoefficient();
resetGamma(k);
gammaSlider.setEnabled(b);
gammaLabel.setEnabled(b);
graphicsPane.onCurveChange();
}
/**
* Updates UI components when a new time-point is selected.
*
* @param t The selected time-point.
*/
void setTimepoint(int t)
{
if (tSlider != null) updateSlider(tSlider, t);
}
/**
* Updates UI components when a new z-section is selected.
*
* @param z The selected z-section.
*/
void setZSection(int z)
{
if (zSlider != null) updateSlider(zSlider, z);
}
/**
* Update the UI components when the color model has changed
* @param b Pass <code>true</code> if color model is greyscale
*/
void setGreyScale(boolean b) {
graphicsPane.updateGreyScale(b);
}
/**
* Returns <code>true</code> if the passed object is one of the
* channel buttons, <code>false</code> otherwise.
*
* @param source The object to handle.
* @return See above.
*/
boolean isSourceDisplayed(Object source)
{
Iterator<ChannelButton> i = channelList.iterator();
while (i.hasNext()) {
if (i.next() == source) return true;
}
return false;
}
/** Renders and displays the rendered image in the preview. */
void renderPreview()
{
if (canvas == null) return;
BufferedImage img = model.renderImage();
canvas.setImage(img);
}
/**
* Builds and lays out the images as seen by other experimenters.
*
* @param results The thumbnails to lay out.
* @param activeRndDef The rendering setting which is currently used
*/
void displayViewedBy(List<ViewedByItem> results, RndProxyDef activeRndDef)
{
graphicsPane.displayViewedBy(results, activeRndDef);
}
/**
* Updates the component displaying the channels' details after update.
*/
void onChannelUpdated()
{
populateChannels();
Iterator<ChannelButton> i = channelList.iterator();
ChannelButton cb;
List<ChannelData> channels = model.getChannelData();
ChannelData data;
while (i.hasNext()) {
cb = i.next();
data = channels.get(cb.getChannelIndex());
cb.setText(data.getChannelLabeling());
}
}
/**
* Returns the selected rendering settings if any.
*
* @return See above.
*/
RndProxyDef getSelectedDef()
{
return graphicsPane.getSelectedDef();
}
/**
* Depending on the source of the event. Sets the gamma value or
* the bit resolution.
* @see ChangeListener#stateChanged(ChangeEvent)
*/
public void stateChanged(ChangeEvent e)
{
Object source = e.getSource();
if (source.equals(gammaSlider)) {
gammaLabel.setText(""+(double) gammaSlider.getValue()/FACTOR);
} else if (source.equals(bitDepthSlider)) {
bitDepthLabel.setText(""+
convertUIBitResolution(bitDepthSlider.getValue()));
} else if (source.equals(tSlider) || source.equals(zSlider)) {
if (lifetimeSlider != null && lifetimeSlider.isVisible()) {
controller.setSelectedXYPlane(zSlider.getValue(),
tSlider.getValue(), lifetimeSlider.getValue());
} else controller.setSelectedXYPlane(zSlider.getValue(),
tSlider.getValue());
} else if (source.equals(lifetimeSlider)) {
controller.setSelectedXYPlane(model.getDefaultZ(),
model.getRealSelectedT(), lifetimeSlider.getValue());
graphicsPane.setSelectedPlane();
}
}
/**
* Reacts to family or channel selection.
* @see ActionListener#actionPerformed(ActionEvent)
*/
public void actionPerformed(ActionEvent e)
{
int index = -1;
index = Integer.parseInt(e.getActionCommand());
int v = channelsBox.getSelectedIndex();
try {
switch (index) {
case FAMILY:
String f = (String)
((JComboBox) e.getSource()).getSelectedItem();
controller.setChannelFamily(v, f);
break;
case CHANNEL:
//Set the family
String family = model.getFamily(v);
double coefficient = model.getCurveCoefficient(v);
familyBox.removeActionListener(this);
familyBox.setSelectedItem(family);
familyBox.addActionListener(this);
//set the gamma.
boolean enabled = family.equals(RendererModel.EXPONENTIAL) ||
family.equals(RendererModel.POLYNOMIAL);
gammaSlider.removeChangeListener(this);
gammaSlider.setValue((int) (coefficient*FACTOR));
gammaSlider.setEnabled(enabled);
gammaSlider.addChangeListener(this);
gammaLabel.setText(""+coefficient);
gammaLabel.setEnabled(enabled);
controller.setChannelSelection(v, true);
}
} catch(NumberFormatException nfe) {
throw new Error("Invalid Action ID "+index, nfe);
}
}
/**
* Reacts to wheels moved event related to the {@link #zSlider} and
* {@link #tSlider}.
* @see MouseWheelListener#mouseWheelMoved(MouseWheelEvent)
*/
public void mouseWheelMoved(MouseWheelEvent e)
{
Object source = e.getSource();
if (source == zSlider && zSlider.isEnabled()) mouseWheelMovedZ(e);
else if (source == tSlider && tSlider.isEnabled())
mouseWheelMovedT(e);
else if (source == lifetimeSlider && lifetimeSlider.isEnabled())
mouseWheelMovedLifetime(e);
}
}