/*
* Copyright 2010-2015 Institut Pasteur.
*
* This file is part of Icy.
*
* Icy 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 3 of the License, or
* (at your option) any later version.
*
* Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>.
*/
package icy.gui.sequence.tools;
import icy.gui.component.button.IcyButton;
import icy.gui.component.sequence.SequenceChooser;
import icy.gui.component.sequence.SequencePreviewPanel;
import icy.gui.dialog.MessageDialog;
import icy.resource.ResourceUtil;
import icy.resource.icon.IcyIcon;
import icy.sequence.DimensionId;
import icy.sequence.Sequence;
import icy.sequence.SequenceModel;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultListModel;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
/**
* Frame for dimension merge operation.
*
* @author Stephane
*/
public class SequenceDimensionMergePanel extends JPanel
{
static class SequenceChannelEntry
{
final Sequence sequence;
final int c;
/**
* @param sequence
* @param c
*/
public SequenceChannelEntry(Sequence sequence, int c)
{
super();
this.sequence = sequence;
this.c = c;
}
public SequenceChannelEntry(Sequence sequence)
{
this(sequence, -1);
}
@Override
public String toString()
{
if (c == -1)
return sequence.toString();
return sequence.toString() + " [channel " + c + "]";
}
}
/**
*
*/
private static final long serialVersionUID = -5908902915282090447L;
// GUI
protected IcyButton addButton;
protected IcyButton removeButton;
protected IcyButton upButton;
protected IcyButton downButton;
protected JList sequenceList;
protected SequenceChooser sequenceChooser;
protected SequencePreviewPanel sequencePreview;
protected JCheckBox interlaceCheckBox;
protected JCheckBox fillEmptyImageCheckBox;
protected JCheckBox fitCheckbox;
private JLabel bottomArrowLabel;
private JLabel dimLabel;
// internals
protected DefaultListModel listModel;
protected ListSelectionModel selectionModel;
protected final DimensionId dim;
/**
* Create the panel.
*/
public SequenceDimensionMergePanel(DimensionId dim)
{
super();
this.dim = dim;
listModel = new DefaultListModel();
initialize();
selectionModel = sequenceList.getSelectionModel();
selectionModel.addListSelectionListener(new ListSelectionListener()
{
@Override
public void valueChanged(ListSelectionEvent e)
{
refreshButtonsState();
}
});
interlaceCheckBox.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
fireChangedEvent();
previewImageChanged();
}
});
fillEmptyImageCheckBox.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
fireChangedEvent();
previewImageChanged();
}
});
fitCheckbox.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
fireChangedEvent();
previewImageChanged();
}
});
addButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
final Sequence seq = sequenceChooser.getSelectedSequence();
if (seq != null)
{
if (checkSequenceIsCompatible(seq, true, true))
{
if (SequenceDimensionMergePanel.this.dim == DimensionId.C)
{
// add per channel
for (int c = 0; c < seq.getSizeC(); c++)
listModel.addElement(new SequenceChannelEntry(seq, c));
}
else
listModel.addElement(new SequenceChannelEntry(seq));
refreshButtonsState();
fireChangedEvent();
previewDimensionChanged();
}
}
}
});
removeButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
listModel.remove(selectionModel.getMinSelectionIndex());
refreshButtonsState();
fireChangedEvent();
previewDimensionChanged();
}
});
upButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
final int index = selectionModel.getMinSelectionIndex();
// exchange index and (index - 1)
final Object obj = listModel.getElementAt(index - 1);
listModel.set(index - 1, listModel.getElementAt(index));
listModel.set(index, obj);
selectionModel.setSelectionInterval(index - 1, index - 1);
refreshButtonsState();
fireChangedEvent();
previewImageChanged();
}
});
downButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
final int index = selectionModel.getMinSelectionIndex();
// exchange index and (index + 1)
final Object obj = listModel.getElementAt(index + 1);
listModel.set(index + 1, listModel.getElementAt(index));
listModel.set(index, obj);
selectionModel.setSelectionInterval(index + 1, index + 1);
refreshButtonsState();
fireChangedEvent();
previewImageChanged();
}
});
dimLabel.setText(dim.toString());
IcyIcon icon = new IcyIcon(ResourceUtil.ICON_ARROW_DOWN);
icon.setDimension(new Dimension(20, 60));
bottomArrowLabel.setIcon(icon);
// interlace not available for channel merge operation
interlaceCheckBox.setVisible(dim != DimensionId.C);
// fillEmptyImageCheckBox.setVisible(false);
refreshButtonsState();
}
private void initialize()
{
GridBagLayout gridBagLayout = new GridBagLayout();
gridBagLayout.columnWidths = new int[] {24, 80, 140, 100, 0, 0};
gridBagLayout.rowHeights = new int[] {0, 26, 0, 0, 0, 0, 0, 174, 0};
gridBagLayout.columnWeights = new double[] {0.0, 1.0, 1.0, 1.0, 0.0, Double.MIN_VALUE};
gridBagLayout.rowWeights = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, Double.MIN_VALUE};
setLayout(gridBagLayout);
JLabel lblSelectSequenceTo = new JLabel("Add sequence to merge in the list :");
GridBagConstraints gbc_lblSelectSequenceTo = new GridBagConstraints();
gbc_lblSelectSequenceTo.fill = GridBagConstraints.BOTH;
gbc_lblSelectSequenceTo.gridwidth = 4;
gbc_lblSelectSequenceTo.insets = new Insets(0, 0, 5, 5);
gbc_lblSelectSequenceTo.gridx = 0;
gbc_lblSelectSequenceTo.gridy = 0;
add(lblSelectSequenceTo, gbc_lblSelectSequenceTo);
sequenceChooser = new SequenceChooser();
GridBagConstraints gbc_sequenceChooser = new GridBagConstraints();
gbc_sequenceChooser.gridwidth = 4;
gbc_sequenceChooser.insets = new Insets(0, 0, 5, 5);
gbc_sequenceChooser.fill = GridBagConstraints.BOTH;
gbc_sequenceChooser.gridx = 0;
gbc_sequenceChooser.gridy = 1;
add(sequenceChooser, gbc_sequenceChooser);
addButton = new IcyButton(new IcyIcon(ResourceUtil.ICON_ROUND_PLUS));
addButton.setToolTipText("Add selected sequence to the list.");
addButton.setFlat(true);
GridBagConstraints gbc_addButton = new GridBagConstraints();
gbc_addButton.fill = GridBagConstraints.BOTH;
gbc_addButton.insets = new Insets(0, 0, 5, 0);
gbc_addButton.gridx = 4;
gbc_addButton.gridy = 1;
add(addButton, gbc_addButton);
dimLabel = new JLabel("Z");
dimLabel.setHorizontalAlignment(SwingConstants.CENTER);
dimLabel.setFont(new Font("Tahoma", Font.BOLD, 14));
GridBagConstraints gbc_dimLabel = new GridBagConstraints();
gbc_dimLabel.fill = GridBagConstraints.HORIZONTAL;
gbc_dimLabel.anchor = GridBagConstraints.BASELINE;
gbc_dimLabel.insets = new Insets(0, 0, 5, 5);
gbc_dimLabel.gridx = 0;
gbc_dimLabel.gridy = 2;
add(dimLabel, gbc_dimLabel);
JScrollPane scrollPane = new JScrollPane();
GridBagConstraints gbc_scrollPane = new GridBagConstraints();
gbc_scrollPane.gridwidth = 3;
gbc_scrollPane.fill = GridBagConstraints.BOTH;
gbc_scrollPane.gridheight = 4;
gbc_scrollPane.insets = new Insets(0, 0, 5, 5);
gbc_scrollPane.gridx = 1;
gbc_scrollPane.gridy = 2;
add(scrollPane, gbc_scrollPane);
sequenceList = new JList(listModel);
scrollPane.setViewportView(sequenceList);
sequenceList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
removeButton = new IcyButton(new IcyIcon(ResourceUtil.ICON_ROUND_MINUS));
removeButton.setToolTipText("Remove selected sequence from the list.");
removeButton.setFlat(true);
GridBagConstraints gbc_removeButton = new GridBagConstraints();
gbc_removeButton.fill = GridBagConstraints.BOTH;
gbc_removeButton.insets = new Insets(0, 0, 5, 0);
gbc_removeButton.gridx = 4;
gbc_removeButton.gridy = 2;
add(removeButton, gbc_removeButton);
bottomArrowLabel = new JLabel("");
bottomArrowLabel.setHorizontalAlignment(SwingConstants.CENTER);
GridBagConstraints gbc_bottomArrowLabel = new GridBagConstraints();
gbc_bottomArrowLabel.gridheight = 3;
gbc_bottomArrowLabel.insets = new Insets(0, 0, 5, 5);
gbc_bottomArrowLabel.gridx = 0;
gbc_bottomArrowLabel.gridy = 3;
add(bottomArrowLabel, gbc_bottomArrowLabel);
upButton = new IcyButton(new IcyIcon(ResourceUtil.ICON_ROUND_ARROW_UP));
upButton.setToolTipText("Move up selected sequence.");
upButton.setFlat(true);
GridBagConstraints gbc_upButton = new GridBagConstraints();
gbc_upButton.fill = GridBagConstraints.BOTH;
gbc_upButton.insets = new Insets(0, 0, 5, 0);
gbc_upButton.gridx = 4;
gbc_upButton.gridy = 3;
add(upButton, gbc_upButton);
downButton = new IcyButton(new IcyIcon(ResourceUtil.ICON_ROUND_ARROW_DOWN));
downButton.setToolTipText("Move down selected sequence.");
downButton.setFlat(true);
GridBagConstraints gbc_downButton = new GridBagConstraints();
gbc_downButton.fill = GridBagConstraints.BOTH;
gbc_downButton.insets = new Insets(0, 0, 5, 0);
gbc_downButton.gridx = 4;
gbc_downButton.gridy = 4;
add(downButton, gbc_downButton);
fitCheckbox = new JCheckBox("Scale image");
fitCheckbox.setToolTipText("Scale all image to the largest one");
GridBagConstraints gbc_fitCheckbox = new GridBagConstraints();
gbc_fitCheckbox.anchor = GridBagConstraints.WEST;
gbc_fitCheckbox.gridwidth = 2;
gbc_fitCheckbox.insets = new Insets(0, 0, 5, 5);
gbc_fitCheckbox.gridx = 0;
gbc_fitCheckbox.gridy = 6;
add(fitCheckbox, gbc_fitCheckbox);
fillEmptyImageCheckBox = new JCheckBox("Fill empty image");
fillEmptyImageCheckBox.setToolTipText("Replace empty image by the previous non empty one");
GridBagConstraints gbc_noEmptyImageCheckBox = new GridBagConstraints();
gbc_noEmptyImageCheckBox.fill = GridBagConstraints.VERTICAL;
gbc_noEmptyImageCheckBox.insets = new Insets(0, 0, 5, 5);
gbc_noEmptyImageCheckBox.gridx = 2;
gbc_noEmptyImageCheckBox.gridy = 6;
add(fillEmptyImageCheckBox, gbc_noEmptyImageCheckBox);
interlaceCheckBox = new JCheckBox("Interlace image");
interlaceCheckBox.setToolTipText("Interlace sequence image");
GridBagConstraints gbc_interlaceCheckBox = new GridBagConstraints();
gbc_interlaceCheckBox.anchor = GridBagConstraints.EAST;
gbc_interlaceCheckBox.gridwidth = 2;
gbc_interlaceCheckBox.fill = GridBagConstraints.VERTICAL;
gbc_interlaceCheckBox.insets = new Insets(0, 0, 5, 0);
gbc_interlaceCheckBox.gridx = 3;
gbc_interlaceCheckBox.gridy = 6;
add(interlaceCheckBox, gbc_interlaceCheckBox);
sequencePreview = new SequencePreviewPanel();
sequencePreview
.setBorder(new TitledBorder(null, "Preview", TitledBorder.LEADING, TitledBorder.TOP, null, null));
GridBagConstraints gbc_sequencePreview = new GridBagConstraints();
gbc_sequencePreview.gridwidth = 5;
gbc_sequencePreview.fill = GridBagConstraints.BOTH;
gbc_sequencePreview.gridx = 0;
gbc_sequencePreview.gridy = 7;
add(sequencePreview, gbc_sequencePreview);
}
public DimensionId getDimensionId()
{
return dim;
}
void refreshButtonsState()
{
final int index = selectionModel.getMinSelectionIndex();
final boolean notEmpty = index != -1;
final int size = listModel.getSize();
removeButton.setEnabled(notEmpty);
upButton.setEnabled(notEmpty && (index != 0));
downButton.setEnabled(notEmpty && (index != (size - 1)));
}
public int[] getSelectedChannels()
{
final int[] result = new int[listModel.size()];
for (int i = 0; i < listModel.getSize(); i++)
result[i] = ((SequenceChannelEntry) listModel.get(i)).c;
return result;
}
public Sequence[] getSequences()
{
final Sequence result[] = new Sequence[listModel.size()];
for (int i = 0; i < listModel.getSize(); i++)
result[i] = ((SequenceChannelEntry) listModel.get(i)).sequence;
return result;
}
boolean checkSequenceIsCompatible(Sequence seq, boolean showMessage, boolean showWarning)
{
boolean warningXYDone = false;
for (Sequence sequence : getSequences())
{
// first check for data type
if (!seq.getDataType_().equals(sequence.getDataType_()))
{
if (showMessage)
MessageDialog.showDialog("You cannot merge sequences with different data type.");
return false;
}
// We can remove all these verification as the merge algorithm take care of that !
// then depending dimension merge check for dimension equality
// switch (getDimensionId())
// {
// case C:
// if (seq.getSizeZ() != sequence.getSizeZ())
// {
// if (showMessage)
// MessageDialog.showDialog("You cannot merge channels from sequences with different Z size.");
//
// return false;
// }
// if (seq.getSizeT() != sequence.getSizeT())
// {
// if (showMessage)
// MessageDialog.showDialog("You cannot merge channels from sequences with different T size.");
//
// return false;
// }
// break;
//
// case Z:
// if (seq.getSizeC() != sequence.getSizeC())
// {
// if (showMessage)
// MessageDialog
// .showDialog("You cannot merge slices from sequences with different number of channel.");
//
// return false;
// }
// if (seq.getSizeT() != sequence.getSizeT())
// {
// if (showMessage)
// MessageDialog.showDialog("You cannot merge slices from sequences with different T size.");
//
// return false;
// }
// break;
//
// case T:
// if (seq.getSizeC() != sequence.getSizeC())
// {
// if (showMessage)
// MessageDialog.showDialog(
// "You cannot merge frames from sequences with different number of channel.",
// MessageDialog.PLAIN_MESSAGE);
//
// return false;
// }
// if (seq.getSizeZ() != sequence.getSizeZ())
// {
// if (showMessage)
// MessageDialog.showDialog("You cannot merge frames from sequences with different Z size.");
//
// return false;
// }
// break;
// }
// also consider the XY size
if (!isFitImagesEnabled())
{
if ((seq.getSizeX() != sequence.getSizeX()) || (seq.getSizeY() != sequence.getSizeY()))
{
if (showWarning && !warningXYDone)
{
MessageDialog
.showDialog(
"Sequences have different XY size !\nYou can enable the \"Scale image\" option to resize images if needed.",
MessageDialog.WARNING_MESSAGE);
warningXYDone = true;
}
}
}
}
return true;
}
/**
* @return the image provider
*/
public SequenceModel getModel()
{
return sequencePreview.getModel();
}
public void setModel(SequenceModel model)
{
sequencePreview.setModel(model);
}
public void previewDimensionChanged()
{
sequencePreview.dimensionChanged();
}
public void previewImageChanged()
{
sequencePreview.imageChanged();
}
public boolean isInterlaceEnabled()
{
return interlaceCheckBox.isVisible() && interlaceCheckBox.isSelected();
}
public boolean isFillEmptyImageEnabled()
{
return fillEmptyImageCheckBox.isVisible() && fillEmptyImageCheckBox.isSelected();
}
public boolean isFitImagesEnabled()
{
return fitCheckbox.isVisible() && fitCheckbox.isSelected();
}
public boolean isInterlaceVisible()
{
return interlaceCheckBox.isVisible();
}
public void setInterlaceVisible(boolean value)
{
interlaceCheckBox.setVisible(value);
}
public boolean isFillEmptyImageVisible()
{
return fillEmptyImageCheckBox.isVisible();
}
public void setFillEmptyImageVisible(boolean value)
{
fillEmptyImageCheckBox.setVisible(value);
}
protected void fireChangedEvent()
{
final ChangeEvent event = new ChangeEvent(SequenceDimensionMergePanel.this);
for (ChangeListener listener : getListeners(ChangeListener.class))
listener.stateChanged(event);
}
public void addChangeListener(ChangeListener listener)
{
listenerList.add(ChangeListener.class, listener);
}
public void removeChangeListener(ChangeListener listener)
{
listenerList.remove(ChangeListener.class, listener);
}
}