001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as
007 * published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
008 * 
009 * Icy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
011 * 
012 * You should have received a copy of the GNU General Public License along with Icy. If not, see
013 * <http://www.gnu.org/licenses/>.
014 */
015package icy.gui.sequence.tools;
016
017import icy.gui.component.RangeComponent;
018import icy.gui.component.sequence.SequencePreviewPanel;
019import icy.sequence.DimensionId;
020import icy.sequence.SequenceModel;
021
022import java.awt.BorderLayout;
023import java.awt.Color;
024import java.awt.GridBagConstraints;
025import java.awt.GridBagLayout;
026import java.awt.Insets;
027import java.util.EventListener;
028
029import javax.swing.JLabel;
030import javax.swing.JPanel;
031import javax.swing.JSpinner;
032import javax.swing.SpinnerNumberModel;
033import javax.swing.SwingConstants;
034import javax.swing.UIManager;
035import javax.swing.border.TitledBorder;
036import javax.swing.event.ChangeEvent;
037import javax.swing.event.ChangeListener;
038
039public class SequenceDimensionAdjustPanel extends JPanel
040{
041    /**
042     * 
043     */
044    private static final long serialVersionUID = 5670426025630944193L;
045
046    public interface RangeChangeListener extends EventListener
047    {
048        public void rangeChanged();
049    }
050
051    protected DimensionId dim;
052
053    // GUI
054    protected SequencePreviewPanel sequencePreviewPanel;
055    protected RangeComponent range;
056    private JPanel extractionRulePanel;
057    protected JSpinner extractSpinner;
058    protected JSpinner loopSpinner;
059
060    // internal
061    double oldLow;
062    double oldHigh;
063
064    /**
065     * Create the panel.
066     */
067    public SequenceDimensionAdjustPanel(DimensionId dim)
068    {
069        super();
070
071        if ((dim != DimensionId.Z) && (dim != DimensionId.T))
072            throw new IllegalArgumentException("Only Z or T dimension allowed");
073
074        this.dim = dim;
075
076        initialize();
077
078        if (dim == DimensionId.Z)
079        {
080            sequencePreviewPanel.getMainPanel().setBorder(
081                    new TitledBorder(null, "Select Z interval", TitledBorder.LEADING, TitledBorder.TOP, null, null));
082            range = new RangeComponent(SwingConstants.VERTICAL);
083            range.setToolTipText("Select Z interval");
084        }
085        else
086        {
087            sequencePreviewPanel.getMainPanel().setBorder(
088                    new TitledBorder(null, "Select T interval", TitledBorder.LEADING, TitledBorder.TOP, null, null));
089            range = new RangeComponent(SwingConstants.HORIZONTAL);
090            range.setToolTipText("Select T interval");
091        }
092
093        range.setMinMaxStep(0, 0, 1);
094        range.setVisible(false);
095        range.addChangeListener(new ChangeListener()
096        {
097            @Override
098            public void stateChanged(ChangeEvent e)
099            {
100                final double low = range.getLow();
101                final double high = range.getHigh();
102
103                if (oldLow != low)
104                {
105                    // low value changed
106                    if (SequenceDimensionAdjustPanel.this.dim == DimensionId.Z)
107                        sequencePreviewPanel.setPositionZ((int) low);
108                    else
109                        sequencePreviewPanel.setPositionT((int) low);
110                }
111                else if (oldHigh != high)
112                {
113                    // high value changed
114                    if (SequenceDimensionAdjustPanel.this.dim == DimensionId.Z)
115                        sequencePreviewPanel.setPositionZ((int) high);
116                    else
117                        sequencePreviewPanel.setPositionT((int) high);
118                }
119
120                oldLow = low;
121                oldHigh = high;
122
123                fireRangeChangedEvent();
124            }
125        });
126
127        if (dim == DimensionId.Z)
128        {
129            // replace z slider by z range component
130            final JPanel panel = sequencePreviewPanel.getZPanel();
131            panel.removeAll();
132            panel.setLayout(new BorderLayout());
133            panel.add(range, BorderLayout.CENTER);
134        }
135        else
136        {
137            // replace t slider by t range component
138            final JPanel panel = sequencePreviewPanel.getTPanel();
139            panel.removeAll();
140            panel.setLayout(new BorderLayout());
141            panel.add(range, BorderLayout.CENTER);
142        }
143
144        extractSpinner.addChangeListener(new ChangeListener()
145        {
146            @Override
147            public void stateChanged(ChangeEvent e)
148            {
149                final SpinnerNumberModel model = (SpinnerNumberModel) loopSpinner.getModel();
150                final int currentValue = ((Integer) model.getValue()).intValue();
151                final int min = getExtractValue();
152
153                // adjust minimum value
154                loopSpinner.setModel(new SpinnerNumberModel(Math.max(min, currentValue), min, (int) range.getMax(), 1));
155
156                fireRangeChangedEvent();
157            }
158        });
159        loopSpinner.addChangeListener(new ChangeListener()
160        {
161            @Override
162            public void stateChanged(ChangeEvent e)
163            {
164                fireRangeChangedEvent();
165            }
166        });
167    }
168
169    private void initialize()
170    {
171        setLayout(new BorderLayout(0, 0));
172
173        sequencePreviewPanel = new SequencePreviewPanel();
174        add(sequencePreviewPanel, BorderLayout.CENTER);
175
176        extractionRulePanel = new JPanel();
177        extractionRulePanel.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Extraction rule",
178                TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
179        add(extractionRulePanel, BorderLayout.SOUTH);
180        GridBagLayout gbl_extractionRulePanel = new GridBagLayout();
181        gbl_extractionRulePanel.columnWidths = new int[] {0, 0, 0, 0, 0, 0};
182        gbl_extractionRulePanel.rowHeights = new int[] {20, 0};
183        gbl_extractionRulePanel.columnWeights = new double[] {0.0, 1.0, 0.0, 1.0, 2.0, Double.MIN_VALUE};
184        gbl_extractionRulePanel.rowWeights = new double[] {0.0, Double.MIN_VALUE};
185        extractionRulePanel.setLayout(gbl_extractionRulePanel);
186
187        JLabel lblKeep = new JLabel("Keep");
188        GridBagConstraints gbc_lblKeep = new GridBagConstraints();
189        gbc_lblKeep.anchor = GridBagConstraints.EAST;
190        gbc_lblKeep.insets = new Insets(0, 0, 0, 5);
191        gbc_lblKeep.gridx = 0;
192        gbc_lblKeep.gridy = 0;
193        extractionRulePanel.add(lblKeep, gbc_lblKeep);
194
195        extractSpinner = new JSpinner();
196        extractSpinner.setModel(new SpinnerNumberModel(new Integer(1), new Integer(1), null, new Integer(1)));
197        extractSpinner
198                .setToolTipText("Number of frames to keep every N frames. 'Keep 1 every 2' means keep every other frame.");
199        GridBagConstraints gbc_extractSpinner = new GridBagConstraints();
200        gbc_extractSpinner.fill = GridBagConstraints.HORIZONTAL;
201        gbc_extractSpinner.anchor = GridBagConstraints.NORTH;
202        gbc_extractSpinner.insets = new Insets(0, 0, 0, 5);
203        gbc_extractSpinner.gridx = 1;
204        gbc_extractSpinner.gridy = 0;
205        extractionRulePanel.add(extractSpinner, gbc_extractSpinner);
206
207        JLabel lblEvery = new JLabel("every");
208        GridBagConstraints gbc_lblEvery = new GridBagConstraints();
209        gbc_lblEvery.anchor = GridBagConstraints.EAST;
210        gbc_lblEvery.insets = new Insets(0, 0, 0, 5);
211        gbc_lblEvery.gridx = 2;
212        gbc_lblEvery.gridy = 0;
213        extractionRulePanel.add(lblEvery, gbc_lblEvery);
214
215        loopSpinner = new JSpinner();
216        loopSpinner.setModel(new SpinnerNumberModel(new Integer(1), new Integer(1), null, new Integer(1)));
217        loopSpinner.setToolTipText("Size for the extraction rule loop.");
218        GridBagConstraints gbc_loopSpinner = new GridBagConstraints();
219        gbc_loopSpinner.fill = GridBagConstraints.HORIZONTAL;
220        gbc_loopSpinner.insets = new Insets(0, 0, 0, 5);
221        gbc_loopSpinner.anchor = GridBagConstraints.NORTH;
222        gbc_loopSpinner.gridx = 3;
223        gbc_loopSpinner.gridy = 0;
224        extractionRulePanel.add(loopSpinner, gbc_loopSpinner);
225    }
226
227    public DimensionId getDimensionId()
228    {
229        return dim;
230    }
231
232    public boolean isPreviewVisible()
233    {
234        return sequencePreviewPanel.isVisible();
235    }
236
237    public void setPreviewVisible(boolean value)
238    {
239        sequencePreviewPanel.setVisible(value);
240    }
241
242    private void setMax(int value)
243    {
244        range.setMinMaxStep(0, value, 1);
245        range.setLowHigh(0, value);
246        range.setVisible(value > 0);
247
248        extractSpinner.setModel(new SpinnerNumberModel(1, 1, Math.max(value, 1), 1));
249        loopSpinner.setModel(new SpinnerNumberModel(1, 1, Math.max(value, 1), 1));
250    }
251
252    public SequenceModel getModel()
253    {
254        return sequencePreviewPanel.getModel();
255
256    }
257
258    public void setModel(SequenceModel model)
259    {
260        if (getModel() != model)
261        {
262            sequencePreviewPanel.setModel(model);
263            dimensionChangedInternal();
264        }
265    }
266
267    public void dimensionChanged()
268    {
269        dimensionChangedInternal();
270        sequencePreviewPanel.dimensionChanged();
271    }
272
273    private void dimensionChangedInternal()
274    {
275        final SequenceModel model = getModel();
276
277        if (model != null)
278        {
279            if (dim == DimensionId.Z)
280                setMax(Math.max(0, model.getSizeZ() - 1));
281            else
282                setMax(Math.max(0, model.getSizeT() - 1));
283        }
284        else
285            setMax(0);
286    }
287
288    public void imageChanged()
289    {
290        sequencePreviewPanel.imageChanged();
291    }
292
293    public int getRangeLow()
294    {
295        return (int) range.getLow();
296    }
297
298    public int getRangeHigh()
299    {
300        return (int) range.getHigh();
301    }
302
303    public boolean isIndexSelected(int index)
304    {
305        if (index < getRangeLow())
306            return false;
307        if (index > getRangeHigh())
308            return false;
309
310        return ((index - getRangeLow()) % getLoopValue()) < getExtractValue();
311    }
312
313    public int getExtractValue()
314    {
315        return ((Integer) extractSpinner.getValue()).intValue();
316    }
317
318    public int getLoopValue()
319    {
320        return ((Integer) loopSpinner.getValue()).intValue();
321    }
322
323    protected void fireRangeChangedEvent()
324    {
325        for (RangeChangeListener listener : getListeners(RangeChangeListener.class))
326            listener.rangeChanged();
327    }
328
329    public void addRangeChangeListener(RangeChangeListener listener)
330    {
331        listenerList.add(RangeChangeListener.class, listener);
332    }
333
334    public void removeRangeChangeListener(RangeChangeListener listener)
335    {
336        listenerList.remove(RangeChangeListener.class, listener);
337    }
338}