/* VisualReferenceModel.java created 2007-11-30
*
*/
package org.signalml.app.view.montage.visualreference;
import static org.signalml.app.util.i18n.SvarogI18n._;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import javax.swing.event.EventListenerList;
import org.signalml.domain.montage.system.IChannelFunction;
import org.signalml.domain.montage.system.ChannelType;
import org.signalml.domain.montage.Montage;
import org.signalml.domain.montage.MontageEvent;
import org.signalml.domain.montage.MontageException;
import org.signalml.domain.montage.MontageListener;
import org.signalml.domain.montage.SourceChannel;
import org.signalml.domain.montage.SourceMontageEvent;
import org.signalml.domain.montage.SourceMontageListener;
import org.signalml.domain.montage.system.ChannelFunction;
import org.signalml.exception.SanityCheckException;
import org.signalml.util.Util;
/** VisualReferenceModel
*
*
* @author Michal Dobaczewski © 2007-2008 CC Otwarte Systemy Komputerowe Sp. z o.o.
*/
public class VisualReferenceModel implements SourceMontageListener, MontageListener {
public static final String BIPOLAR_MODE_PROPERTY = "bipolarMode";
public static final String BIPOLAR_COMPATIBLE_PROPERTY = "bipolarCompatible";
public static final String ACTIVE_CHANNEL_PROPERTY = "activeChannel";
public static final String ACTIVE_ARROW_PROPERTY = "activeArrow";
private Montage montage;
private transient PropertyChangeSupport pcSupport;
private transient EventListenerList listenerList = new EventListenerList();
private boolean bipolarMode;
private boolean bipolarCompatible;
private VisualReferenceArrow activeArrow;
private VisualReferenceChannel activeChannel;
private VisualReferenceChessboardBin othersBin;
private VisualReferencePositionedBin positionedBin;
private ArrayList<VisualReferenceSourceChannel> sourceChannels;
private ArrayList<VisualReferenceChannel> channels;
private ArrayList<LinkedList<VisualReferenceChannel>> channelLists;
private ArrayList<VisualReferenceArrow> arrows;
public VisualReferenceModel() {
pcSupport = new PropertyChangeSupport(this);
readAllFromMontage();
}
public Montage getMontage() {
return montage;
}
public void setMontage(Montage montage) {
if (this.montage != montage) {
if (this.montage != null) {
this.montage.removeSourceMontageListener(this);
this.montage.removeMontageListener(this);
}
this.montage = montage;
if (montage != null) {
montage.addSourceMontageListener(this);
montage.addMontageListener(this);
setBipolarCompatible(montage.isBipolar());
} else {
setBipolarCompatible(false);
}
readAllFromMontage();
}
}
private void readAllFromMontage() {
// TODO consider making this lazy
sourceChannels = new ArrayList<VisualReferenceSourceChannel>();
channels = new ArrayList<VisualReferenceChannel>();
channelLists = new ArrayList<LinkedList<VisualReferenceChannel>>();
arrows = new ArrayList<VisualReferenceArrow>();
othersBin = new VisualReferenceChessboardBin();
positionedBin = new VisualReferencePositionedBin();
if (montage != null) {
int cnt = montage.getSourceChannelCount();
int i;
int primary;
VisualReferenceSourceChannel visualSourceChannel;
VisualReferenceChannel channel;
sourceChannels.ensureCapacity(cnt);
channelLists.ensureCapacity(cnt);
for (i=0; i<cnt; i++) {
SourceChannel sourceChannel = montage.getSourceChannelAt(i);
visualSourceChannel = new VisualReferenceSourceChannel(sourceChannel);
sourceChannels.add(visualSourceChannel);
channelLists.add(new LinkedList<VisualReferenceChannel>());
if (
sourceChannel.getFunction().getName().equals(ChannelFunction.EEG.getName())
&& sourceChannel.getEegElectrode() != null
) {
// the channel is positioned
positionedBin.add(visualSourceChannel);
} else {
othersBin.add(visualSourceChannel);
}
}
cnt = montage.getMontageChannelCount();
channels.ensureCapacity(cnt);
for (i=0; i<cnt; i++) {
primary = montage.getMontagePrimaryChannelAt(i);
channel = new VisualReferenceChannel(primary);
channel.setLabel(montage.getMontageChannelLabelAt(i));
channels.add(channel);
channelLists.get(primary).add(channel);
}
readArrowsFromMontage();
}
othersBin.setName(_("Others"));
positionedBin.setName(_("Positioned"));
setActiveChannel(null);
fireMontageStructureChanged(this);
}
private void readArrowsFromMontage() {
arrows.clear();
int cnt = montage.getMontageChannelCount();
String[] refs;
int i, e;
int primary;
VisualReferenceArrow arrow;
for (i=0; i<cnt; i++) {
primary = montage.getMontagePrimaryChannelAt(i);
refs = montage.getReference(i);
for (e=0; e<refs.length; e++) {
if (e != primary && refs[e] != null) {
arrow = new VisualReferenceArrow(e, i);
arrows.add(arrow);
}
}
}
}
// montageIndex is a source index in this mode
private void addBipolarModeReference(int montageIndex, int sourceIndex) {
int[] current = montage.getMontageChannelsForSourceChannel(montageIndex);
int i;
int refCandidate = -1;
for (i=0; i<current.length; i++) {
if (montage.hasReference(current[i], sourceIndex)) {
// this bipolar channel already exist
return;
}
if (refCandidate < 0 && !montage.hasReference(current[i])) {
refCandidate = current[i];
}
}
String label = montage.getSourceChannelLabelAt(montageIndex) + "-" + montage.getSourceChannelLabelAt(sourceIndex);
if (refCandidate >= 0) {
montage.setReference(refCandidate, sourceIndex, "-1");
try {
montage.setMontageChannelLabelAt(refCandidate, montage.getNewMontageChannelLabel(label));
} catch (MontageException ex) {
// this should not happen, label was generated
throw new SanityCheckException(ex);
}
} else {
montage.addBipolarMontageChannel(montageIndex, label, sourceIndex);
}
}
private void addRegularModeReference(int montageIndex, int sourceIndex) {
int primary = montage.getMontagePrimaryChannelAt(montageIndex);
if (primary == sourceIndex) {
return;
}
String[] refs = montage.getReference(montageIndex);
int i;
int cnt = 0;
for (i=0; i<refs.length; i++) {
if (i == primary || i == sourceIndex) { // neither primary nor current are counted
continue;
}
if (refs[i] != null) {
cnt++;
}
}
cnt++; // count in currently added
String token;
if (cnt == 1) {
token = "-1";
} else {
token = "-1/" + cnt;
}
boolean changed = false;
for (i=0; i<refs.length; i++) {
if (i == primary) {
continue;
}
if (i == sourceIndex || refs[i] != null) {
if (!changed && (refs[i] == null || !refs[i].equals(token))) {
changed = true;
}
refs[i] = token;
}
}
if (changed) {
montage.setReference(montageIndex, refs);
}
}
public void addReference(int montageIndex, int sourceIndex) {
if (bipolarMode) {
// bipolar mode reaction
addBipolarModeReference(montageIndex, sourceIndex);
} else {
// normal mode reaction
addRegularModeReference(montageIndex, sourceIndex);
}
//selects currently added arrow.
for (VisualReferenceArrow arrow: this.arrows) {
if (arrow.getSourceChannel() == sourceIndex && montageIndex == arrow.getTargetChannel()) {
setActiveArrow(arrow);
break;
}
}
}
// montageIndex is a montage index in this mode
private void removeBipolarModeReference(int montageIndex, int sourceIndex) {
/*
int[] current = montage.getMontageChannelsForSourceChannel( montageIndex );
int i;
for( i=0; i<current.length; i++ ) {
if( montage.hasReference( current[i], sourceIndex ) ) {
montage.removeMontageChannel( current[i] );
return;
}
}
*/
montage.removeMontageChannel(montageIndex);
}
private void removeRegularModeReference(int montageIndex, int sourceIndex) {
int primary = montage.getMontagePrimaryChannelAt(montageIndex);
if (primary == sourceIndex) {
return;
}
String[] refs = montage.getReference(montageIndex);
int i;
int cnt = 0;
for (i=0; i<refs.length; i++) {
if (i == primary || i == sourceIndex) { // neither primary nor current are counted
continue;
}
if (refs[i] != null) {
cnt++;
}
}
String token;
if (cnt == 0) {
token = null;
}
else if (cnt == 1) {
token = "-1";
} else {
token = "-1/" + cnt;
}
boolean changed = false;
for (i=0; i<refs.length; i++) {
if (i == primary) {
continue;
}
if (i == sourceIndex) {
if (!changed && (refs[i] != null)) {
changed = true;
}
refs[i] = null;
}
if (refs[i] != null) {
if (!changed && !Util.equalsWithNulls(refs[i], token)) {
changed = true;
}
refs[i] = token;
}
}
if (changed) {
montage.setReference(montageIndex, refs);
}
}
public void removeReference(int montageIndex, int sourceIndex) {
if (bipolarMode) {
// bipolar mode reaction
removeBipolarModeReference(montageIndex, sourceIndex);
} else {
// normal mode reaction
removeRegularModeReference(montageIndex, sourceIndex);
}
}
public VisualReferenceBin getOthersBin() {
return othersBin;
}
public VisualReferencePositionedBin getPositionedBin() {
return positionedBin;
}
public VisualReferenceSourceChannel getSourceChannel(int index) {
return sourceChannels.get(index);
}
public int indexOfSourceChannel(VisualReferenceSourceChannel o) {
return sourceChannels.indexOf(o);
}
public boolean isSourceChannelsEmpty() {
return sourceChannels.isEmpty();
}
public Iterator<VisualReferenceSourceChannel> sourceChannelIterator() {
return sourceChannels.iterator();
}
public int sourceChannelsSize() {
return sourceChannels.size();
}
public VisualReferenceChannel getChannel(int index) {
return channels.get(index);
}
public int indexOfChannel(VisualReferenceChannel channel) {
return channels.indexOf(channel);
}
public boolean isChannelsEmpty() {
return channels.isEmpty();
}
public Iterator<VisualReferenceChannel> channelsIterator() {
return channels.iterator();
}
public int channelsSize() {
return channels.size();
}
public VisualReferenceChannel getChannelPerPrimary(int sourceIndex, int index) {
return channelLists.get(sourceIndex).get(index);
}
public int indexOfChannelPerPrimary(int sourceIndex, VisualReferenceChannel channel) {
return channelLists.get(sourceIndex).indexOf(channel);
}
public boolean isChannelsPerPrimaryEmpty(int sourceIndex) {
return channelLists.get(sourceIndex).isEmpty();
}
public Iterator<VisualReferenceChannel> channelsPerPrimaryIterator(int sourceIndex) {
return channelLists.get(sourceIndex).iterator();
}
public int channelsPerPrimarySize(int sourceIndex) {
return channelLists.get(sourceIndex).size();
}
public VisualReferenceArrow getArrow(int index) {
return arrows.get(index);
}
public int indexOfArrow(VisualReferenceArrow o) {
return arrows.indexOf(o);
}
public boolean isArrowsEmpty() {
return arrows.isEmpty();
}
public Iterator<VisualReferenceArrow> arrowsIterator() {
return arrows.iterator();
}
public int arrowsSize() {
return arrows.size();
}
public boolean isBipolarMode() {
return bipolarMode;
}
public void setBipolarMode(boolean bipolarMode) {
boolean newBipolarMode = bipolarMode && isBipolarCompatible();
if (this.bipolarMode != newBipolarMode) {
this.bipolarMode = newBipolarMode;
pcSupport.firePropertyChange(BIPOLAR_MODE_PROPERTY, !newBipolarMode, newBipolarMode);
}
}
public boolean isBipolarCompatible() {
return bipolarCompatible;
}
public void setBipolarCompatible(boolean bipolarCompatible) {
if (this.bipolarCompatible != bipolarCompatible) {
this.bipolarCompatible = bipolarCompatible;
if (!bipolarCompatible) {
setBipolarMode(false);
}
pcSupport.firePropertyChange(BIPOLAR_COMPATIBLE_PROPERTY, !bipolarCompatible, bipolarCompatible);
}
}
public int getArrowOrder(int target, int source) {
int primary = montage.getMontagePrimaryChannelAt(target);
int[] channels = montage.getMontageChannelsForSourceChannel(primary);
int order = 0;
for (int channel : channels) {
if (channel == target) {
break;
}
if (montage.hasReference(channel, source)) {
order++;
}
}
return order;
}
public VisualReferenceArrow getActiveArrow() {
return activeArrow;
}
public void setActiveArrow(VisualReferenceArrow activeArrow) {
if (this.activeArrow != activeArrow) {
VisualReferenceArrow oldArrow = this.activeArrow;
if (activeArrow != null) {
selectChannelAt(activeArrow.getTargetChannel());
}
this.activeArrow = activeArrow;
pcSupport.firePropertyChange(ACTIVE_ARROW_PROPERTY, oldArrow, activeArrow);
}
}
public VisualReferenceChannel getActiveChannel() {
return activeChannel;
}
public void setActiveChannel(VisualReferenceChannel activeChannel) {
if (this.activeChannel != activeChannel) {
VisualReferenceChannel oldChannel = this.activeChannel;
setActiveArrow(null);
this.activeChannel = activeChannel;
pcSupport.firePropertyChange(ACTIVE_CHANNEL_PROPERTY, oldChannel, activeChannel);
}
}
public void selectNextChannel() {
if (channels.isEmpty()) {
return;
}
if (activeChannel == null) {
setActiveChannel(channels.get(0));
} else {
int index = channels.indexOf(activeChannel);
index = (index + 1) % channels.size();
setActiveChannel(channels.get(index));
}
}
public void selectPreviousChannel() {
if (channels.isEmpty()) {
return;
}
if (activeChannel == null) {
setActiveChannel(channels.get(channels.size()-1));
} else {
int index = channels.indexOf(activeChannel);
index--;
if (index < 0) {
index = channels.size() - 1;
}
setActiveChannel(channels.get(index));
}
}
public void selectChannelAt(int index) {
setActiveChannel(channels.get(index));
}
@Override
public void montageChannelsAdded(MontageEvent ev) {
readAllFromMontage();
setBipolarCompatible(montage.isBipolar());
fireMontageChannelsChanged(this, ev);
}
@Override
public void montageChannelsChanged(MontageEvent ev) {
int index = ev.getChannel();
channels.get(index).setLabel(montage.getMontageChannelLabelAt(index));
fireMontageChannelsChanged(this, ev);
}
@Override
public void montageChannelsRemoved(MontageEvent ev) {
readAllFromMontage();
setBipolarCompatible(montage.isBipolar());
fireMontageChannelsChanged(this, ev);
}
@Override
public void montageReferenceChanged(MontageEvent ev) {
readArrowsFromMontage();
setBipolarCompatible(montage.isBipolar());
fireReferenceChanged(this, ev);
}
@Override
public void montageStructureChanged(MontageEvent ev) {
readAllFromMontage();
setBipolarCompatible(montage.isBipolar());
fireMontageStructureChanged(this);
}
@Override
public void sourceMontageChannelAdded(SourceMontageEvent ev) {
readAllFromMontage();
setBipolarCompatible(montage.isBipolar());
fireSourceChannelsChanged(this, ev);
}
@Override
public void sourceMontageChannelChanged(SourceMontageEvent ev) {
readAllFromMontage();
setBipolarCompatible(montage.isBipolar());
fireSourceChannelsChanged(this, ev);
}
@Override
public void sourceMontageChannelRemoved(SourceMontageEvent ev) {
readAllFromMontage();
setBipolarCompatible(montage.isBipolar());
fireSourceChannelsChanged(this, ev);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(propertyName, listener);
}
public void addVisualReferenceListener(VisualReferenceListener l) {
listenerList.add(VisualReferenceListener.class, l);
}
public void removeVisualReferenceListener(VisualReferenceListener l) {
listenerList.remove(VisualReferenceListener.class, l);
}
protected void fireSourceChannelsChanged(Object source, SourceMontageEvent sourceMontageEvent) {
Object[] listeners = listenerList.getListenerList();
VisualReferenceEvent e = null;
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==VisualReferenceListener.class) {
if (e == null) {
e = new VisualReferenceEvent(source, sourceMontageEvent);
}
((VisualReferenceListener)listeners[i+1]).sourceChannelsChanged(e);
}
}
}
protected void fireMontageChannelsChanged(Object source, MontageEvent montageEvent) {
Object[] listeners = listenerList.getListenerList();
VisualReferenceEvent e = null;
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==VisualReferenceListener.class) {
if (e == null) {
e = new VisualReferenceEvent(source, montageEvent);
}
((VisualReferenceListener)listeners[i+1]).montageChannelsChanged(e);
}
}
}
protected void fireReferenceChanged(Object source, MontageEvent montageEvent) {
Object[] listeners = listenerList.getListenerList();
VisualReferenceEvent e = null;
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==VisualReferenceListener.class) {
if (e == null) {
e = new VisualReferenceEvent(source, montageEvent);
}
((VisualReferenceListener)listeners[i+1]).referenceChanged(e);
}
}
}
protected void fireMontageStructureChanged(Object source) {
Object[] listeners = listenerList.getListenerList();
VisualReferenceEvent e = null;
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==VisualReferenceListener.class) {
if (e == null) {
e = new VisualReferenceEvent(source);
}
((VisualReferenceListener)listeners[i+1]).montageStructureChanged(e);
}
}
}
@Override
public void sourceMontageEegSystemChanged(SourceMontageEvent ev) {
}
}