/*
* 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.component.sequence;
import icy.common.listener.AcceptListener;
import icy.gui.main.GlobalSequenceListener;
import icy.gui.util.ComponentUtil;
import icy.main.Icy;
import icy.sequence.Sequence;
import icy.util.StringUtil;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
/**
* The sequence chooser is a component derived from JComboBox. <br>
* The combo auto refresh its content regarding to the sequence opened in ICY.<br>
* You can get it with getSequenceSelected()
*
* @author Fabrice de Chaumont & Stephane<br>
*/
public class SequenceChooser extends JComboBox implements GlobalSequenceListener
{
public interface SequenceChooserListener
{
/**
* Called when the sequence chooser selection changed for specified sequence.
*/
public void sequenceChanged(Sequence sequence);
}
private class SequenceComboModel extends DefaultComboBoxModel
{
/**
*
*/
private static final long serialVersionUID = -1402261337279171323L;
/**
* cached items list
*/
final List<WeakReference<Sequence>> cachedList;
public SequenceComboModel()
{
super();
cachedList = new ArrayList<WeakReference<Sequence>>();
updateList();
}
public void updateList()
{
// save selected item
final Object selected = getSelectedItem();
final int oldSize = cachedList.size();
cachedList.clear();
// add null entry at first position
if (nullEntryName != null)
cachedList.add(new WeakReference<Sequence>(null));
final List<Sequence> sequences = Icy.getMainInterface().getSequences();
// add active sequence entry at second position
if ((sequences.size() > 0) && (activeSequence != null))
cachedList.add(new WeakReference<Sequence>(activeSequence));
// add others sequence
for (Sequence seq : sequences)
if ((filter == null) || filter.accept(seq))
cachedList.add(new WeakReference<Sequence>(seq));
final int newSize = cachedList.size();
// some elements has been removed
if (newSize < oldSize)
fireIntervalRemoved(this, newSize, oldSize - 1);
// some elements has been added
else if (newSize > oldSize)
fireIntervalAdded(this, oldSize, newSize - 1);
// and some elements changed
fireContentsChanged(this, 0, newSize - 1);
// restore selected item
setSelectedItem(selected);
}
@Override
public Object getElementAt(int index)
{
return cachedList.get(index).get();
}
@Override
public int getSize()
{
return cachedList.size();
}
}
private static final long serialVersionUID = -6108163762809540675L;
public static final String SEQUENCE_SELECT_CMD = "sequence_select";
/**
* var
*/
AcceptListener filter;
final String nullEntryName;
/**
* listeners
*/
protected final List<SequenceChooserListener> listeners;
/**
* internals
*/
protected WeakReference<Sequence> previousSelectedSequence;
protected final SequenceComboModel model;
protected final Sequence activeSequence;
/**
* Create a new Sequence chooser component (JComboBox for sequence selection).
*
* @param activeSequenceEntry
* If true the combobox will display an <i>Active Sequence</i> entry so when we select it
* the {@link #getSelectedSequence()} method returns the current active sequence.
* @param nullEntryName
* If this parameter is not <code>null</code> the combobox will display an extra entry
* with the given string to define <code>null</code> sequence selection so when this
* entry will be selected the {@link #getSelectedSequence()} will return <code>null</code>.
* @param nameMaxLength
* Maximum authorized length for the sequence name display in the combobox (extra
* characters are truncated).<br>
* That prevent the combobox to be resized to very large width.
*/
public SequenceChooser(final boolean activeSequenceEntry, final String nullEntryName, final int nameMaxLength)
{
super();
this.nullEntryName = nullEntryName;
if (activeSequenceEntry)
activeSequence = new Sequence("active sequence");
else
activeSequence = null;
model = new SequenceComboModel();
setModel(model);
setRenderer(new ListCellRenderer()
{
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
boolean cellHasFocus)
{
final JLabel result = new JLabel();
if (value instanceof Sequence)
{
final String name = ((Sequence) value).getName();
result.setText(StringUtil.limit(name, nameMaxLength));
result.setToolTipText(name);
}
else if (value == null)
result.setText(nullEntryName);
return result;
}
});
addActionListener(this);
// default
listeners = new ArrayList<SequenceChooserListener>();
setActionCommand(SEQUENCE_SELECT_CMD);
previousSelectedSequence = new WeakReference<Sequence>(null);
setSelectedItem(null);
// fix height
ComponentUtil.setFixedHeight(this, 26);
}
/**
* @deprecated Use {@link #SequenceChooser(boolean, String, int)} instead.
*/
@SuppressWarnings("unused")
@Deprecated
public SequenceChooser(final int sequenceNameMaxLength, final boolean nullEntry, final boolean autoSelectIfNull,
final String nullEntryName)
{
this(false, nullEntry ? nullEntryName : null, sequenceNameMaxLength);
}
/**
* @deprecated Use {@link #SequenceChooser(boolean, String, int)} instead.
*/
@SuppressWarnings("unused")
@Deprecated
public SequenceChooser(int maxLength, boolean nullEntry, boolean autoSelectIfNull)
{
this(false, nullEntry ? "no sequence" : null, maxLength);
}
/**
* @deprecated Use {@link #SequenceChooser(boolean, String, int)} instead.
*/
@Deprecated
public SequenceChooser(int maxLength, boolean nullEntry)
{
this(false, nullEntry ? "no sequence" : null, maxLength);
}
/**
* Create a new Sequence chooser component (JComboBox for sequence selection).
*
* @param activeSequenceEntry
* If true the combobox will display an <i>Active Sequence</i> entry so when we select it
* the {@link #getSelectedSequence()} method returns the current active sequence.
* @param nullEntryName
* If this parameter is not <code>null</code> the combobox will display an extra entry
* with the given string to define <code>null</code> sequence selection so when this
* entry will be selected the {@link #getSelectedSequence()} will return <code>null</code>.
*/
public SequenceChooser(boolean activeSequenceEntry, String nullEntryName)
{
this(activeSequenceEntry, nullEntryName, 64);
}
/**
* @deprecated Use {@link #SequenceChooser(boolean, String, int)} instead.
*/
@Deprecated
public SequenceChooser(int nameMaxLength)
{
this(false, "no sequence", nameMaxLength);
}
/**
* Create a new Sequence chooser component (JComboBox for sequence selection).
*/
public SequenceChooser()
{
this(true, null, 64);
}
@Override
public void addNotify()
{
super.addNotify();
Icy.getMainInterface().addGlobalSequenceListener(this);
}
@Override
public void removeNotify()
{
Icy.getMainInterface().removeGlobalSequenceListener(this);
super.removeNotify();
}
/**
* @return the filter
*/
public AcceptListener getFilter()
{
return filter;
}
/**
* Set a filter for sequence display.<br>
* Only Sequence accepted by the filter will appear in the combobox.
*/
public void setFilter(AcceptListener filter)
{
if (this.filter != filter)
{
this.filter = filter;
model.updateList();
}
}
/**
* @return current selected sequence.
*/
public Sequence getSelectedSequence()
{
final Sequence result = (Sequence) getSelectedItem();
// special case for active sequence
if (result == activeSequence)
return Icy.getMainInterface().getActiveSequence();
return result;
}
/**
* Select the <i>Active sequence</i> entry if enable.
*/
public void setActiveSequenceSelected()
{
if (activeSequence != null)
setSelectedItem(activeSequence);
}
/**
* @param sequence
* The sequence to select in the combo box
*/
public void setSelectedSequence(Sequence sequence)
{
if (sequence != getSelectedSequence())
setSelectedItem(sequence);
}
/**
* @deprecated
* use {@link #setSelectedSequence(Sequence)} instead
*/
@Deprecated
public void setSequenceSelected(Sequence sequence)
{
setSelectedSequence(sequence);
}
// called when sequence selection has changed
private void sequenceChanged(Sequence sequence)
{
fireSequenceChanged(sequence);
}
private void fireSequenceChanged(Sequence sequence)
{
for (SequenceChooserListener listener : getListeners())
listener.sequenceChanged(sequence);
}
public ArrayList<SequenceChooserListener> getListeners()
{
return new ArrayList<SequenceChooserListener>(listeners);
}
public void addListener(SequenceChooserListener listener)
{
if (!listeners.contains(listener))
listeners.add(listener);
}
public void removeListener(SequenceChooserListener listener)
{
listeners.remove(listener);
}
@Override
public void actionPerformed(ActionEvent e)
{
final Sequence selected = getSelectedSequence();
if (previousSelectedSequence.get() != selected)
{
previousSelectedSequence = new WeakReference<Sequence>(selected);
// sequence changed
sequenceChanged(selected);
}
}
@Override
public void sequenceOpened(Sequence sequence)
{
model.updateList();
}
@Override
public void sequenceClosed(Sequence sequence)
{
model.updateList();
}
}