/*
* Created on Jan 10, 2006
*
* Copyright (c) 2005 Peter Johan Salomonsen (http://www.petersalomonsen.com)
*
* http://www.frinika.com
*
* This file is part of Frinika.
*
* Frinika 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 2 of the License, or
* (at your option) any later version.
* Frinika 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 Frinika; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.frinika.sequencer.model;
import java.io.Serializable;
import com.frinika.sequencer.FrinikaTrackWrapper;
import com.frinika.sequencer.gui.Item;
/**
* MultiEvent is a representation of one or more MidiEvents that form a note pair,
* a control change/pitch bend envelope or a single MidiEvent.
*
* Note that the clone() method must be implemented for all non-abstract extensions
* of the MultiEvent class. This is for the edithistory to work properly.
*
* @author Peter Johan Salomonsen
*/
public abstract class MultiEvent implements Comparable,Serializable,EditHistoryRecordable,Item,Selectable,Cloneable {
private static final long serialVersionUID = 1L;
transient static long multiEventCounter = 1; // For generating unique multiEventIDs
MidiPart part;
long multiEventID = 0;
protected long startTick;
private Integer trackerColumn; // The prefered column number to use in the tracker view
transient boolean selected = false;
transient MultiEventEndTickComparable multiEventEndTickComparable = null;
// zombies have not been commited to a FTW (used by the pianoroll for displaying notes).
transient boolean zombie=true;
/*
* Protected constructor used by SubsetMultiEvent
* This doesn't set a multiEventID resulting that it always will be ordered as
* the first in a group of MultiEvents with the same tick. This should never
* be added to a track.
*/
protected MultiEvent(long startTick)
{
this.startTick = startTick;
}
/**
*
* @param track
* @param startTick
* @deprecated
*/
public MultiEvent(FrinikaTrackWrapper track,long startTick)
{
// System. out.println(" Creating MultiEvent ");
// assert(false);
// this.multiEventGroup = track.defaultGroup;
this.startTick = startTick;
this.multiEventID = multiEventCounter++;
}
public MultiEvent(MidiPart part,long startTick)
{
this.part = part;
this.startTick = startTick;
this.multiEventID = multiEventCounter++;
}
/**
* @return Returns the startTick.
*/
public long getStartTick() {
return startTick;
}
/**
* sub classes should override this.
* @return Returns the endTick
*/
abstract public long getEndTick();
/**
*
* @param startTick The startTick to set.
*/
public void setStartTick(long startTick) {
this.startTick = startTick;
/**
* NOTE: Previosly add and remove from track was removed from here
* Do this in the MultiEventChangeRecorder style instead
*/
}
/**
*
* @return
* @deprecated
*/
public final FrinikaTrackWrapper getTrack()
{
return ((MidiLane)(part.lane)).getTrack();
}
public MidiPart getMidiPart() {
return part;
}
/**
* Remove the MidiEvents generated by this MultiEvent from the track
* and fire CommitEvent to CommitListener
*
*/
void commitRemove() {
commitRemoveImpl();
getMidiPart().fireCommitRemove(this); // Jens
}
/**
* Add the MidiEvents generated by this MultiEvent to the track
* and fire CommitEvent to CommitListener
*
*/
public void commitAdd() {
commitAddImpl();
getMidiPart().fireCommitAdd(this); // Jens
}
/**
* Remove the MidiEvents generated by this MultiEvent from the track
*
*/
abstract void commitRemoveImpl(); // Jens, name-change was necessary for allowing listener-notification (which, in turn, is required for implementing ghosts)
/**
* Add the MidiEvents generated by this MultiEvent to the track
*
*/
abstract void commitAddImpl(); // Jens, name-change was necessary for allowing listener-notification (which, in turn, is required for implementing ghosts)
/**
* This method will be run by the processor handler
*@deprecated Do not use this - it was previously used by processors - but they're broken - the correct procedure is: remove from part - change - add to part again
*/
public void commitChanges()
{
commitRemove();
commitAdd();
}
public int compareTo(Object obj)
{
int ret = new Long(startTick).compareTo(new Long(((MultiEvent)obj).getStartTick()));
// In case the tick is the same use multiEventID for ordering
if(ret==0 && obj != this)
{
return new Long(multiEventID).compareTo(new Long(((MultiEvent)obj).multiEventID));
}
else
return ret;
}
public void setSelected(boolean yes) {
if (selected == yes) return;
selected = yes;
}
public boolean isSelected() {
return selected;
}
/**
* Create a parentless copy to allow serialiation without explosions
*/
public MultiEvent detachedCopy() {
MultiEvent ev=null;
try {
ev = (MultiEvent)clone();
ev.part=null;
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ev;
}
public Selectable deepCopy(Selectable parent) {
MultiEvent ev=null;
try {
ev = (MultiEvent)clone();
if (parent != null ) {
ev.part=(MidiPart)parent;
} else {
ev.part=part;
}
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ev;
}
public MidiPart getPart() {
return part;
}
/*
* Added these 2 so NoteEvent and ControllerEvent can share methods for the ControllerView
*/
public int getValue() {return 0;}
public void setValue(int val) {}
/*
* Override these if you want the GUI to map low level values onto the GUI display values
* (a bit of a hack)
*
*/
public int getValueUI() {return getValue();}
public void setValueUI(int val) {setValue(val);}
public void deepMove(long tick) {
startTick+=tick;
}
public void removeFromModel() {
part.remove(this);
}
public void addToModel() {
part.add(this);
}
public long leftTickForMove() {
return startTick;
}
/**
* PLease override if need be
*/
public long rightTickForMove() {
return startTick;
}
/**
* The prefered column to use in a tracker view. In case of no prefered column NULL is returned
* @return
*/
public Integer getTrackerColumn() {
return trackerColumn;
}
public void setTrackerColumn(int column) {
this.trackerColumn = column;
}
/**
* Get a comparable wrapper for sorting on end tick
* @return
*/
public MultiEventEndTickComparable getMultiEventEndTickComparable() {
if(multiEventEndTickComparable == null)
multiEventEndTickComparable = new MultiEventEndTickComparable(this);
return multiEventEndTickComparable;
}
@Override
public Object clone() throws CloneNotSupportedException {
MultiEvent evt = (MultiEvent)super.clone();
evt.multiEventID = multiEventCounter++;
evt.setSelected(false);
return evt;
}
public boolean isZombie() {
return zombie;
}
}