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
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.gui.component.sequence;
020
021import icy.image.IcyBufferedImage;
022import icy.image.IcyBufferedImageUtil;
023import icy.sequence.SequenceModel;
024import icy.sequence.SequenceModel.SequenceModelListener;
025import icy.system.thread.ThreadUtil;
026import icy.util.GraphicsUtil;
027import icy.util.StringUtil;
028
029import java.awt.BorderLayout;
030import java.awt.Dimension;
031import java.awt.Font;
032import java.awt.Graphics;
033import java.awt.Graphics2D;
034import java.awt.GridBagConstraints;
035import java.awt.GridBagLayout;
036import java.awt.Insets;
037import java.awt.RenderingHints;
038import java.awt.image.BufferedImage;
039
040import javax.swing.JLabel;
041import javax.swing.JPanel;
042import javax.swing.JSlider;
043import javax.swing.SwingConstants;
044import javax.swing.border.EmptyBorder;
045import javax.swing.event.ChangeEvent;
046import javax.swing.event.ChangeListener;
047
048public class SequencePreviewPanel extends JPanel implements ChangeListener, SequenceModelListener
049{
050    /**
051     * 
052     */
053    private static final long serialVersionUID = 4985194381532600393L;
054
055    private class CustomPanel extends JPanel implements Runnable
056    {
057        /**
058         * 
059         */
060        private static final long serialVersionUID = 6307431557815572470L;
061
062        private BufferedImage cache;
063
064        public CustomPanel()
065        {
066            super();
067
068            cache = null;
069        }
070
071        @Override
072        protected void paintComponent(Graphics g)
073        {
074            super.paintComponent(g);
075
076            final int w = getWidth();
077            final int h = getHeight();
078            final Graphics2D g2 = (Graphics2D) g.create();
079
080            g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
081            g2.setFont(g2.getFont().deriveFont(Font.BOLD, 22));
082
083            if (cache == null)
084                GraphicsUtil.drawCenteredString(g2, "No image", w / 2, h / 2, false);
085            else if ((w > 0) && (h > 0))
086            {
087                final int sw = getSizeX();
088                final int sh = getSizeY();
089                final int iw = cache.getWidth();
090                final int ih = cache.getHeight();
091                final int fiw;
092                final int fih;
093
094                if (fitToView)
095                {
096                    final double ratio1 = Math.max((double) iw / (double) sw, (double) ih / (double) sh);
097                    final double ratio2 = Math.max((double) sw / (double) w, (double) sh / (double) h);
098
099                    fiw = (int) (iw / (ratio1 * ratio2));
100                    fih = (int) (ih / (ratio1 * ratio2));
101                }
102                else
103                {
104                    final double ratio = Math.max((double) sw / (double) w, (double) sh / (double) h);
105
106                    fiw = (int) (iw / ratio);
107                    fih = (int) (ih / ratio);
108                }
109
110                g2.drawImage(cache, (w - fiw) / 2, (h - fih) / 2, fiw, fih, null);
111            }
112
113            g2.dispose();
114        }
115
116        public void imageChanged()
117        {
118            // request rebuild cache
119            ThreadUtil.runSingle(this);
120        }
121
122        int getSizeX()
123        {
124            if (model != null)
125                return model.getSizeX();
126            if (cache != null)
127                return cache.getWidth();
128            return 0;
129        }
130
131        int getSizeY()
132        {
133            if (model != null)
134                return model.getSizeY();
135            if (cache != null)
136                return cache.getHeight();
137            return 0;
138        }
139
140        @Override
141        public void run()
142        {
143            // rebuild cache and repaint
144            final BufferedImage img = getImage();
145
146            if (img instanceof IcyBufferedImage)
147                cache = IcyBufferedImageUtil.toBufferedImage((IcyBufferedImage) img, BufferedImage.TYPE_INT_ARGB);
148            else
149                cache = img;
150
151            repaint();
152        }
153    }
154
155    protected boolean autoHideSliders;
156    protected boolean fitToView;
157
158    protected JSlider tSlider;
159    protected JSlider zSlider;
160    protected CustomPanel imagePanel;
161    protected SequenceModel model;
162    protected JLabel titleLabel;
163    protected JPanel mainPanel;
164    protected JPanel zPanel;
165    protected JPanel tPanel;
166    protected JLabel lblZ;
167    protected JLabel lblT;
168    protected JLabel lblZValue;
169    protected JLabel lblTValue;
170
171    /**
172     * Create the panel.
173     */
174    public SequencePreviewPanel(String title, boolean autoHideSliders)
175    {
176        super();
177
178        this.autoHideSliders = autoHideSliders;
179        fitToView = true;
180
181        model = null;
182
183        initializeGui();
184
185        if (autoHideSliders)
186        {
187            zPanel.setVisible(false);
188            tPanel.setVisible(false);
189        }
190
191        setMaxZ(0);
192        setMaxT(0);
193
194        zSlider.addChangeListener(this);
195        tSlider.addChangeListener(this);
196
197        setTitle(title);
198
199        // setMinimumSize(new Dimension(320, 200));
200        setPreferredSize(new Dimension(280, 200));
201
202        validate();
203    }
204
205    /**
206     * Create the panel.
207     */
208    public SequencePreviewPanel(boolean autoHideSliders)
209    {
210        this(null, autoHideSliders);
211    }
212
213    /**
214     * Create the panel.
215     */
216    public SequencePreviewPanel(String title)
217    {
218        this(title, true);
219    }
220
221    /**
222     * Create the panel.
223     */
224    public SequencePreviewPanel()
225    {
226        this(null, true);
227    }
228
229    private void initializeGui()
230    {
231        setLayout(new BorderLayout(0, 0));
232
233        titleLabel = new JLabel("Title");
234        titleLabel.setBorder(new EmptyBorder(2, 0, 4, 0));
235        titleLabel.setFont(new Font("Tahoma", Font.BOLD, 13));
236        titleLabel.setHorizontalAlignment(SwingConstants.CENTER);
237        add(titleLabel, BorderLayout.NORTH);
238
239        mainPanel = new JPanel();
240        add(mainPanel, BorderLayout.CENTER);
241        GridBagLayout gbl_mainPanel = new GridBagLayout();
242        gbl_mainPanel.columnWidths = new int[] {0, 0, 0};
243        gbl_mainPanel.rowHeights = new int[] {0, 0, 0};
244        gbl_mainPanel.columnWeights = new double[] {0.0, 1.0, Double.MIN_VALUE};
245        gbl_mainPanel.rowWeights = new double[] {1.0, 0.0, Double.MIN_VALUE};
246        mainPanel.setLayout(gbl_mainPanel);
247
248        imagePanel = new CustomPanel();
249        GridBagConstraints gbc_imagePanel = new GridBagConstraints();
250        gbc_imagePanel.insets = new Insets(0, 0, 5, 5);
251        gbc_imagePanel.fill = GridBagConstraints.BOTH;
252        gbc_imagePanel.gridx = 1;
253        gbc_imagePanel.gridy = 0;
254        mainPanel.add(imagePanel, gbc_imagePanel);
255
256        zPanel = new JPanel();
257        GridBagConstraints gbc_zPanel = new GridBagConstraints();
258        gbc_zPanel.insets = new Insets(0, 0, 5, 5);
259        gbc_zPanel.fill = GridBagConstraints.BOTH;
260        gbc_zPanel.gridx = 0;
261        gbc_zPanel.gridy = 0;
262        mainPanel.add(zPanel, gbc_zPanel);
263        GridBagLayout gbl_zPanel = new GridBagLayout();
264        gbl_zPanel.columnWidths = new int[] {0, 0};
265        gbl_zPanel.rowHeights = new int[] {0, 0, 0, 0};
266        gbl_zPanel.columnWeights = new double[] {0.0, Double.MIN_VALUE};
267        gbl_zPanel.rowWeights = new double[] {0.0, 1.0, 0.0, Double.MIN_VALUE};
268        zPanel.setLayout(gbl_zPanel);
269
270        lblZ = new JLabel("Z");
271        GridBagConstraints gbc_lblZ = new GridBagConstraints();
272        gbc_lblZ.fill = GridBagConstraints.BOTH;
273        gbc_lblZ.insets = new Insets(0, 0, 5, 0);
274        gbc_lblZ.gridx = 0;
275        gbc_lblZ.gridy = 0;
276        zPanel.add(lblZ, gbc_lblZ);
277        lblZ.setFont(new Font("Tahoma", Font.BOLD, 12));
278        lblZ.setHorizontalAlignment(SwingConstants.CENTER);
279
280        zSlider = new JSlider(SwingConstants.VERTICAL);
281        GridBagConstraints gbc_zSlider = new GridBagConstraints();
282        gbc_zSlider.fill = GridBagConstraints.BOTH;
283        gbc_zSlider.insets = new Insets(0, 0, 5, 0);
284        gbc_zSlider.gridx = 0;
285        gbc_zSlider.gridy = 1;
286        zPanel.add(zSlider, gbc_zSlider);
287        // zSlider.setFocusable(false);
288
289        lblZValue = new JLabel("0");
290        GridBagConstraints gbc_lblZValue = new GridBagConstraints();
291        gbc_lblZValue.fill = GridBagConstraints.BOTH;
292        gbc_lblZValue.gridx = 0;
293        gbc_lblZValue.gridy = 2;
294        zPanel.add(lblZValue, gbc_lblZValue);
295        lblZValue.setHorizontalAlignment(SwingConstants.CENTER);
296        lblZValue.setFont(new Font("Tahoma", Font.BOLD, 11));
297
298        tPanel = new JPanel();
299        GridBagConstraints gbc_tPanel = new GridBagConstraints();
300        gbc_tPanel.fill = GridBagConstraints.BOTH;
301        gbc_tPanel.gridx = 1;
302        gbc_tPanel.gridy = 1;
303        mainPanel.add(tPanel, gbc_tPanel);
304        GridBagLayout gbl_tPanel = new GridBagLayout();
305        gbl_tPanel.columnWidths = new int[] {0, 0, 0, 0};
306        gbl_tPanel.rowHeights = new int[] {0, 0};
307        gbl_tPanel.columnWeights = new double[] {0.0, 1.0, 0.0, Double.MIN_VALUE};
308        gbl_tPanel.rowWeights = new double[] {1.0, Double.MIN_VALUE};
309        tPanel.setLayout(gbl_tPanel);
310
311        lblTValue = new JLabel("0");
312        lblTValue.setMaximumSize(new Dimension(1000, 14));
313        lblTValue.setPreferredSize(new Dimension(20, 14));
314        lblTValue.setMinimumSize(new Dimension(20, 14));
315        GridBagConstraints gbc_lblTValue = new GridBagConstraints();
316        gbc_lblTValue.fill = GridBagConstraints.BOTH;
317        gbc_lblTValue.insets = new Insets(0, 0, 0, 5);
318        gbc_lblTValue.gridx = 0;
319        gbc_lblTValue.gridy = 0;
320        tPanel.add(lblTValue, gbc_lblTValue);
321        lblTValue.setHorizontalAlignment(SwingConstants.CENTER);
322        lblTValue.setFont(new Font("Tahoma", Font.BOLD, 11));
323
324        tSlider = new JSlider(SwingConstants.HORIZONTAL);
325        GridBagConstraints gbc_tSlider = new GridBagConstraints();
326        gbc_tSlider.fill = GridBagConstraints.BOTH;
327        gbc_tSlider.insets = new Insets(0, 0, 0, 5);
328        gbc_tSlider.gridx = 1;
329        gbc_tSlider.gridy = 0;
330        tPanel.add(tSlider, gbc_tSlider);
331        // tSlider.setFocusable(false);
332
333        lblT = new JLabel("T");
334        lblT.setPreferredSize(new Dimension(20, 14));
335        lblT.setMaximumSize(new Dimension(1000, 14));
336        lblT.setMinimumSize(new Dimension(20, 14));
337        GridBagConstraints gbc_lblT = new GridBagConstraints();
338        gbc_lblT.fill = GridBagConstraints.BOTH;
339        gbc_lblT.gridx = 2;
340        gbc_lblT.gridy = 0;
341        tPanel.add(lblT, gbc_lblT);
342        lblT.setFont(new Font("Tahoma", Font.BOLD, 12));
343        lblT.setHorizontalAlignment(SwingConstants.CENTER);
344
345        validate();
346    }
347
348    /**
349     * @return the main panel
350     */
351    public JPanel getMainPanel()
352    {
353        return mainPanel;
354    }
355
356    /**
357     * @return the zPanel
358     */
359    public JPanel getZPanel()
360    {
361        return zPanel;
362    }
363
364    /**
365     * @return the tPanel
366     */
367    public JPanel getTPanel()
368    {
369        return tPanel;
370    }
371
372    public boolean getAutoHideSliders()
373    {
374        return autoHideSliders;
375    }
376
377    public void setAutoHideSliders(boolean value)
378    {
379        if (autoHideSliders != value)
380        {
381            autoHideSliders = value;
382            zPanel.setVisible((zSlider.getMaximum() > 0) && value);
383            tPanel.setVisible((tSlider.getMaximum() > 0) && value);
384        }
385    }
386
387    public void setFitToView(boolean value)
388    {
389        if (fitToView != value)
390        {
391            fitToView = value;
392            imagePanel.imageChanged();
393        }
394    }
395
396    public void setPositionZ(int z)
397    {
398        zSlider.setValue(z);
399        imageChanged();
400    }
401
402    public void setPositionT(int t)
403    {
404        tSlider.setValue(t);
405        imageChanged();
406    }
407
408    private void setMaxZ(int value)
409    {
410        zSlider.setMaximum(Math.max(0, value));
411        if (autoHideSliders)
412            zPanel.setVisible(value > 0);
413    }
414
415    private void setMaxT(int value)
416    {
417        tSlider.setMaximum(Math.max(0, value));
418        if (autoHideSliders)
419            tPanel.setVisible(value > 0);
420    }
421
422    /**
423     * @return the image provider
424     */
425    public SequenceModel getModel()
426    {
427        return model;
428    }
429
430    public void setModel(SequenceModel model)
431    {
432        if (this.model != model)
433        {
434            if (this.model != null)
435                this.model.removeSequenceModelListener(this);
436
437            this.model = model;
438
439            if (model != null)
440                model.addSequenceModelListener(this);
441
442            dimensionChanged();
443        }
444    }
445
446    public void setTitle(String value)
447    {
448        if (!titleLabel.getText().equals(value))
449        {
450            titleLabel.setText(value);
451            titleLabel.setVisible(!StringUtil.isEmpty(value));
452        }
453    }
454
455    @Override
456    public void dimensionChanged()
457    {
458        if (model != null)
459        {
460            setMaxZ(model.getSizeZ() - 1);
461            setMaxT(model.getSizeT() - 1);
462        }
463        else
464        {
465            setMaxZ(0);
466            setMaxT(0);
467        }
468
469        imagePanel.imageChanged();
470    }
471
472    @Override
473    public void imageChanged()
474    {
475        imagePanel.imageChanged();
476    }
477
478    BufferedImage getImage()
479    {
480        if (model == null)
481            return null;
482
483        return model.getImage(tSlider.getValue(), zSlider.getValue());
484    }
485
486    @Override
487    public void stateChanged(ChangeEvent e)
488    {
489        imagePanel.imageChanged();
490        lblZValue.setText(Integer.toString(zSlider.getValue()));
491        lblTValue.setText(Integer.toString(tSlider.getValue()));
492    }
493}