/* * Created on Apr 12, 2007 */ package ecologylab.oodss.logging.playback; import java.util.LinkedList; import java.util.List; import javax.swing.BoundedRangeModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import ecologylab.generic.Debug; import ecologylab.oodss.logging.Logging; import ecologylab.oodss.logging.MixedInitiativeOp; import ecologylab.oodss.logging.Prologue; /** * Model of playback for logged operations. Controls advancement through a log during playback, as well as rewind, etc. * * @author Zachary O. Toups (toupsz@cs.tamu.edu) */ public class LogPlaybackControlModel<E extends MixedInitiativeOp, T extends Logging<E>> implements BoundedRangeModel { protected T log; private int currentPlaybackOp = 0; private boolean singleEventMode = false; private List<ChangeListener> changeListeners = new LinkedList<ChangeListener>(); protected long startTime = 0; /** * */ public LogPlaybackControlModel(T log) { this.log = log; } public T getLog() { return log; } /** * Returns the control to the first op. * */ public void reset() { this.setPlaybackOpTo(0); } /** * Advances the current op by one. * */ public void forward() { this.setPlaybackOpTo(currentPlaybackOp + 1); } /** * Moves back one op. * */ public void back() { this.setPlaybackOpTo(currentPlaybackOp - 1); } public E getCurrentOp() { return log.getOpSequence().get(currentPlaybackOp); } /** * @see javax.swing.BoundedRangeModel#addChangeListener(javax.swing.event.ChangeListener) */ @Override public void addChangeListener(ChangeListener arg0) { changeListeners.add(arg0); } /** * @see javax.swing.BoundedRangeModel#getExtent() */ @Override public int getExtent() { return log.size(); } /** * @see javax.swing.BoundedRangeModel#getMaximum() */ @Override public int getMaximum() { return log.size() - 1; } /** * @see javax.swing.BoundedRangeModel#getMinimum() */ @Override public int getMinimum() { return 0; } /** * @see javax.swing.BoundedRangeModel#getValue() */ @Override public int getValue() { return currentPlaybackOp; } /** * @see javax.swing.BoundedRangeModel#getValueIsAdjusting() */ @Override public boolean getValueIsAdjusting() { return singleEventMode; } /** * @see javax.swing.BoundedRangeModel#removeChangeListener(javax.swing.event.ChangeListener) */ @Override public void removeChangeListener(ChangeListener arg0) { changeListeners.remove(arg0); } /** * Throws an UnsupportedOperationException; minimum is set by the underlying log data. * * @see javax.swing.BoundedRangeModel#setMinimum(int) */ @Override public void setMinimum(int arg0) throws UnsupportedOperationException { throw new UnsupportedOperationException("Cannot set minimum."); } /** * Throws an UnsupportedOperationException; maximum is set by the underlying log data. * * @see javax.swing.BoundedRangeModel#setMaximum(int) */ @Override public void setMaximum(int arg0) throws UnsupportedOperationException { throw new UnsupportedOperationException("Cannot set maximum."); } @Override public void setExtent(int arg0) throws UnsupportedOperationException { throw new UnsupportedOperationException("Extent set by underlying data; cannot set extent manually."); } @Override public void setRangeProperties(int arg0, int arg1, int arg2, int arg3, boolean arg4) { throw new UnsupportedOperationException( "Range properties set by underlying data; cannot set properties manually."); } /** * @see javax.swing.BoundedRangeModel#setValue(int) */ @Override public void setValue(int arg0) { this.setPlaybackOpTo(arg0); } protected void setPlaybackOpTo(int index) { if (index > (this.getMaximum())) { setPlaybackOpTo(this.getMaximum()); } else if (index < 0) { setPlaybackOpTo(0); } else { currentPlaybackOp = index; } if (!singleEventMode) { this.fireChangeEvent(); } } /** * @see javax.swing.BoundedRangeModel#setValueIsAdjusting(boolean) */ @Override public void setValueIsAdjusting(boolean arg0) { // if (singleEventMode && !arg0) // { // transition from true to false fires events // this.fireChangeEvent(); // } // this.singleEventMode = arg0; } private void fireChangeEvent() { for (ChangeListener l : changeListeners) { l.stateChanged(new ChangeEvent(this)); } } /** * Returns the next operation after the current one if one exists, otherwise returns the current operation. * * @return */ public E getNext() { if (this.currentPlaybackOp != (this.getMaximum())) { return log.getOpSequence().get(currentPlaybackOp + 1); } else { Debug.println("last op"); return this.getCurrentOp(); } } public Prologue getLogPrologue() { return this.log.getPrologue(); } public void setStartPoint() { startTime = this.getCurrentOp().getSessionTime(); } public long getTimeOffset() { return this.getCurrentOp().getSessionTime() - startTime; } }