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.lut;
020
021import icy.canvas.IcyCanvas3D;
022import icy.file.xml.XMLPersistentHelper;
023import icy.gui.component.button.IcyButton;
024import icy.gui.component.button.IcyToggleButton;
025import icy.gui.component.renderer.ColormapComboBoxRenderer;
026import icy.gui.dialog.OpenDialog;
027import icy.gui.dialog.SaveDialog;
028import icy.gui.util.ComponentUtil;
029import icy.gui.util.GuiUtil;
030import icy.gui.viewer.Viewer;
031import icy.image.colormap.IcyColorMap;
032import icy.image.colormap.IcyColorMap.IcyColorMapType;
033import icy.image.colormap.IcyColorMapEvent;
034import icy.image.colormap.IcyColorMapListener;
035import icy.image.lut.LUT;
036import icy.image.lut.LUT.LUTChannel;
037import icy.resource.ResourceUtil;
038import icy.resource.icon.IcyIcon;
039import icy.sequence.Sequence;
040
041import java.awt.BorderLayout;
042import java.awt.event.ActionEvent;
043import java.awt.event.ActionListener;
044import java.util.List;
045
046import javax.swing.BorderFactory;
047import javax.swing.Box;
048import javax.swing.ButtonGroup;
049import javax.swing.JComboBox;
050import javax.swing.JPanel;
051import javax.swing.JSeparator;
052import javax.swing.SwingConstants;
053
054/**
055 * @author stephane
056 */
057public class ColormapPanel extends JPanel implements IcyColorMapListener
058{
059    /**
060     * 
061     */
062    private static final long serialVersionUID = -4042084504553770641L;
063
064    private static final String DEFAULT_COLORMAP_DIR = IcyColorMap.DEFAULT_COLORMAP_DIR;
065    private static final String DEFAULT_COLORMAP_NAME = "colormap.xml";
066
067    /**
068     * gui
069     */
070    private final ColormapViewer colormapViewer;
071    final IcyToggleButton rgbBtn;
072    final IcyToggleButton grayBtn;
073    final IcyToggleButton alphaBtn;
074    final ButtonGroup colormapTypeBtnGrp;
075    final JComboBox colormapComboBox;
076
077    /**
078     * associated Viewer & LUTBand
079     */
080    public final Viewer viewer;
081    public final LUTChannel lutChannel;
082
083    /**
084     * cached
085     */
086    final IcyColorMap colormap;
087
088    private boolean modifyingColormap;
089
090    public ColormapPanel(final Viewer viewer, final LUTChannel lutChannel)
091    {
092        super();
093
094        this.viewer = viewer;
095        this.lutChannel = lutChannel;
096        modifyingColormap = false;
097
098        colormap = lutChannel.getColorMap();
099        colormap.setName("Custom");
100
101        colormapViewer = new ColormapViewer(lutChannel);
102
103        // colormap type
104        rgbBtn = new IcyToggleButton(new IcyIcon(ResourceUtil.ICON_RGB_COLOR, false));
105        rgbBtn.setToolTipText("Set colormap type to Color");
106        rgbBtn.setFocusPainted(false);
107        ComponentUtil.setFixedWidth(rgbBtn, 26);
108        grayBtn = new IcyToggleButton(new IcyIcon(ResourceUtil.ICON_GRAY_COLOR, false));
109        grayBtn.setToolTipText("Set colormap type to Gray");
110        grayBtn.setFocusPainted(false);
111        ComponentUtil.setFixedWidth(grayBtn, 26);
112        alphaBtn = new IcyToggleButton(new IcyIcon(ResourceUtil.ICON_ALPHA_COLOR, false));
113        alphaBtn.setToolTipText("Set colormap type to Alpha (transparency)");
114        alphaBtn.setFocusPainted(false);
115        ComponentUtil.setFixedWidth(alphaBtn, 26);
116
117        colormapTypeBtnGrp = new ButtonGroup();
118
119        colormapTypeBtnGrp.add(rgbBtn);
120        colormapTypeBtnGrp.add(grayBtn);
121        colormapTypeBtnGrp.add(alphaBtn);
122
123        // select item according to current colormap type
124        updateColormapType(colormap.getType());
125
126        rgbBtn.addActionListener(new ActionListener()
127        {
128            @Override
129            public void actionPerformed(ActionEvent e)
130            {
131                colormap.setType(IcyColorMapType.RGB);
132            }
133        });
134        grayBtn.addActionListener(new ActionListener()
135        {
136            @Override
137            public void actionPerformed(ActionEvent e)
138            {
139                colormap.setType(IcyColorMapType.GRAY);
140            }
141        });
142        alphaBtn.addActionListener(new ActionListener()
143        {
144            @Override
145            public void actionPerformed(ActionEvent e)
146            {
147                colormap.setType(IcyColorMapType.ALPHA);
148            }
149        });
150
151        // alpha checkbox
152        // final JCheckBox alphaEnabled = new JCheckBox("alpha", null, true);
153        // alphaEnabled.setToolTipText("Enable alpha control");
154        // alphaEnabled.addItemListener(new ItemListener()
155        // {
156        // @Override
157        // public void itemStateChanged(ItemEvent e)
158        // {
159        // // set alpha
160        // colormapViewer.setAlphaEnabled(e.getStateChange() == ItemEvent.SELECTED);
161        // }
162        // });
163
164        final IcyColorMap defaultColormap;
165        // retrieve the default sequence LUT
166        final LUT defaultLUT = viewer.getSequence().getDefaultLUT();
167
168        // compatible colormap (should always be the case)
169        if (defaultLUT.isCompatible(lutChannel.getLut()))
170        {
171            defaultColormap = defaultLUT.getLutChannel(lutChannel.getChannel()).getColorMap();
172            defaultColormap.setName("Default");
173        }
174        else
175        {
176            // asynchronous sequence change, get a default colormap from current one
177            defaultColormap = new IcyColorMap("Default");
178            // copy from current colormap
179            defaultColormap.copyFrom(colormap);
180        }
181
182        // copy it in the sequence user colormap
183        updateSequenceColormap();
184
185        // get colormap list
186        final List<IcyColorMap> colormaps = IcyColorMap.getAllColorMaps(true, true);
187
188        // remove the default colormap if already present in the list
189        colormaps.remove(defaultColormap);
190        // and set it at position 0
191        colormaps.add(0, defaultColormap);
192
193        // this is the user customized colormap
194        colormaps.add(colormap);
195
196        // build colormap selector
197        colormapComboBox = new JComboBox(colormaps.toArray());
198        colormapComboBox.setRenderer(new ColormapComboBoxRenderer(colormapComboBox));
199        // limit size
200        ComponentUtil.setFixedWidth(colormapComboBox, 96);
201        colormapComboBox.setToolTipText("Select colormap model");
202        // don't want focusable here
203        colormapComboBox.setFocusable(false);
204        // set to current colormap
205        colormapComboBox.setSelectedItem(colormap);
206
207        colormapComboBox.addActionListener(new ActionListener()
208        {
209            @Override
210            public void actionPerformed(ActionEvent e)
211            {
212                setColorMap((IcyColorMap) colormapComboBox.getSelectedItem());
213            }
214        });
215
216        // load button
217        final IcyButton loadButton = new IcyButton(new IcyIcon(ResourceUtil.ICON_OPEN));
218        loadButton.setFlat(true);
219        loadButton.setToolTipText("Load colormap from file");
220
221        // action to load colormap
222        loadButton.addActionListener(new ActionListener()
223        {
224            @Override
225            public void actionPerformed(ActionEvent e)
226            {
227                final String filename = OpenDialog.chooseFile("Load colormap...", DEFAULT_COLORMAP_DIR,
228                        DEFAULT_COLORMAP_NAME);
229
230                if (filename != null)
231                {
232                    final IcyColorMap map = new IcyColorMap();
233
234                    if (XMLPersistentHelper.loadFromXML(map, filename))
235                        setColorMap(map);
236                }
237            }
238        });
239
240        // save button
241        final IcyButton saveButton = new IcyButton(new IcyIcon(ResourceUtil.ICON_SAVE));
242        saveButton.setFlat(true);
243        saveButton.setToolTipText("Save colormap to file");
244
245        // action to save colormap
246        saveButton.addActionListener(new ActionListener()
247        {
248            @Override
249            public void actionPerformed(ActionEvent e)
250            {
251                final String filename = SaveDialog.chooseFile("Save colormap...", DEFAULT_COLORMAP_DIR,
252                        DEFAULT_COLORMAP_NAME);
253
254                if (filename != null)
255                    XMLPersistentHelper.saveToXML(colormap, filename);
256            }
257        });
258
259        // set up GUI
260        final JPanel bottomPanel = new JPanel();
261        bottomPanel.setLayout(new BorderLayout());
262        bottomPanel.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
263
264        bottomPanel.add(GuiUtil.createLineBoxPanel(rgbBtn, grayBtn, alphaBtn), BorderLayout.WEST);
265        bottomPanel.add(Box.createGlue(), BorderLayout.CENTER);
266        bottomPanel.add(
267                GuiUtil.createLineBoxPanel(colormapComboBox, new JSeparator(SwingConstants.VERTICAL), loadButton,
268                        Box.createHorizontalStrut(2), saveButton), BorderLayout.EAST);
269        // bottomPanel.add(Box.createHorizontalStrut(6));
270        // bottomPanel.add(alphaEnabled);
271
272        bottomPanel.validate();
273
274        setLayout(new BorderLayout());
275
276        add(colormapViewer, BorderLayout.CENTER);
277        add(bottomPanel, BorderLayout.SOUTH);
278
279        validate();
280    }
281
282    @Override
283    public void addNotify()
284    {
285        super.addNotify();
286
287        // listen colormap changes
288        colormap.addListener(this);
289    }
290
291    @Override
292    public void removeNotify()
293    {
294        colormap.removeListener(this);
295
296        super.removeNotify();
297    }
298
299    /**
300     * @return the colormapViewer
301     */
302    public ColormapViewer getColormapViewer()
303    {
304        return colormapViewer;
305    }
306
307    private void updateColormapType(IcyColorMapType type)
308    {
309        switch (type)
310        {
311            case RGB:
312                colormapTypeBtnGrp.setSelected(rgbBtn.getModel(), true);
313                break;
314            case GRAY:
315                colormapTypeBtnGrp.setSelected(grayBtn.getModel(), true);
316                break;
317            case ALPHA:
318                colormapTypeBtnGrp.setSelected(alphaBtn.getModel(), true);
319                break;
320        }
321    }
322
323    /**
324     * Set the colormap.
325     */
326    public void setColorMap(IcyColorMap src)
327    {
328        if (colormap == src)
329            return;
330
331        modifyingColormap = true;
332        try
333        {
334            if (viewer.getCanvas() instanceof IcyCanvas3D)
335            {
336                // copy alpha component only if we have specific alpha info
337                // copyAlpha = !src.alpha.isAllSame();
338                colormap.beginUpdate();
339                try
340                {
341                    colormap.copyFrom(src, false);
342                    colormap.setAlphaToLinear3D();
343                }
344                finally
345                {
346                    colormap.endUpdate();
347                }
348            }
349            else
350                colormap.copyFrom(src, true);
351        }
352        finally
353        {
354            modifyingColormap = false;
355        }
356
357        colormapComboBox.setSelectedItem(colormap);
358    }
359
360    void updateSequenceColormap()
361    {
362        // set the current colormap in the sequence
363        final Sequence seq = viewer.getSequence();
364
365        if (seq != null)
366        {
367            final int ch = lutChannel.getChannel();
368
369            if (ch < seq.getSizeC())
370                seq.setColormap(ch, colormap, true);
371        }
372    }
373
374    /**
375     * @deprecated Use {@link #setColorMap(IcyColorMap)} instead.
376     */
377    @Deprecated
378    public void copyColorMap(IcyColorMap src)
379    {
380        setColorMap(src);
381    }
382
383    @Override
384    public void colorMapChanged(IcyColorMapEvent e)
385    {
386        switch (e.getType())
387        {
388            case TYPE_CHANGED:
389                // colormap type has changed ? --> update combo state
390                updateColormapType(e.getColormap().getType());
391                break;
392
393            case MAP_CHANGED:
394                // colormap has been manually changed, set name to custom
395                if (!modifyingColormap)
396                    colormap.setName("Custom");
397                // update the combo box colormap representation
398                colormapComboBox.setSelectedItem(colormap);
399                colormapComboBox.repaint();
400                break;
401        }
402
403        updateSequenceColormap();
404    }
405}