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