/*
* 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.lut;
import icy.gui.component.CheckTabbedPane;
import icy.gui.lut.abstract_.IcyLutViewer;
import icy.gui.util.GuiUtil;
import icy.gui.viewer.Viewer;
import icy.image.colormap.IcyColorMap;
import icy.image.colormap.IcyColorMap.IcyColorMapType;
import icy.image.colormap.IcyColorMapEvent;
import icy.image.colormap.IcyColorMapListener;
import icy.image.colormap.LinearColorMap;
import icy.image.lut.LUT;
import icy.image.lut.LUT.LUTChannel;
import icy.math.Scaler;
import icy.preferences.ApplicationPreferences;
import icy.preferences.XMLPreferences;
import icy.sequence.Sequence;
import icy.sequence.SequenceEvent;
import icy.sequence.SequenceListener;
import icy.system.thread.ThreadUtil;
import icy.type.DataType;
import icy.util.StringUtil;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JRadioButton;
import javax.swing.JTabbedPane;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class LUTViewer extends IcyLutViewer implements IcyColorMapListener, SequenceListener
{
private static final long serialVersionUID = 8385018166371243663L;
/**
* pref id
*/
private static final String PREF_ID_HISTO = "gui.histo";
private static final String ID_AUTO_REFRESH = "autoRefresh";
private static final String ID_AUTO_BOUNDS = "autoBounds";
private static final String ID_LOG_VIEW = "logView";
/**
* gui
*/
final CheckTabbedPane bottomPane;
final JCheckBox autoRefreshHistoCheckBox;
final JCheckBox autoBoundsCheckBox;
private final ButtonGroup scaleGroup;
private final JRadioButton logButton;
private final JRadioButton linearButton;
/**
* data
*/
final List<LUTChannelViewer> lutChannelViewers;
/**
* preferences
*/
final XMLPreferences pref;
final Runnable boundsUpdater;
final Runnable channelNameUpdater;
final Runnable channelEnableUpdater;
final Runnable channelTabColorUpdater;
public LUTViewer(Viewer viewer, LUT lut)
{
super(viewer, lut);
pref = ApplicationPreferences.getPreferences().node(PREF_ID_HISTO);
boundsUpdater = new Runnable()
{
@Override
public void run()
{
final Sequence sequence = getSequence();
if (sequence != null)
{
double[][] typeBounds = sequence.getChannelsTypeBounds();
double[][] bounds = sequence.getChannelsBounds();
for (int i = 0; i < Math.min(getLut().getNumChannel(), typeBounds.length); i++)
{
double[] tb = typeBounds[i];
double[] b = bounds[i];
final Scaler scaler = getLut().getLutChannel(i).getScaler();
scaler.setAbsLeftRightIn(tb[0], tb[1]);
scaler.setLeftRightIn(b[0], b[1]);
}
}
}
};
channelEnableUpdater = new Runnable()
{
@Override
public void run()
{
for (int c = 0; c < Math.min(getLut().getNumChannel(), bottomPane.getTabCount()); c++)
bottomPane.setTabChecked(c, getLut().getLutChannel(c).isEnabled());
}
};
channelTabColorUpdater = new Runnable()
{
@Override
public void run()
{
for (int c = 0; c < Math.min(getLut().getNumChannel(), bottomPane.getTabCount()); c++)
{
final IcyColorMap colormap = getLut().getLutChannel(c).getColorMap();
bottomPane.setBackgroundAt(c, colormap.getDominantColor());
}
}
};
channelNameUpdater = new Runnable()
{
@Override
public void run()
{
final Sequence sequence = getSequence();
if (sequence != null)
{
// need to be done on EDT
ThreadUtil.invokeNow(new Runnable()
{
@Override
public void run()
{
for (int c = 0; c < Math.min(sequence.getSizeC(), bottomPane.getTabCount()); c++)
{
final String channelName = sequence.getChannelName(c);
bottomPane.setTitleAt(c, StringUtil.limit(channelName, 10));
if (sequence.getDefaultChannelName(c).equals(channelName))
bottomPane.setToolTipTextAt(c, "Channel " + c);
else
bottomPane.setToolTipTextAt(c, channelName + " (channel " + c + ")");
}
}
});
}
}
};
lutChannelViewers = new ArrayList<LUTChannelViewer>();
// build GUI
bottomPane = new CheckTabbedPane(SwingConstants.BOTTOM, true);
bottomPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
// add tab for each channel
for (int c = 0; c < lut.getNumChannel(); c++)
{
final LUTChannel lutChannel = lut.getLutChannel(c);
final LUTChannelViewer lbv = new LUTChannelViewer(viewer, lutChannel);
lutChannel.getColorMap().addListener(this);
lutChannelViewers.add(lbv);
bottomPane.addTab("ch " + c, lbv);
}
bottomPane.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent e)
{
final int size = lutChannelViewers.size();
boolean changedState[] = new boolean[size];
boolean enabledState[] = new boolean[size];
for (int i = 0; i < size; i++)
{
try
{
// null pointer exception can sometime happen here, normal
enabledState[i] = bottomPane.isTabChecked(i);
changedState[i] = lutChannelViewers.get(i).getLutChannel().isEnabled() != enabledState[i];
}
catch (Exception exc)
{
enabledState[i] = true;
changedState[i] = false;
}
}
// we really want to only set state which changed here and not the one which has
// been set from a "setEnabled" event
for (int i = 0; i < size; i++)
{
if (changedState[i])
{
lutChannelViewers.get(i).getLutChannel().setEnabled(enabledState[i]);
}
}
}
});
autoRefreshHistoCheckBox = new JCheckBox("Refresh", pref.getBoolean(ID_AUTO_REFRESH, true));
autoRefreshHistoCheckBox.setToolTipText("Automatically refresh histogram when data is modified");
autoRefreshHistoCheckBox.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
final boolean value = autoRefreshHistoCheckBox.isSelected();
if (value)
refreshAllHistogram();
pref.putBoolean(ID_AUTO_REFRESH, value);
}
});
if (autoRefreshHistoCheckBox.isSelected())
refreshAllHistogram();
autoBoundsCheckBox = new JCheckBox("Auto bounds", getPreferredAutoBounds());
autoBoundsCheckBox.setToolTipText("Automatically ajdust bounds when data is modified");
autoBoundsCheckBox.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
final boolean value = autoBoundsCheckBox.isSelected();
if (value)
{
ThreadUtil.runSingle(boundsUpdater);
refreshAllHistogram();
autoRefreshHistoCheckBox.setSelected(true);
autoRefreshHistoCheckBox.setEnabled(false);
}
else
{
final boolean refreshValue = pref.getBoolean(ID_AUTO_REFRESH, true);
if (refreshValue)
refreshAllHistogram();
autoRefreshHistoCheckBox.setSelected(refreshValue);
autoRefreshHistoCheckBox.setEnabled(true);
}
pref.putBoolean(ID_AUTO_BOUNDS, value);
}
});
final Sequence seq = getSequence();
scaleGroup = new ButtonGroup();
logButton = new JRadioButton("log");
logButton.setToolTipText("Display histogram in a logarithm form");
logButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
scaleTypeChanged(true);
}
});
linearButton = new JRadioButton("linear");
linearButton.setToolTipText("Display histogram in a linear form");
linearButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
scaleTypeChanged(false);
}
});
scaleGroup.add(logButton);
scaleGroup.add(linearButton);
// default
if (pref.getBoolean(ID_LOG_VIEW, true))
logButton.setSelected(true);
else
linearButton.setSelected(true);
setLayout(new BorderLayout());
add(GuiUtil.createLineBoxPanel(autoRefreshHistoCheckBox, autoBoundsCheckBox, Box.createHorizontalGlue(),
Box.createHorizontalStrut(4), logButton, linearButton), BorderLayout.NORTH);
add(bottomPane, BorderLayout.CENTER);
validate();
// update channel name and color
channelTabColorUpdater.run();
channelNameUpdater.run();
if (seq != null)
{
if (!seq.hasUserLUT() && autoBoundsCheckBox.isSelected())
{
ThreadUtil.runSingle(boundsUpdater);
refreshAllHistogram();
autoRefreshHistoCheckBox.setSelected(true);
autoRefreshHistoCheckBox.setEnabled(false);
}
seq.addListener(this);
}
}
private boolean getPreferredAutoBounds()
{
boolean result = pref.getBoolean(ID_AUTO_BOUNDS, true);
if (!result)
return false;
final Sequence sequence = getSequence();
if (sequence != null)
{
// byte data type ?
if (sequence.getDataType_() == DataType.UBYTE)
{
final int numChannel = getLut().getNumChannel();
// custom colormaps --> cannot use auto bounds
for (int c = 0; c < numChannel; c++)
if (!getLut().getLutChannel(c).getColorMap().isLinear())
return false;
if ((numChannel == 3) || (numChannel == 4))
{
boolean rgb;
// check if we have classic RGB
rgb = getLut().getLutChannel(0).getColorMap().equals(LinearColorMap.red_)
&& getLut().getLutChannel(1).getColorMap().equals(LinearColorMap.green_)
&& getLut().getLutChannel(2).getColorMap().equals(LinearColorMap.blue_);
// ARGB
if (numChannel == 4)
rgb &= (getLut().getLutChannel(3).getColorMap().getType() == IcyColorMapType.ALPHA);
// do not use auto bounds for classic (A)RGB images
if (rgb)
return false;
}
}
}
return true;
}
@Override
public Sequence getSequence()
{
return super.getSequence();
}
@Override
public LUT getLut()
{
return super.getLut();
}
public boolean getAutoBounds()
{
return autoBoundsCheckBox.isSelected();
}
public void setAutoBound(boolean value)
{
autoBoundsCheckBox.setSelected(value);
}
public boolean getAutoRefreshHistogram()
{
return autoRefreshHistoCheckBox.isSelected();
}
public void setAutoRefreshHistogram(boolean value)
{
autoRefreshHistoCheckBox.setSelected(value);
}
public boolean getLogScale()
{
return logButton.isSelected();
}
public void setLogScale(boolean value)
{
if (value)
logButton.setSelected(true);
else
linearButton.setSelected(true);
}
void refreshAllHistogram()
{
for (int i = 0; i < lutChannelViewers.size(); i++)
lutChannelViewers.get(i).getScalerPanel().refreshHistogram();
}
void scaleTypeChanged(boolean log)
{
pref.putBoolean(ID_LOG_VIEW, log);
// change histogram scale type
for (int i = 0; i < lutChannelViewers.size(); i++)
lutChannelViewers.get(i).getScalerPanel().getScalerViewer().scaleTypeChanged(log);
}
@Override
public void colorMapChanged(IcyColorMapEvent e)
{
switch (e.getType())
{
case ENABLED_CHANGED:
ThreadUtil.runSingle(channelEnableUpdater);
break;
case MAP_CHANGED:
ThreadUtil.runSingle(channelTabColorUpdater);
break;
case TYPE_CHANGED:
break;
}
}
public void dispose()
{
removeAll();
Sequence seq = getSequence();
if (seq != null)
seq.removeListener(this);
}
@Override
public void sequenceChanged(SequenceEvent sequenceEvent)
{
final SequenceEvent e = sequenceEvent;
switch (e.getSourceType())
{
case SEQUENCE_META:
ThreadUtil.runSingle(channelNameUpdater);
break;
case SEQUENCE_COMPONENTBOUNDS:
if (autoBoundsCheckBox.isSelected())
ThreadUtil.runSingle(boundsUpdater);
break;
}
}
@Override
public void sequenceClosed(Sequence sequence)
{
}
}