/* * 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); } }