Index: /trunk/src_apollo/org/apollo/agents/MelodySearch.java
===================================================================
--- /trunk/src_apollo/org/apollo/agents/MelodySearch.java	(revision 317)
+++ /trunk/src_apollo/org/apollo/agents/MelodySearch.java	(revision 318)
@@ -23,5 +23,5 @@
 import org.apollo.util.TextItemSearchResult;
 import org.apollo.widgets.LinkedTrack;
-import org.apollo.widgets.SampledTrackEvo4;
+import org.apollo.widgets.SampledTrack;
 import org.apollo.widgets.TrackWidgetCommons;
 import org.expeditee.agents.SearchAgent;
@@ -54,5 +54,5 @@
 	
 	/** Either querry from raw audio or track widget: */
-	private SampledTrackEvo4 querryTrack = null; 
+	private SampledTrack querryTrack = null; 
 	
 	/** Either querry from raw audio  or track widget: */
@@ -113,6 +113,6 @@
 			}
 
-			if (iw != null && iw instanceof SampledTrackEvo4) {
-				querryTrack = (SampledTrackEvo4)iw;
+			if (iw != null && iw instanceof SampledTrack) {
+				querryTrack = (SampledTrack)iw;
 			}
 			
@@ -140,5 +140,5 @@
 			String linkedTrackPrefix = trackPrefix;
 			
-			trackPrefix += SampledTrackEvo4.class.getName();
+			trackPrefix += SampledTrack.class.getName();
 			linkedTrackPrefix += LinkedTrack.class.getName();
 			
@@ -250,5 +250,5 @@
 		
 						// should this widget be indexed?
-						if (res.containsData(SampledTrackEvo4.META_DONT_INDEX_AUDIO_TAG)) continue;
+						if (res.containsData(SampledTrack.META_DONT_INDEX_AUDIO_TAG)) continue;
 						
 						// get the local filename
@@ -258,7 +258,7 @@
 						// Parse meta
 						for (String data : res.data) {
-							if (data.startsWith(SampledTrackEvo4.META_LOCALNAME_TAG) &&
-									data.length() > SampledTrackEvo4.META_LOCALNAME_TAG.length()) {
-								localFilename = data.substring(SampledTrackEvo4.META_LOCALNAME_TAG.length());
+							if (data.startsWith(SampledTrack.META_LOCALNAME_TAG) &&
+									data.length() > SampledTrack.META_LOCALNAME_TAG.length()) {
+								localFilename = data.substring(SampledTrack.META_LOCALNAME_TAG.length());
 								if (trackName != null) break;
 							} else if (data.startsWith(TrackWidgetCommons.META_NAME_TAG) &&
Index: /trunk/src_apollo/org/apollo/audio/structure/AudioStructureModel.java
===================================================================
--- /trunk/src_apollo/org/apollo/audio/structure/AudioStructureModel.java	(revision 317)
+++ /trunk/src_apollo/org/apollo/audio/structure/AudioStructureModel.java	(revision 318)
@@ -25,5 +25,5 @@
 import org.apollo.util.TextItemSearchResult;
 import org.apollo.widgets.LinkedTrack;
-import org.apollo.widgets.SampledTrackEvo4;
+import org.apollo.widgets.SampledTrack;
 import org.apollo.widgets.TrackWidgetCommons;
 import org.expeditee.gui.DisplayIO;
@@ -286,5 +286,5 @@
 			String linkedTrackPrefix = trackPrefix;
 			
-			trackPrefix += SampledTrackEvo4.class.getName();
+			trackPrefix += SampledTrack.class.getName();
 			linkedTrackPrefix += LinkedTrack.class.getName();
 
@@ -325,7 +325,7 @@
 							data = data.trim();
 							
-							if (data.startsWith(SampledTrackEvo4.META_LOCALNAME_TAG) &&
-									data.length() > SampledTrackEvo4.META_LOCALNAME_TAG.length()) {
-								localFileName = data.substring(SampledTrackEvo4.META_LOCALNAME_TAG.length());
+							if (data.startsWith(SampledTrack.META_LOCALNAME_TAG) &&
+									data.length() > SampledTrack.META_LOCALNAME_TAG.length()) {
+								localFileName = data.substring(SampledTrack.META_LOCALNAME_TAG.length());
 							
 							} else if (data.startsWith(TrackWidgetCommons.META_INITIATIONTIME_TAG) 
@@ -589,6 +589,6 @@
 				for (InteractiveWidget iw : rootFrame.getInteractiveWidgets()) {
 					
-					if (iw instanceof SampledTrackEvo4) {
-						SampledTrackEvo4 sampledTrackWidget = (SampledTrackEvo4)iw;
+					if (iw instanceof SampledTrack) {
+						SampledTrack sampledTrackWidget = (SampledTrack)iw;
 
 						TrackGraphNode tinf = 
Index: /trunk/src_apollo/org/apollo/audio/util/FrameLayoutDaemon.java
===================================================================
--- /trunk/src_apollo/org/apollo/audio/util/FrameLayoutDaemon.java	(revision 317)
+++ /trunk/src_apollo/org/apollo/audio/util/FrameLayoutDaemon.java	(revision 318)
@@ -27,5 +27,5 @@
 import org.apollo.util.NullableLong;
 import org.apollo.widgets.LinkedTrack;
-import org.apollo.widgets.SampledTrackEvo4;
+import org.apollo.widgets.SampledTrack;
 import org.expeditee.gui.Browser;
 import org.expeditee.gui.DisplayIO;
@@ -331,6 +331,6 @@
 					AbstractTrackGraphNode abinf = null;
 
-					if (iw instanceof SampledTrackEvo4) {
-						SampledTrackEvo4 sampledTrackWidget = (SampledTrackEvo4)iw;
+					if (iw instanceof SampledTrack) {
+						SampledTrack sampledTrackWidget = (SampledTrack)iw;
 			
 
@@ -493,8 +493,8 @@
 					
 					int width = -1;
-					if (freespaceTrackToFormat instanceof SampledTrackEvo4) {
-						
-						long rt = ((SampledTrackEvo4)freespaceTrackToFormat).getRunningMSTimeFromRawAudio();
-						if (rt <= 0) rt = ((SampledTrackEvo4)freespaceTrackToFormat).getRunningMSTimeFromMeta();
+					if (freespaceTrackToFormat instanceof SampledTrack) {
+						
+						long rt = ((SampledTrack)freespaceTrackToFormat).getRunningMSTimeFromRawAudio();
+						if (rt <= 0) rt = ((SampledTrack)freespaceTrackToFormat).getRunningMSTimeFromMeta();
 						if (rt > 0) {
 							width = (int)(currentTimeline.getPixelPerTime() * rt);
@@ -674,6 +674,6 @@
 		for (InteractiveWidget iw : frame.getInteractiveWidgets()) {
 			
-			if (iw instanceof SampledTrackEvo4) {
-				SampledTrackEvo4 sampledTrackWidget = (SampledTrackEvo4)iw;
+			if (iw instanceof SampledTrack) {
+				SampledTrack sampledTrackWidget = (SampledTrack)iw;
 
 				// TrackGraphModel provides best data
Index: /trunk/src_apollo/org/apollo/audio/util/SoundDesk.java
===================================================================
--- /trunk/src_apollo/org/apollo/audio/util/SoundDesk.java	(revision 317)
+++ /trunk/src_apollo/org/apollo/audio/util/SoundDesk.java	(revision 318)
@@ -25,5 +25,5 @@
 import org.apollo.mvc.SubjectChangedEvent;
 import org.apollo.widgets.LinkedTrack;
-import org.apollo.widgets.SampledTrackEvo4;
+import org.apollo.widgets.SampledTrack;
 import org.expeditee.gui.Frame;
 
@@ -1015,5 +1015,5 @@
 	 * 		The channel ID... never null.
 	 */
-	public static String createPureLocalChannelID(SampledTrackEvo4 sampledTrackWidget) {
+	public static String createPureLocalChannelID(SampledTrack sampledTrackWidget) {
 		assert(sampledTrackWidget != null);
 		return createPureLocalChannelID(sampledTrackWidget.getLocalFileName());
Index: /trunk/src_apollo/org/apollo/gui/EditableSampledTrackGraphView.java
===================================================================
--- /trunk/src_apollo/org/apollo/gui/EditableSampledTrackGraphView.java	(revision 317)
+++ /trunk/src_apollo/org/apollo/gui/EditableSampledTrackGraphView.java	(revision 318)
@@ -22,5 +22,5 @@
 import org.apollo.mvc.SubjectChangedEvent;
 import org.apollo.util.TrackNameCreator;
-import org.apollo.widgets.SampledTrackEvo4;
+import org.apollo.widgets.SampledTrack;
 import org.expeditee.gui.Browser;
 import org.expeditee.gui.DisplayIO;
@@ -175,11 +175,11 @@
 				|| e.getButton() == MouseEvent.BUTTON3) && !FreeItems.getInstance().isEmpty())) {
 			
-			SampledTrackEvo4 floatingTrack = null;
+			SampledTrack floatingTrack = null;
 			
 			// Look for a floating track for injection of audio bytes
 			for (Item i : FreeItems.getInstance()) {
 				if (i instanceof WidgetCorner) {
-					if (((WidgetCorner)i).getWidgetSource() instanceof SampledTrackEvo4) {
-						floatingTrack = (SampledTrackEvo4)((WidgetCorner)i).getWidgetSource();
+					if (((WidgetCorner)i).getWidgetSource() instanceof SampledTrack) {
+						floatingTrack = (SampledTrack)((WidgetCorner)i).getWidgetSource();
 						break;
 					}
@@ -259,5 +259,5 @@
 
 	/**
-	 * Copies the selection into free space as a {@link SampledTrackEvo4}
+	 * Copies the selection into free space as a {@link SampledTrack}
 	 * 
 	 * @param e
@@ -273,5 +273,5 @@
 			if (targetFrame != null) {
 				
-				SampledTrackEvo4 twidget = SampledTrackEvo4.createFromMemory(
+				SampledTrack twidget = SampledTrack.createFromMemory(
 						copiedAudioRegion, 
 						getSampledTrackModel().getFormat(),
Index: /trunk/src_apollo/org/apollo/gui/FrameRenderPasses.java
===================================================================
--- /trunk/src_apollo/org/apollo/gui/FrameRenderPasses.java	(revision 317)
+++ /trunk/src_apollo/org/apollo/gui/FrameRenderPasses.java	(revision 318)
@@ -15,5 +15,5 @@
 import org.apollo.widgets.LinkedTrack;
 import org.apollo.widgets.SampleRecorder;
-import org.apollo.widgets.SampledTrackEvo4;
+import org.apollo.widgets.SampledTrack;
 import org.expeditee.gui.Browser;
 import org.expeditee.gui.FrameGraphics;
@@ -55,5 +55,5 @@
 
 			// Conceivable could have multiple
-			LinkedList<SampledTrackEvo4> toPaint = null;
+			LinkedList<SampledTrack> toPaint = null;
 			
 			for (Item i : FreeItems.getInstance()) {
@@ -61,13 +61,13 @@
 					InteractiveWidget iw = ((WidgetCorner)i).getWidgetSource();
 					
-					if (iw != null && iw instanceof SampledTrackEvo4) {
+					if (iw != null && iw instanceof SampledTrack) {
 						
 						if (toPaint == null) {
-							toPaint = new LinkedList<SampledTrackEvo4>();
+							toPaint = new LinkedList<SampledTrack>();
 						} else if (toPaint.contains(iw)) {
 							continue;
 						}
 						
-						toPaint.add((SampledTrackEvo4)iw);
+						toPaint.add((SampledTrack)iw);
 					}
 				}
@@ -75,5 +75,5 @@
 			
 			if (toPaint != null) {
-				for (SampledTrackEvo4 trackWidget : toPaint) {
+				for (SampledTrack trackWidget : toPaint) {
 					if (g.getClip() == null
 							|| g.getClip().intersects(trackWidget.getComponant().getBounds()))
@@ -198,5 +198,5 @@
 				if (iw != null) {
 					
-					if (iw != null && (iw instanceof SampledTrackEvo4 ||
+					if (iw != null && (iw instanceof SampledTrack ||
 							iw instanceof LinkedTrack || iw instanceof SampleRecorder)) {
 						
Index: /trunk/src_apollo/org/apollo/io/SampledAudioFileImporter.java
===================================================================
--- /trunk/src_apollo/org/apollo/io/SampledAudioFileImporter.java	(revision 317)
+++ /trunk/src_apollo/org/apollo/io/SampledAudioFileImporter.java	(revision 318)
@@ -5,5 +5,5 @@
 import java.io.IOException;
 
-import org.apollo.widgets.SampledTrackEvo4;
+import org.apollo.widgets.SampledTrack;
 import org.expeditee.gui.DisplayIO;
 import org.expeditee.importer.FileImporter;
@@ -23,5 +23,5 @@
 				DisplayIO.getCurrentFrame() == null) return null;
 		
-		SampledTrackEvo4 trackWidget = SampledTrackEvo4.createFromFile(
+		SampledTrack trackWidget = SampledTrack.createFromFile(
 				f,
 				DisplayIO.getCurrentFrame(), 
Index: /trunk/src_apollo/org/apollo/meldex/MeldexConversion.java
===================================================================
--- /trunk/src_apollo/org/apollo/meldex/MeldexConversion.java	(revision 317)
+++ /trunk/src_apollo/org/apollo/meldex/MeldexConversion.java	(revision 318)
@@ -7,5 +7,5 @@
 
 import org.apollo.io.AudioIO;
-import org.apollo.widgets.SampledTrackEvo4;
+import org.apollo.widgets.SampledTrack;
 
 /**
@@ -25,5 +25,5 @@
 	 * 		If conversion fails.
 	 */
-	public static Melody toMelody(SampledTrackEvo4 trackWidget) throws IOException {
+	public static Melody toMelody(SampledTrack trackWidget) throws IOException {
 		
 		assert(trackWidget != null);
Index: /trunk/src_apollo/org/apollo/widgets/SampleRecorder.java
===================================================================
--- /trunk/src_apollo/org/apollo/widgets/SampleRecorder.java	(revision 317)
+++ /trunk/src_apollo/org/apollo/widgets/SampleRecorder.java	(revision 318)
@@ -17,4 +17,6 @@
 import java.io.IOException;
 import java.io.PipedInputStream;
+import java.util.LinkedList;
+import java.util.List;
 
 import javax.sound.sampled.AudioFormat;
@@ -49,8 +51,15 @@
 import org.expeditee.gui.Frame;
 import org.expeditee.gui.FrameGraphics;
+import org.expeditee.gui.FrameMouseActions;
 import org.expeditee.items.ItemParentStateChangedEvent;
 import org.expeditee.items.Text;
 import org.expeditee.items.widgets.InteractiveWidget;
 
+/**
+ * Records sampled audio ... the cornerstone widget to Apollo.
+ * 
+ * @author Brook Novak
+ *
+ */
 public class SampleRecorder extends InteractiveWidget 
 	implements ActionListener, Observer, MultitrackLoadListener {
@@ -85,4 +94,6 @@
 	private boolean hasExplicityStopped = false;
 	
+	private boolean isSelfDestructable = false;
+	
 	private final static int BUTTON_HEIGHT = 50;
 	private final static int LABEL_HEIGHT = 30;
@@ -97,4 +108,8 @@
 	
 	private final static int RENDER_POINTS_PER_SECOND = 20; // how many points to render each second
+	
+	private final static String SELF_DESTRUCT_OFF_META = "permanent";
+	private final static String COUNTDOWN_META = "countdown=";
+	
 	
 	public SampleRecorder(Text source, String[] args) {
@@ -105,4 +120,10 @@
 				COUNTDOWN_SETTINGS_HEIGHT + BUTTON_HEIGHT + LABEL_HEIGHT + AnimatedSampleGraph.GRAPH_HEIGHT + (4 * VERT_SPACING));
 		
+		isSelfDestructable = !containsDataTrimmedIgnoreCase(SELF_DESTRUCT_OFF_META);
+		int countdown = getStrippedDataInt(COUNTDOWN_META, 0);
+		if (countdown < 0) countdown = 0;
+		else if (countdown > MAX_COUNTDOWN_TIME)
+			countdown = MAX_COUNTDOWN_TIME;
+
 		// Create gui layout
 		recordButton = new JButton();
@@ -133,5 +154,6 @@
 		countDownSpinner = new JSpinner(model);
 		countDownSpinner.setPreferredSize(new Dimension(50, COUNTDOWN_SETTINGS_HEIGHT));
-		
+		countDownSpinner.setValue(countdown);
+
 		countDownSpinnerLabel = new JLabel("Count down:");
 		countDownSpinnerLabel.setPreferredSize(new Dimension(AnimatedSampleGraph.GRAPH_WIDTH - 50, COUNTDOWN_SETTINGS_HEIGHT));
@@ -307,7 +329,9 @@
 				
 			} else {
-
-				// Remove this temporary widget
-				removeSelf();
+				
+				if (isSelfDestructable) {
+					// Remove this temporary widget
+					removeSelf();
+				}
 			
 				// Spawn an audio track using the actual bytes and audio format buffered from
@@ -326,5 +350,5 @@
 					 if (!shouldPlayback) initiationTime = -1;
 					 
-					 SampledTrackEvo4 trackWidget = SampledTrackEvo4.createFromMemory(
+					 SampledTrack trackWidget = SampledTrack.createFromMemory(
 							 audioByteReader.bufferedAudioBytes.toByteArray(), 
 							 audioByteReader.audioFormat, 
@@ -336,5 +360,16 @@
 							 null);
 					 
-					 targetFrame.addAllItems(trackWidget.getItems());
+					 if (isSelfDestructable) {
+						 
+						 targetFrame.addAllItems(trackWidget.getItems());
+		
+					 } else {
+						 
+						 FrameMouseActions.pickup(trackWidget.getItems());
+							
+						// Reset the state
+						setState(WidgetState.Ready, "Ready"); 
+						
+					 }
 	
 					 
@@ -379,5 +414,18 @@
 		return null;
 	}
-	
+
+	@Override
+	protected List<String> getData() {
+
+		List<String> data = new LinkedList<String>();
+		
+		if (isSelfDestructable) 
+			data.add(SELF_DESTRUCT_OFF_META);
+		
+		data.add(COUNTDOWN_META + countDownSpinner.getValue());
+
+		return data;
+	}
+
 	@Override
 	protected void onParentStateChanged(int eventType) {
Index: /trunk/src_apollo/org/apollo/widgets/SampledTrack.java
===================================================================
--- /trunk/src_apollo/org/apollo/widgets/SampledTrack.java	(revision 318)
+++ /trunk/src_apollo/org/apollo/widgets/SampledTrack.java	(revision 318)
@@ -0,0 +1,1869 @@
+package org.apollo.widgets;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.geom.Rectangle2D;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.UnsupportedAudioFileException;
+import javax.swing.SwingUtilities;
+
+import org.apollo.audio.ApolloPlaybackMixer;
+import org.apollo.audio.ApolloSubjectChangedEvent;
+import org.apollo.audio.SampledTrackModel;
+import org.apollo.audio.TrackSequence;
+import org.apollo.audio.structure.AudioStructureModel;
+import org.apollo.audio.structure.TrackGraphNode;
+import org.apollo.audio.util.FrameLayoutDaemon;
+import org.apollo.audio.util.SoundDesk;
+import org.apollo.audio.util.Timeline;
+import org.apollo.audio.util.TrackMixSubject;
+import org.apollo.gui.EditableSampledTrackGraphView;
+import org.apollo.gui.ExpandedTrackManager;
+import org.apollo.gui.PlaybackControlPopup;
+import org.apollo.gui.SampledTrackGraphView;
+import org.apollo.gui.SampledTrackGraphView.EffecientInvalidator;
+import org.apollo.io.AudioIO;
+import org.apollo.io.AudioPathManager;
+import org.apollo.io.IconRepository;
+import org.apollo.io.AudioIO.AudioFileLoader;
+import org.apollo.items.EmulatedTextItem;
+import org.apollo.items.EmulatedTextItem.TextChangeListener;
+import org.apollo.mvc.Observer;
+import org.apollo.mvc.Subject;
+import org.apollo.mvc.SubjectChangedEvent;
+import org.apollo.util.AudioMath;
+import org.apollo.util.PopupReaper;
+import org.apollo.util.TrackModelHandler;
+import org.apollo.util.TrackModelLoadManager;
+import org.apollo.util.TrackNameCreator;
+import org.expeditee.gui.Browser;
+import org.expeditee.gui.DisplayIO;
+import org.expeditee.gui.Frame;
+import org.expeditee.gui.FrameGraphics;
+import org.expeditee.gui.MouseEventRouter;
+import org.expeditee.gui.PopupManager;
+import org.expeditee.items.ItemParentStateChangedEvent;
+import org.expeditee.items.ItemUtils;
+import org.expeditee.items.Text;
+import org.expeditee.items.widgets.HeavyDutyInteractiveWidget;
+import org.expeditee.items.widgets.InteractiveWidget;
+import org.expeditee.items.widgets.InteractiveWidgetInitialisationFailedException;
+import org.expeditee.items.widgets.InteractiveWidgetNotAvailableException;
+
+/**
+ * The sampled track widgets in apollo.
+ * 
+ * @author Brook Novak
+ *
+ */
+public class SampledTrack extends HeavyDutyInteractiveWidget 
+	implements TrackModelHandler, EffecientInvalidator, Observer {
+
+	/** The observered subject. Can be null */
+	private SampledTrackModel trackModel = null;
+	
+	/** Used for the loading phase. Can change serveal times of a lifetime. for example after import and save it becomes local */
+	private String loadFilenameArgument = null; // preproceeds with ARG_IMPORT_TAG if importing.
+	
+	private String localFileName; // Immutable - assigned on constuction
+	
+	/** Used for dumping audio to a temp file when deleted - to free memory.
+	 * Protocol: Set when the widget is deleted. Unset when loaded. */
+	private File recoveryFile = null;
+	
+	private boolean shouldOmitAudioIndexing = false;
+	
+	private EditableSampledTrackGraphView fulltrackView;
+	private EmulatedTextItem nameLabel; 
+	private PlaybackPopup playbackControlPopup = null;
+	private TrackMixSubject trackMix; // immutable - like local filename
+	
+	/** The amount of load bar that is allocated to file loading */
+	private static final float FILE_LOADING_PERCENT_RANGE = 0.95f;
+
+	/** For parsing arguments */
+	public static final String ARG_IMPORT_TAG = "if=";
+	
+	/** For parsing metadata */
+	public static final String META_LOCALNAME_TAG = "ln=";
+	
+	private static final Stroke FREESPACE_OUTLINING = new BasicStroke(2.0f);
+	
+	private static final Color SEMI_TRANSPARENT_FREESPACE_BACKCOLOR = new Color(
+			FREESPACE_BACKCOLOR.getRed(), FREESPACE_BACKCOLOR.getGreen(),
+			FREESPACE_BACKCOLOR.getBlue(), 128);
+	
+	
+	/** If a track widget has a data string thaty contains META_SHOULD_INDEX_AUDIO_TAG, then
+	 * the track should not be indexed for searching the audio.... default isdoes not exist
+	 */
+	public static final String META_DONT_INDEX_AUDIO_TAG = "dontindexaudio";
+
+	/**
+	 * Constructor used for loading directly from given bytes.
+	 * 
+	 * @param source
+	 * 
+	 * @param audioBytes
+	 * 
+	 * @param format
+	 * 
+	 * @param mixTemplate
+	 * 		Can be null
+	 * 
+	 */
+	private SampledTrack(Text source, byte[] audioBytes, AudioFormat format, TrackMixSubject mixTemplate) {
+		
+		super(source, new EditableSampledTrackGraphView(), 
+				100, -1, 
+				FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, 
+				TrackWidgetCommons.CACHE_DEPTH,
+				true);
+
+		// Must set upon construction - always
+		localFileName = AudioPathManager.generateLocateFileName("wav");
+		updateData(META_LOCALNAME_TAG, META_LOCALNAME_TAG + localFileName);
+		
+		trackModel = new SampledTrackModel(audioBytes, format, localFileName);
+		
+		// Ensure that the model is marked as modiefied so that it will save
+		trackModel.setAudioModifiedFlag(true);
+		
+		trackModel.setName(getStrippedDataString(TrackWidgetCommons.META_NAME_TAG));
+
+		// Create the immutable mix subject
+		if (mixTemplate == null)
+			trackMix = SoundDesk.getInstance().getOrCreateMix(SoundDesk.createPureLocalChannelID(this));
+		
+		else trackMix = SoundDesk.getInstance().createMix(
+				SoundDesk.createPureLocalChannelID(this), 
+				mixTemplate.getVolume(), 
+				mixTemplate.isMuted(),
+				true);
+				
+		// Keep meta as constant as possible for best results
+		updateData(TrackWidgetCommons.META_RUNNINGMSTIME_TAG, TrackWidgetCommons.META_RUNNINGMSTIME_TAG + getRunningMSTimeFromRawAudio());
+		
+		createGUI();
+		
+		initObservers();
+		
+	}
+
+	/**
+	 * Constructor called by Expeditee. Eventually loads audio from file.
+	 * 
+	 * @param source
+	 * 
+	 * @param args
+	 * 		Can have a ARG_IMPORT_TAG argument...
+	 */
+	public SampledTrack(Text source, String[] args) {
+		super(source, new EditableSampledTrackGraphView(), 
+				100, -1, 
+				FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, 
+				TrackWidgetCommons.CACHE_DEPTH);
+		
+		// Read the metadata
+		localFileName = getStrippedDataString(META_LOCALNAME_TAG);
+
+		// Ensure the local filename is assigned - even if file does not exist...
+		// Also it could be importing a file...
+		if (localFileName == null) {
+			localFileName = AudioPathManager.generateLocateFileName("wav");
+			updateData(META_LOCALNAME_TAG, META_LOCALNAME_TAG + localFileName);
+		}
+
+		trackMix = SoundDesk.getInstance().getOrCreateMix(SoundDesk.createPureLocalChannelID(this));
+
+		loadFilenameArgument = localFileName;
+		if (args != null) { // parse args
+			for (String arg : args) {
+				if (arg != null && arg.length() > ARG_IMPORT_TAG.length()) {
+					loadFilenameArgument = arg;
+				}
+			}
+		}
+		
+		createGUI();
+
+	}
+	
+	private void initObservers() {
+		
+		// Listen for model events
+		trackModel.addObserver(this);
+		trackModel.addObserver(fulltrackView);
+		fulltrackView.setMix(trackMix); // use the same mix/channel for playback
+		ExpandedTrackManager.getInstance().addObserver(this);
+		
+		// Show graph as being selected if expanded / pending to expand.
+		if (ExpandedTrackManager.getInstance().isTrackInExpansionSelection(trackModel)) {
+			fulltrackView.setBackColor(new Color(100, 100, 100), new Color(120, 120, 120));
+		} else {
+			fulltrackView.setBackColor(SampledTrackGraphView.DEFAULT_BACKGROUND_COLOR, SampledTrackGraphView.DEFAULT_BACKGROUND_HIGHTLIGHTS_COLOR);
+		}
+
+	}
+
+	private void createGUI() {
+		
+		// Set widget as a fixed size widget- using width from last recorded width in the meta data.
+		int width = getStrippedDataInt(TrackWidgetCommons.META_LAST_WIDTH_TAG, -1);
+		if (width >= 0 && width < FrameLayoutDaemon.MIN_TRACK_WIDGET_WIDTH) {
+			width = FrameLayoutDaemon.MIN_TRACK_WIDGET_WIDTH;
+		}
+		
+		if (width < 0) {
+			setSize(-1, -1, 
+					FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, 
+					FrameLayoutDaemon.TRACK_WIDGET_DEFAULT_WIDTH, FrameLayoutDaemon.TRACK_WIDGET_HEIGHT);
+		} else {
+			setSize(width, width, 
+					FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, 
+					width, FrameLayoutDaemon.TRACK_WIDGET_HEIGHT);
+		}
+		
+		shouldOmitAudioIndexing = containsDataTrimmedIgnoreCase(META_DONT_INDEX_AUDIO_TAG);
+
+		playbackControlPopup = new PlaybackPopup();
+		
+		fulltrackView = (EditableSampledTrackGraphView)_swingComponent;
+		fulltrackView.setAlwaysFullView(true);
+		
+		/*fulltrackView.setInvalidator(new EffecientInvalidator() {
+			public void onGraphDirty(SampledTrackGraphView graph, Rectangle dirty) {
+		        dirty.translate(getX(), getY() - 1);
+		        FrameGraphics.invalidateArea(dirty); 
+		        FrameGraphics.refresh(true); 
+			}
+		});*/
+		
+		// Auto-show playback popup.
+		// Block messages if the track is expanded
+		fulltrackView.addMouseMotionListener(new MouseMotionListener() {
+
+			public void mouseDragged(MouseEvent e) {
+				if (nameLabel != null) {
+					if (nameLabel.onMouseDragged(e)) {
+						e.consume();
+					}
+				}
+			}
+
+			public void mouseMoved(MouseEvent e) {
+
+				if (nameLabel != null) {
+					nameLabel.onMouseMoved(e, fulltrackView);
+				}
+				
+				if (playbackControlPopup == null) {
+					playbackControlPopup = new PlaybackPopup();
+				} 
+
+				// Only show popup iff there is nothing expanded / expanding.
+				if (!PopupManager.getInstance().isShowing(playbackControlPopup) &&
+						!ExpandedTrackManager.getInstance().doesExpandedTrackExist()) {	
+					
+					// Get rid of all popups
+					PopupManager.getInstance().hideAutohidePopups();
+					
+					Rectangle animationSource = _swingComponent.getBounds();
+					
+					// Determine where popup should show
+					int x = SampledTrack.this.getX();
+					int y = SampledTrack.this.getY() - playbackControlPopup.getHeight() - 2; // by default show above
+					
+					// I get sick.dizzy from the popup expanding from the whole thing...
+					animationSource.height = 1;
+					animationSource.width = Math.min(animationSource.width, playbackControlPopup.getWidth());
+					
+					if (y < 0) {
+						y = SampledTrack.this.getY() + SampledTrack.this.getHeight() + 2;
+						animationSource.y = y - 2;
+					} 
+
+					// Animate the popup
+					PopupManager.getInstance().showPopup(
+							playbackControlPopup, 
+							new Point(x, y), 
+							fulltrackView,
+							PopupManager.getInstance().new ExpandShrinkAnimator(
+									animationSource,
+									Color.LIGHT_GRAY));
+					
+					PopupReaper.getInstance().initPopupLifetime(
+							playbackControlPopup, 
+							PopupManager.getInstance().new ExpandShrinkAnimator(
+									animationSource,
+									Color.LIGHT_GRAY), 
+									TrackWidgetCommons.POPUP_LIFETIME);
+					
+				} else {
+					PopupReaper.getInstance().revivePopup(playbackControlPopup, TrackWidgetCommons.POPUP_LIFETIME);
+				}
+				
+			}
+
+		});
+		
+		// Expand on double click
+		// Block messages if the track is expanded
+		fulltrackView.addMouseListener(new MouseListener() {
+
+			public void mouseClicked(MouseEvent e) {
+				if (trackModel == null) return;
+				
+				if (nameLabel != null) {
+					if (nameLabel.onMouseClicked(e)) {
+						e.consume();
+						return;
+					}
+				}
+				
+				if (e.getClickCount() >= 2) {
+					
+					expand(e.isControlDown());
+					
+				}
+
+			}
+
+			public void mouseEntered(MouseEvent e) {
+			}
+
+			public void mouseExited(MouseEvent e) {
+			}
+
+			public void mousePressed(MouseEvent e) {
+				if (nameLabel != null) {
+					if (nameLabel.onMousePressed(e)) {
+						e.consume();
+						return;
+					}
+				}
+				
+				// Consume events if track is selected for expansion
+				if (ExpandedTrackManager.getInstance().isTrackInExpansionSelection(trackModel) &&
+						e.getButton() != MouseEvent.BUTTON1) { // but allow selection only
+					e.consume();
+				}
+			}
+
+			public void mouseReleased(MouseEvent e) {
+				if (nameLabel != null) {
+					if (nameLabel.onMouseReleased(e)) {
+						e.consume();
+					}
+				}
+				
+				// Consume events if track is selected for expansion
+				if (ExpandedTrackManager.getInstance().isTrackInExpansionSelection(trackModel)) {
+					e.consume();
+				}
+				
+		
+			}
+		
+		});
+		
+		fulltrackView.addKeyListener(new KeyListener() {
+
+			public void keyPressed(KeyEvent e) {
+				if (nameLabel != null) {
+					if (nameLabel.onKeyPressed(e, fulltrackView)) {
+						e.consume();
+					}
+				}
+			}
+
+			public void keyReleased(KeyEvent e) {
+				if (nameLabel != null) {
+					if (nameLabel.onKeyReleased(e, fulltrackView)) {
+						e.consume();
+					}
+				}
+				
+				// Toggle indexing
+				if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_I) {
+					setShouldOmitIndexAudio(!shouldOmitIndexAudio());
+					FrameGraphics.refresh(true);
+				}
+
+			}
+
+			public void keyTyped(KeyEvent e) {
+			}
+			
+		});
+		
+		
+		
+		nameLabel = new EmulatedTextItem(_swingComponent, new Point(10, 20));
+		nameLabel.setBackgroundColor(Color.WHITE);	
+		
+		String metaName = getStrippedDataString(TrackWidgetCommons.META_NAME_TAG);
+		if (metaName == null) metaName = "Untitled";
+		nameLabel.setText(metaName); 
+
+		nameLabel.addTextChangeListener(new TextChangeListener() { //  a little bit loopy!
+
+			public void onTextChanged(Object source, String newLabel) {
+				if (trackModel != null && !nameLabel.getText().equals(trackModel.getName())) {
+					trackModel.setName(nameLabel.getText());
+					SampledTrack.this.updateData(TrackWidgetCommons.META_NAME_TAG, trackModel.getName());
+					
+				}
+			}
+
+		});
+		
+		// Make sure the above mouse listeners get first serve
+		fulltrackView.reAddListeners();
+		
+		// Make sure border color is correct
+		updateBorderColor();
+		setWidgetEdgeThickness(TrackWidgetCommons.STOPPED_TRACK_EDGE_THICKNESS);
+
+	}
+
+	/**
+	 * Creates a {@link SampledTrack} instantly from audio bytes in memory and adds it to a given frame.
+	 * @param audioBytes
+	 * 			The audio samples.
+	 * 
+	 * @param format
+	 * 			The format of the audio samples.
+	 * 
+	 * @param targetFrame
+	 * 			The frame that the widget will reside.
+	 * 
+	 * @param x
+	 * 
+	 * @param y
+	 * 
+	 * @param specificInitTime
+	 * 		Negative to determine init time from x position. Positive to override
+	 * 		and determine x position from given init time
+	 * 
+	 * @param name
+	 * 			The name of the new track. If null then a default name will be used.
+	 * 
+	 * @param mixTemplate 
+	 * 			The mix data to clone for the new sampled tracks mix.
+	 * 			If null then default mix settings will be used.
+	 * 
+	 * @return The {@link SampledTrack} instance added to the given frame.
+	 * 
+	 */
+	public static SampledTrack createFromMemory(
+			byte[] audioBytes, 
+			AudioFormat format,
+			Frame targetFrame,
+			int x, int y,
+			long specificInitTime,
+			String name,
+			TrackMixSubject mixTemplate) {
+		
+		assert (targetFrame != null);
+		assert (audioBytes != null);
+		assert (format != null);
+		
+		Text source = new Text(targetFrame.getNextItemID());
+		source.setParent(targetFrame);
+		
+		
+		
+		long runningtime = AudioMath.framesToMilliseconds(
+				audioBytes.length / format.getFrameSize(), format);
+		
+		
+		Timeline tl = FrameLayoutDaemon.getInstance().getTimeline(targetFrame);
+		
+		int width = -1;
+		if (tl != null) {
+			width = (int)((double)runningtime / tl.getTimePerPixel());
+		}	
+		
+		// Clamp width to something reasonable.
+		if (width > 0 && width < FrameLayoutDaemon.MIN_TRACK_WIDGET_WIDTH) width = FrameLayoutDaemon.MIN_TRACK_WIDGET_WIDTH;
+
+		if (specificInitTime < 0) {
+			
+			if (tl == null) specificInitTime = 0;
+			else specificInitTime = tl.getMSTimeAtX(x);
+			
+		} else if (tl != null) { // if defined a specific ms time and a timeline is avialable
+			
+			x = tl.getXAtMSTime(specificInitTime);
+		} 
+		
+		source.setPosition(x, y);
+			
+		// Setup metadata
+		LinkedList<String> data = new LinkedList<String>();
+
+		data.add(TrackWidgetCommons.META_INITIATIONTIME_TAG + specificInitTime);
+		data.add(TrackWidgetCommons.META_LAST_WIDTH_TAG + width); // although layout manager will handle, just to quick set
+		if (name != null) data.add(TrackWidgetCommons.META_NAME_TAG + name);
+		
+		source.setData(data);
+		
+		SampledTrack strack = new SampledTrack(source, audioBytes, format, mixTemplate);
+
+		return strack;
+	}
+	
+	
+	/**
+	 * Creates a {@link SampledTrack} from file - which is tyo be imported into apollos
+	 * 
+	 * @param targetFrame
+	 * 			The frame that the widget will reside.
+	 * 
+	 * @param file
+	 * 			The audio file to import
+	 * 
+	 * @return The {@link SampledTrack} instance added to the given frame.
+	 * 
+	 */
+	public static SampledTrack createFromFile(File file, Frame targetFrame, int x, int y) {
+		assert (targetFrame != null);
+		assert (file != null);
+		
+	
+		String[] args = new String[] { 
+				ARG_IMPORT_TAG + file.getAbsolutePath()
+				};
+		
+		Text source = new Text(
+				targetFrame.getNextItemID(),
+				ItemUtils.GetTag(ItemUtils.TAG_IWIDGET) + ":" + formatArgs(args));
+		
+		source.setParent(targetFrame);
+		
+		source.setPosition(x, y);
+		
+		long initTime = FrameLayoutDaemon.getInstance().getMSAtX(x, targetFrame);
+
+		// Setup metadata
+		LinkedList<String> data = new LinkedList<String>();
+
+		data.add(TrackWidgetCommons.META_INITIATIONTIME_TAG + initTime);
+		data.add(TrackWidgetCommons.META_LAST_WIDTH_TAG + FrameLayoutDaemon.TRACK_WIDGET_DEFAULT_WIDTH); // once loaded then the layout will handle proper size
+		data.add(TrackWidgetCommons.META_NAME_TAG + file.getName());
+		
+		source.setData(data);
+
+		SampledTrack strack = new SampledTrack(source, args);
+
+		return strack;
+	}
+
+	
+	@Override
+	protected String[] getArgs() {
+		return null;
+	}
+
+	@Override
+	protected List<String> getData() {
+
+		List<String> data = new LinkedList<String>();
+		
+		data.add(META_LOCALNAME_TAG + localFileName);
+		
+		if (shouldOmitAudioIndexing) data.add(META_DONT_INDEX_AUDIO_TAG);
+		
+		String lastName = getName();
+		
+		if (lastName != null)
+			data.add(TrackWidgetCommons.META_NAME_TAG + lastName);
+		
+		data.add(TrackWidgetCommons.META_LAST_WIDTH_TAG + getWidth());
+		
+		Long initTime = null;
+		
+		Frame f = getParentFrame();
+		if (f != null) {
+			TrackGraphNode tinf = AudioStructureModel.getInstance().getTrackGraphInfo(localFileName, f.getName());
+			if (tinf != null) {
+				initTime = tinf.getInitiationTime();
+			}
+		}
+		
+		if (initTime == null) initTime = getInitiationTimeFromMeta(); // old meta
+		if (initTime == null) initTime = 0L;
+
+		data.add(TrackWidgetCommons.META_INITIATIONTIME_TAG + initTime);
+		
+
+		return data;
+	}
+	
+	
+	@Override
+	public InteractiveWidget copy() 
+		throws InteractiveWidgetNotAvailableException, InteractiveWidgetInitialisationFailedException {
+		
+		if (trackModel == null) {
+			return super.copy();
+			
+		} else {
+
+			return SampledTrack.createFromMemory(
+					trackModel.getAllAudioBytesCopy(),
+					trackModel.getFormat(), 
+					getSource().getParentOrCurrentFrame(), 
+					getX(), 
+					getY(),
+					-1,
+					TrackNameCreator.getNameCopy(trackModel.getName()),
+					trackMix);
+		}
+		
+	}
+
+	@Override
+	public int getLoadDelayTime() {
+		return 0;
+	}
+
+	@Override
+	protected float loadWidgetData() {
+		
+		// Load audio from file
+		File f = null;
+		boolean isImporting = false;
+		
+		if (recoveryFile != null) { // are we recovering?
+			
+			setLoadScreenMessage("Recovering audio file...");
+			
+			if (recoveryFile.exists()) f = recoveryFile;
+		}
+		
+		if (f == null) { // must be importing or loading from local repository
+
+			if (loadFilenameArgument != null) {
+				if (loadFilenameArgument.startsWith(ARG_IMPORT_TAG)) { // importing a file?
+					
+					setLoadScreenMessage("Importing audio file...");
+					
+					isImporting = true;
+					f = new File(loadFilenameArgument.substring(ARG_IMPORT_TAG.length()));
+					
+				} else {
+					assert(loadFilenameArgument == localFileName);
+					
+					setLoadScreenMessage("Loading audio file...");
+					
+					f = new File(
+							AudioPathManager.AUDIO_HOME_DIRECTORY + loadFilenameArgument);
+				}
+				
+				if (!f.exists() || !f.isFile()) f = null;
+			} 
+			
+		}
+
+		if (f == null) {
+			if (recoveryFile != null) {
+				setLoadScreenMessage("Recovery file missing");
+			} else {
+				setLoadScreenMessage("File missing");
+			}
+			
+			return LOAD_STATE_FAILED;
+		}
+		
+		if (trackModel != null && trackModel.getFilepath() != null &&
+				trackModel.equals(f.getPath())) {
+			
+			// already have loaded
+			assert(!isImporting);
+			
+		} else {
+
+			try {
+				
+				trackModel = TrackModelLoadManager.getInstance().load(f.getPath(), localFileName, this, false);
+				
+				if (trackModel == null) { // load operation canceled
+					assert(hasCancelBeenRequested()); 
+					return LOAD_STATE_INCOMPLETED;
+					
+				} else if(isImporting) { // ensure that file path is null - since not yet saved
+					trackModel.setFilepath(null);
+					
+				} else if (recoveryFile != null) {
+					
+					// If recovering - might be recovering an existing track that had been
+					// saved to the repository .. thus re-use the old file
+					trackModel.setFilepath(AudioPathManager.AUDIO_HOME_DIRECTORY + localFileName);
+				}
+				
+			} catch (IOException e) {
+				e.printStackTrace();
+				setLoadScreenMessage("Failed to load audio file");
+				return LOAD_STATE_FAILED;
+				
+			} catch (UnsupportedAudioFileException e) {
+				e.printStackTrace();
+				setLoadScreenMessage("Format not supported");
+				return LOAD_STATE_FAILED;
+			} catch (OutOfMemoryError e) {
+				e.printStackTrace();
+				setLoadScreenMessage("Out of memory");
+				return LOAD_STATE_FAILED;
+			}
+			
+		}
+		
+		// If was imported / recovered, then set as being modified
+		if (isImporting || recoveryFile != null) {
+			trackModel.setAudioModifiedFlag(true);
+		}
+		
+		// Set the name for this track
+		String name = getStrippedDataString(TrackWidgetCommons.META_NAME_TAG);
+		if (name != null) 
+			trackModel.setName(name);
+		
+		initObservers(); // sets default name if non set
+		
+		// If was recovering - get rid of temp data
+		if (recoveryFile != null) {
+			recoveryFile.delete();
+			recoveryFile = null; // ensure that out of a recovery state
+		}
+		
+		// Keep meta as constant as possible for best results
+		updateData(TrackWidgetCommons.META_RUNNINGMSTIME_TAG, TrackWidgetCommons.META_RUNNINGMSTIME_TAG + getRunningMSTimeFromRawAudio());
+		
+		// Must make sure that this track is on the track graph model
+		try {
+			SwingUtilities.invokeAndWait(new Runnable() {
+				
+				public void run() {
+					
+					Frame parent = getParentFrame();
+					String pfname = (parent != null) ?  parent.getName() : null;
+					
+					TrackGraphNode tinf = AudioStructureModel.getInstance().getTrackGraphInfo(localFileName, pfname);
+					if (tinf == null) {
+
+						// Determine new initation time according to position
+						long initTime = (parent != null) ? 
+								FrameLayoutDaemon.getInstance().getMSAtX(getX(), parent)
+								: 0;
+
+						// Keep TrackGraphModel consistant
+						AudioStructureModel.getInstance().onTrackWidgetAnchored(
+								localFileName, 
+								pfname, 
+								initTime, 
+								AudioMath.framesToMilliseconds(trackModel.getFrameCount(), trackModel.getFormat()),
+								getName());
+						
+					}
+				}
+			});
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		} catch (InvocationTargetException e) {
+			e.printStackTrace();
+		}
+		
+		
+		// Notify layout manager - not really needed but to be extra safe force the daemon
+		// to be super consistant
+		FrameLayoutDaemon.getInstance().forceRecheck();
+
+		return LOAD_STATE_COMPLETED;
+	}
+	
+	/**
+	 * Used by save manager
+	 */
+	public boolean doesNeedSaving() {
+		return (trackModel != null && (trackModel.isAudioModified() || trackModel.getFilepath() == null));
+	}
+
+	/**
+	 * Used by save manager
+	 */
+	public String getSaveName() {
+		
+		if (trackModel != null && trackModel.getName() != null)
+			return "Sampled Track: " + trackModel.getName();
+		else return "A Sampled Track";
+		
+	}
+
+	/**
+	 * Saves the audio bytes to file
+	 */
+	@Override
+	protected void saveWidgetData() {
+		
+		if (trackModel == null) return; // nothing to save
+		
+		// If saving for the file time then get a filename
+		if (trackModel.getFilepath() == null) {
+			trackModel.setFilepath(AudioPathManager.AUDIO_HOME_DIRECTORY + localFileName);
+			loadFilenameArgument = localFileName; // set to now local, next load will be local
+		}
+		
+		// Save audio bytes.
+		try {
+			
+			AudioIO.savePCMAudioToWaveFile(
+					trackModel.getFilepath(), 
+					trackModel.getAllAudioBytes(), // Safe: arrays are immutable
+					trackModel.getFormat());
+			
+			// Reset modified flag
+			trackModel.setAudioModifiedFlag(false);
+			
+		} catch (IOException e) {
+			e.printStackTrace();
+		} catch (UnsupportedAudioFileException e) {
+			e.printStackTrace();
+		}
+
+		// Ensure audio bytes can be collected if this has expired
+		if (isExpired()) {
+
+			try {
+				SwingUtilities.invokeAndWait(new Runnable() {
+					public void run() {
+						releaseMemory(true);
+					}
+				});
+			} catch (InterruptedException e) {
+				e.printStackTrace();
+			} catch (InvocationTargetException e) {
+				e.printStackTrace();
+			}
+			
+		}
+		
+	
+	}
+
+	@Override
+	public void onDelete() {
+		super.onDelete();
+		// Its nice to keep everything layed out as much as possible
+		FrameLayoutDaemon.getInstance().resumeLayout(this);
+	}
+
+	@Override
+	protected void unloadWidgetData() {
+
+		// Still needs to save?
+		if (doesNeedSaving() || trackModel == null)
+			return; // Release memory later when saved
+
+		try {
+			// Release memory - on swing thread to avoid nullified model data wil painting / editing.
+			SwingUtilities.invokeAndWait(new Runnable() {
+				public void run() {
+					releaseMemory(true);
+				}
+			});
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		} catch (InvocationTargetException e) {
+			e.printStackTrace();
+		}
+
+		
+	}
+	
+	@Override
+	protected void tempUnloadWidgetData() {
+
+		// Need unloading?
+		if (trackModel == null) return;
+		
+		// Get rid of old temporary files
+		if (recoveryFile != null && recoveryFile.exists()) {
+			recoveryFile.delete();
+		}
+		
+		// Dump memory if needs saving
+		if (trackModel.isAudioModified() || trackModel.getFilepath() == null) {
+
+			try {
+				// Always be unique
+				String uniqueID = AudioPathManager.generateLocateFileName("wav");
+				recoveryFile = File.createTempFile("APOLLO_BACKUP" + uniqueID, null);
+				
+			} catch (IOException e) {
+				e.printStackTrace();
+				return; // cannot dump ... must keep in memory.
+			}
+
+			// Dump audio to file
+			try {
+				AudioIO.savePCMAudioToWaveFile(
+						recoveryFile.getAbsolutePath(), 
+						trackModel.getAllAudioBytes(), 
+						trackModel.getFormat());
+			} catch (IOException e1) {
+				e1.printStackTrace();
+				return; // cannot dump ... must keep in memory.
+			} catch (UnsupportedAudioFileException e1) {
+				e1.printStackTrace();
+				return; // cannot dump ... must keep in memory.
+			}
+		}
+		
+		try {
+			// Release memory - on swing thread to avoid nullified model data wil painting / editing.
+			SwingUtilities.invokeAndWait(new Runnable() {
+				public void run() {
+					releaseMemory(false);
+				}
+			});
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		} catch (InvocationTargetException e) {
+			e.printStackTrace();
+		}
+
+	}
+
+	/**
+	 * To be called by the swing thread only
+	 * This is nessessary to avoid runtime memory leaks!!
+	 */
+	private void releaseMemory(boolean onlyIfExpired) {
+		
+		if (!onlyIfExpired || (onlyIfExpired && isExpired())) {
+			
+			if (trackModel != null) {
+				
+				trackModel.removeObserver(fulltrackView);
+				trackModel.removeObserver(this);
+				trackModel = null;
+				
+				assert (fulltrackView.getObservedSubject() == null);
+				assert (fulltrackView.getMix() == null);
+			}
+			
+			fulltrackView.releaseBuffer();
+			
+			ExpandedTrackManager.getInstance().removeObserver(this);
+
+		}
+		
+	}
+
+	/**
+	 * Global Track model re-use ....
+	 */
+	public SampledTrackModel getSharedSampledTrackModel(String localfilename) {
+		if (trackModel != null &&
+				trackModel.getLocalFilename().equals(localfilename)) {
+			assert(localfilename.equals(localFileName));
+			return trackModel;
+		}
+		return null;
+
+	}
+	
+	/**
+	 * @see #getAudioFormat()
+	 * 
+	 * @return
+	 * 		The audio bytes for this track widget. Null if not loaded.
+	 */
+	public byte[] getAudioBytes() {
+		return (trackModel != null) ? trackModel.getAllAudioBytes() : null;
+	}
+	
+	/**
+	 * @see #getAudioBytes()
+	 * 
+	 * @return
+	 * 		The audio format of the audio bytes for this track widget. Null if not loaded.
+	 */
+	public AudioFormat getAudioFormat() {
+		return (trackModel != null) ? trackModel.getFormat() : null;
+	}
+	
+	/**
+	 * <b>Warning</b> if the path happens to be a path of a recovery file
+	 * then it is deleted if the widget is reloaded.
+	 * 
+	 * @return 
+	 * 		A full filepath to load the audio from. Never null. The path	
+	 * 		may lead to a out of date file or no file.
+	 */
+	public String getLatestSavedAudioPath() {
+		
+		if (recoveryFile != null && recoveryFile.exists()) {
+			return recoveryFile.getAbsolutePath();
+		}
+			
+		return AudioPathManager.AUDIO_HOME_DIRECTORY + localFileName;
+		
+		
+	}
+
+	public void onGraphDirty(SampledTrackGraphView graph, Rectangle dirty) {
+        dirty.translate(getX(), getY() - 1);
+        FrameGraphics.invalidateArea(dirty); 
+        FrameGraphics.refresh(true);
+	}
+
+	public Subject getObservedSubject() {
+		return null;
+	}
+	
+
+	@Override
+	protected void onSizeChanged() {
+		super.onSizeChanged();
+		// Keep meta as constant as possible for best reults
+		updateData(TrackWidgetCommons.META_LAST_WIDTH_TAG, TrackWidgetCommons.META_LAST_WIDTH_TAG + getWidth());
+	}
+
+	/**
+	 * Responds to model changed events by updating the GUI ...
+	 */
+	public void modelChanged(Subject source, SubjectChangedEvent event) {
+		
+		// If the expansion selection has changed - check to see if was for this
+		if (source == ExpandedTrackManager.getInstance()) {
+			
+			// Show graph as being selected if expanded / pending to expand.
+			if (trackModel != null &&
+					ExpandedTrackManager.getInstance().isTrackInExpansionSelection(trackModel)) {
+				fulltrackView.setBackColor(new Color(100, 100, 100), new Color(120, 120, 120));
+			} else {
+				fulltrackView.setBackColor(SampledTrackGraphView.DEFAULT_BACKGROUND_COLOR, SampledTrackGraphView.DEFAULT_BACKGROUND_HIGHTLIGHTS_COLOR);
+			}
+			
+			return;
+		}
+		
+		Frame parent = null;
+
+		switch (event.getID()) {
+		
+			case ApolloSubjectChangedEvent.LOAD_STATUS_REPORT:
+				// If this widget is loading - then update the load status
+				if (isInLoadProgress()) {
+					
+					// If the load has been cancelled, then cancel the loader
+					if (hasCancelBeenRequested()) {
+						
+						AudioFileLoader loader = (AudioFileLoader)source;
+						loader.cancelLoad();
+						
+					} else {
+						float perc = ((Float)event.getState()).floatValue();
+						if (perc > 1.0f) perc = 1.0f;
+						perc *= FILE_LOADING_PERCENT_RANGE; // Dont stretch load to all of bar - still will have more work to do
+						updateLoadPercentage(perc);
+					}
+					
+				}
+				
+				break;
+				
+			case ApolloSubjectChangedEvent.NAME_CHANGED:
+				if (trackModel != null && !nameLabel.getText().equals(trackModel.getName())) {
+					nameLabel.setText(trackModel.getName());
+				}
+				
+				// Get graph model consistant
+				parent = getParentFrame();
+				String pfname = (parent != null) ?  parent.getName() : null;
+				
+				AudioStructureModel.getInstance().onTrackWidgetNameChanged(
+						localFileName, 
+						pfname, 
+						getName());
+						
+				break;
+				
+			case ApolloSubjectChangedEvent.AUDIO_INSERTED:
+			case ApolloSubjectChangedEvent.AUDIO_REMOVED:
+				
+				long newRunningTime = getRunningMSTimeFromRawAudio();
+				assert(newRunningTime > 0);
+				
+				long oldRunningTime = getRunningMSTimeFromMeta();
+				
+				// Keep meta as constant as possible for best reults
+				updateData(TrackWidgetCommons.META_RUNNINGMSTIME_TAG, TrackWidgetCommons.META_RUNNINGMSTIME_TAG + newRunningTime);
+
+				if (trackModel != null) {
+					
+					parent = getParentFrame();
+					
+					// Keep TrackGraphModel consistant
+					AudioStructureModel.getInstance().onTrackWidgetAudioEdited(
+							localFileName,
+							(parent != null) ? parent.getName() : null, 
+							newRunningTime);
+					
+					if (trackModel.getSelectionStart() == 0 && oldRunningTime > newRunningTime) {
+						long inittime = getInitiationTimeFromMeta();
+						inittime += (oldRunningTime - newRunningTime);
+						updateData(TrackWidgetCommons.META_INITIATIONTIME_TAG, 
+								TrackWidgetCommons.META_INITIATIONTIME_TAG + inittime);
+						
+						AudioStructureModel.getInstance().onTrackWidgetRemoved(localFileName, (parent != null) ? parent.getName() : null);
+						AudioStructureModel.getInstance().onTrackWidgetAnchored(localFileName, (parent != null) ? parent.getName() : null, 
+								inittime, newRunningTime, getName());
+					}
+					
+				}
+				
+				break;
+				
+
+		}
+
+	}
+	
+	public void setObservedSubject(Subject parent) {
+	}
+
+	@Override
+	public void paintInFreeSpace(Graphics g) {
+		paintInFreeSpace(g, false);
+	}
+	
+	public void paintInFreeSpace(Graphics g, boolean isAtFinalPass) { 
+
+		if (isLoaded()) { 
+			
+			
+			if (ExpandedTrackManager.getInstance().isAnyExpandedTrackVisible() &&
+					!isAtFinalPass) {
+				// If a expanded track is in view .. then must render lastly
+				return;
+			} 
+		
+			// Check to see if dragging over a EditableSampledTrackGraphView
+			MouseEvent me = MouseEventRouter.getCurrentMouseEvent();
+			if (me != null) {
+			
+				if (me.getComponent() != fulltrackView && 
+						me.getComponent() instanceof EditableSampledTrackGraphView &&
+						!((EditableSampledTrackGraphView)me.getComponent()).isPlaying()) {
+					
+
+					Point containerPoint = SwingUtilities.convertPoint(me.getComponent(), 
+							new Point(0,0), Browser._theBrowser.getContentPane());
+					
+					Shape clipBackUp = g.getClip();
+					g.setClip(null);
+					
+					g.setColor(Color.ORANGE);
+					((Graphics2D)g).setStroke(EditableSampledTrackGraphView.GRAPH_BAR_STROKE);
+					g.drawLine(
+							containerPoint.x + me.getX(),
+							containerPoint.y, 
+							containerPoint.x + me.getX(),
+							containerPoint.y + me.getComponent().getHeight());
+					
+					FrameGraphics.invalidateArea(new Rectangle(
+							containerPoint.x + me.getX(), 
+							containerPoint.y, 
+							1,
+							containerPoint.y + me.getComponent().getHeight() + 1));
+			
+					// Restore clip
+					g.setClip(clipBackUp);
+					
+					g.setColor(SEMI_TRANSPARENT_FREESPACE_BACKCOLOR);
+					g.fillRect(getX(), getY(), getWidth(), getHeight());
+					
+					if (isAtFinalPass) { // final pass does not draw the borders... so must manually draw them
+						g.setColor(Color.BLACK);
+						((Graphics2D)g).setStroke(FREESPACE_OUTLINING);
+						g.drawRect(getX(), getY(), getWidth(), getHeight());
+					}
+		
+					return;
+				}
+				
+			}
+		
+			
+			
+		}
+		
+		super.paintInFreeSpace(g);
+		
+		if (isLoaded()) {
+			
+			Shape clipBackUp = g.getClip();
+			Rectangle tmpClip = (clipBackUp != null) ? clipBackUp.getBounds() : 
+				new Rectangle(0, 0, 
+						Browser._theBrowser.getContentPane().getWidth(), 
+						Browser._theBrowser.getContentPane().getHeight());
+				
+			g.setClip(tmpClip.intersection(getBounds()));
+		
+			// Draw the name
+			String name = getName();
+			if (name == null) name = "Unnamed";
+
+			g.setFont(TrackWidgetCommons.FREESPACE_TRACKNAME_FONT);
+			g.setColor(TrackWidgetCommons.FREESPACE_TRACKNAME_TEXT_COLOR);
+			
+			// Center track name
+			FontMetrics fm   = g.getFontMetrics(TrackWidgetCommons.FREESPACE_TRACKNAME_FONT);
+			Rectangle2D rect = fm.getStringBounds(name, g);
+			
+			g.drawString(
+					name, 
+					this.getX() + (int)((getWidth() - rect.getWidth()) / 2), 
+					this.getY() + (int)((getHeight() - rect.getHeight()) / 2) + (int)rect.getHeight()
+					);
+			
+			g.setClip(clipBackUp);
+		
+		}
+		
+	}
+	
+	
+
+	@Override
+	public void paint(Graphics g) {
+		super.paint(g);
+		
+		if (isLoaded() && nameLabel != null) {
+			nameLabel.paint(g);
+			
+			if (shouldOmitIndexAudio()) {
+				
+				int shiftOffset = SoundDesk.getInstance().isPlaying(trackMix.getChannelID()) ?
+						-20 : 0;
+				
+				IconRepository.getIcon("omitindexed.png").paintIcon(
+						_swingComponent, 
+						g, 
+						getX() + getWidth() - EditableSampledTrackGraphView.LOCK_ICON_CORNER_OFFSET + shiftOffset, 
+						getY() + EditableSampledTrackGraphView.LOCK_ICON_CORNER_OFFSET - 16);
+				
+			}
+		}
+		
+	}
+	
+	private MouseEvent lastInsertME = null;
+	
+	@Override
+	protected void onParentStateChanged(int eventType) {
+		super.onParentStateChanged(eventType);
+
+		Frame parent = null;
+
+		switch (eventType) {
+
+			// Logic for injecting audio tracks into EditableSampledTrackGraphView's
+			case ItemParentStateChangedEvent.EVENT_TYPE_ADDED:
+			case ItemParentStateChangedEvent.EVENT_TYPE_ADDED_VIA_OVERLAY:
+
+				// Resume any layouts suspended by this track widget
+				FrameLayoutDaemon.getInstance().resumeLayout(this);
+				
+				if (trackModel != null) {
+
+					parent = getParentFrame();
+
+					// Determine new initation time according to anchored position
+					long initTime = (parent != null) ? 
+							FrameLayoutDaemon.getInstance().getMSAtX(getX(), parent)
+							: 0;
+
+					// Keep TrackGraphModel consistant
+					AudioStructureModel.getInstance().onTrackWidgetAnchored(
+							localFileName, 
+							(parent != null) ? parent.getName() : null, 
+							initTime, 
+							AudioMath.framesToMilliseconds(trackModel.getFrameCount(), trackModel.getFormat()),
+							getName());
+					
+					
+					MouseEvent me = MouseEventRouter.getCurrentMouseEvent();
+					
+					if (me != null && me != lastInsertME &&
+							(me.getButton() == MouseEvent.BUTTON2 ||
+									me.getButton() == MouseEvent.BUTTON3)) {
+						
+						// Although widgets filter out multiple parent changed events per mouse event - 
+						// it also considers the event type, and because this it remove itself from the frame/freespace
+						// it jumbles up the filtering so that this will be invoked for each corner.
+						lastInsertME = me;
+					
+						if (me.getComponent() != fulltrackView && 
+								me.getComponent() instanceof EditableSampledTrackGraphView) {
+
+							EditableSampledTrackGraphView editableSampledTrack = 
+								(EditableSampledTrackGraphView)me.getComponent();
+
+							// Inject this into the widget
+							try {
+								injectAudio(editableSampledTrack, me.getX(), true);
+							} catch (IOException ex) {
+								ex.printStackTrace();
+							}
+
+						}
+							
+					} else if (me == lastInsertME) {
+						// Note due to a injection removing this widget while in the midst of
+						// anchoring, the widget parent event filtering will not work thus
+						// must keep removing self until the mouse event has done.
+						removeSelf();
+					}
+				}
+				
+			case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN:
+			case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN_VIA_OVERLAY:
+
+				
+/*
+				// Listen for volume or mute changed events
+				trackMix.addObserver(this);
+				
+				// Listen for solo events and track sequence creation events
+				MixDesk.getInstance().addObserver(this);
+				*/
+				
+			break; 
+			
+
+			case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED:
+			case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED_VIA_OVERLAY:
+
+				// If yanking this from the frame into free space suspend the layout daemon
+				// for this frame
+				if (MouseEventRouter.getCurrentMouseEvent() != null &&
+						MouseEventRouter.getCurrentMouseEvent().getButton() == MouseEvent.BUTTON2 &&
+						MouseEventRouter.getCurrentMouseEvent() != lastInsertME) {
+					Frame suspended = DisplayIO.getCurrentFrame();
+					if (suspended != null) {
+						FrameLayoutDaemon.getInstance().suspendLayout(suspended, this);
+					}
+				}
+				
+				if (trackModel != null) {
+					parent = getParentFrame();
+					// Keep TrackGraphModel consistant
+					AudioStructureModel.getInstance().onTrackWidgetRemoved(localFileName, 
+							(parent != null) ? parent.getName() : null);
+				}
+				
+			case ItemParentStateChangedEvent.EVENT_TYPE_HIDDEN:
+				/*
+				trackMix.removeObserver(this);
+				MixDesk.getInstance().removeObserver(this);*/
+				
+				break; 
+				
+		}
+
+	}
+
+	/**
+	 * Injects this widgets bytes into a EditableSampledTrackGraphView
+	 * and removes this widget from freespace/parent-frame.
+	 * 
+	 * Doesn't inject bytes if the target EditableSampledTrackGraphView is in a playing state.
+	 * 
+	 * @param target
+	 * 		The target EditableSampledTrackGraphView to inject this widgets bytes into
+	 * 
+	 * @param graphX
+	 * 		The X pixel in the target's graph. Must be in valid range.
+	 * 
+	 * @param destroySelf
+	 * 		If want to destroy this widget
+	 * 
+	 * @throws IOException
+	 * 		If the insert failed ... can occur if bytes need to be
+	 * 		converted into targets format.
+	 */
+	public void injectAudio(EditableSampledTrackGraphView target, int graphX, boolean destroySelf)
+		throws IOException {
+		assert(target != null);
+		assert(graphX >= 0);
+		assert(graphX <= target.getWidth());
+		
+		// Cannot inject into EditableSampledTrackGraphView's while playing, although
+		// won't break anything, this would be confusing for the user.
+		if (target.isPlaying()) return;
+		
+		// Inject the audio at the graph poistion
+		int insertFramePoint = target.frameAtX(graphX);
+
+		// Inject audio 
+		target.insertAudio(
+				trackModel.getAllAudioBytes(), 
+				trackModel.getFormat(), 
+				insertFramePoint); 
+
+		// Note: if removed from free space .. then there should be no
+		// more references to this item therefore the memory will be freed
+		// eventually after this invoke
+		if (destroySelf) removeSelf();
+	}
+	
+	/**
+	 * 
+	 * @return
+	 * 		The unique loval filename for this track. Auto-assigned - and rememered. Never null.
+	 */
+	public String getLocalFileName() {
+		return localFileName;
+	}
+
+	/**
+	 * Determines the running time from the raw audio in memory.
+	 * 
+	 * @return
+	 * 		The running time or this track in MS.
+	 * 		-1 if not loaded.
+	 */
+	public long getRunningMSTimeFromRawAudio()
+	{
+		if (this.trackModel != null) {
+			return AudioMath.framesToMilliseconds(trackModel.getFrameCount(), trackModel.getFormat());
+		}
+		
+		return -1;
+	}
+	
+	/**
+	 * Determines the running time from the meta data.
+	 * 
+	 * @return
+	 * 		The running time or this track in MS.
+	 * 		-1 if unavilable.
+	 */
+	public long getRunningMSTimeFromMeta() {
+		return getStrippedDataLong(TrackWidgetCommons.META_RUNNINGMSTIME_TAG, new Long(-1));
+	}
+	
+	/**
+	 * @return
+	 * 		The name given to this widget... can be null.
+	 */
+	public String getName() {
+		if (this.trackModel != null)
+			return trackModel.getName();
+		else if (this.nameLabel != null) {
+			return nameLabel.getText();
+		} 
+		return getStrippedDataString(TrackWidgetCommons.META_NAME_TAG);
+	}
+	
+	/**
+	 * Determines the initiation time from the meta data.
+	 * 
+	 * @return
+	 * 		The initiation time or this track in MS.
+	 * 		null if unavilable.
+	 */
+	public Long getInitiationTimeFromMeta() {
+		return getStrippedDataLong(TrackWidgetCommons.META_INITIATIONTIME_TAG, null);
+	}
+	
+	private void updateBorderColor() {
+		
+		// Get border color currently used
+		Color oldC = getSource().getBorderColor();
+		
+		Color newC = TrackWidgetCommons.getBorderColor(
+				SoundDesk.getInstance().isSolo(trackMix.getChannelID()), 
+				trackMix.isMuted());
+		
+		// Update the color
+		if (!newC.equals(oldC)) {
+			setWidgetEdgeColor(newC);
+		}
+	}
+	
+	/**
+	 * State icons live at the top right corner
+	 *
+	 */
+	private void invalidateStateIcons() {
+		this.invalidateSelf(); // TODO
+	}
+	
+	private void setShouldOmitIndexAudio(boolean shouldOmit) {
+		this.shouldOmitAudioIndexing = shouldOmit;
+		if (!shouldOmit) {
+			removeData(META_DONT_INDEX_AUDIO_TAG);
+		} else {
+			addDataIfCaseInsensitiveNotExists(META_DONT_INDEX_AUDIO_TAG);
+		}
+		invalidateStateIcons();
+	}
+	
+	public boolean shouldOmitIndexAudio() {
+		return shouldOmitAudioIndexing;
+	}
+	
+//	/**
+//	 * Invalidates.
+//	 * @param shouldLayout
+//	 */
+//	private void setShouldLayout(boolean shouldLayout) {
+//		
+//		if (shouldLayout) {
+//			removeData(TrackWidgetCommons.META_OMIT_LAYOUT_TAG);
+//		} else {
+//			addDataIfCaseInsensitiveNotExists(TrackWidgetCommons.META_OMIT_LAYOUT_TAG);
+//		}
+//		
+//		invalidateStateIcons();
+//	}
+//	
+
+//	
+//	public boolean shouldLayout() {
+//		
+//		return (!containsDataTrimmedIgnoreCase(TrackWidgetCommons.META_OMIT_LAYOUT_TAG));
+//	}
+	
+	/**
+	 * Doesn't expand if not anchored ... 
+	 * @param addToExpand
+	 */
+	private void expand(boolean addToExpand) {
+		
+		Frame parent = getParentFrame();
+		String pfname = (parent != null) ?  parent.getName() : null;
+		if (pfname == null) return;
+
+		if (!ExpandedTrackManager.getInstance().isTrackInExpansionSelection(trackModel)) {
+			
+			if (addToExpand) {
+				
+				// Show the expanded view for this track once control has been released
+				ExpandedTrackManager.getInstance().addTrackToSelection(
+						trackModel,
+						pfname,
+						_swingComponent.getBounds(),
+						trackMix);
+
+			} else {
+
+				// Get rid of all popups
+				PopupManager.getInstance().hideAutohidePopups();
+				
+				int start = trackModel.getSelectionStart();
+				int length = trackModel.getSelectionLength();
+				
+				if (length <= 1) {
+					start = 0;
+					length = trackModel.getFrameCount();
+				}
+				
+				// Show the expanded view for this track
+				ExpandedTrackManager.getInstance().expandSingleTrack(
+						trackModel, 
+						_swingComponent.getBounds(),
+						trackMix,
+						pfname,
+						start,
+						length
+						);
+			
+			}
+			
+		} else {
+			
+			// Take away track from being selected.
+			ExpandedTrackManager.getInstance().removeTrackFromSelection(trackModel);
+		}
+		
+	}
+
+	/**
+	 * The small popup for common actions.
+	 * 
+	 * @author Brook Novak
+	 *
+	 */
+	private class PlaybackPopup extends PlaybackControlPopup implements Observer {
+
+		private static final long serialVersionUID = 1L;
+		
+		public PlaybackPopup() {
+			miscButton.setActionCommand("expand");
+			miscButton.setIcon(IconRepository.getIcon("expand.png"));
+			miscButton.setToolTipText("Expand");
+
+		}
+
+		@Override
+		public void onHide() {
+			super.onHide();
+			
+			// Listen for volume or mute changed events
+			trackMix.removeObserver(this);
+			
+			// Listen for solo events and track sequence creation events
+			SoundDesk.getInstance().removeObserver(this);
+		}
+
+		@Override
+		public void onShow() {
+			super.onShow();
+			// Listen for volume or mute changed events
+			trackMix.addObserver(this);
+			
+			// Listen for solo events and track sequence creation events
+			SoundDesk.getInstance().addObserver(this);
+			updateVolume((int)(100 * trackMix.getVolume()));
+			updateMute(trackMix.isMuted());
+			updateSolo(SoundDesk.getInstance().isSolo(trackMix.getChannelID()));
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			if (trackModel == null) return;
+			
+			if (e.getSource() == playPauseButton) {
+
+				try {
+	
+					if (!SoundDesk.getInstance().isPlaying(trackMix.getChannelID())) { // play / resume
+						
+						int startFrame = -1, endFrame = -1;
+						
+						// Resume playback?
+						if (SoundDesk.getInstance().isPaused(trackMix.getChannelID())) {
+							startFrame = SoundDesk.getInstance().getLastPlayedFramePosition(trackMix.getChannelID());
+							if (startFrame >= 0 && startFrame < trackModel.getFrameCount()) {
+				
+								// The user may have edited the audio track and reselected it
+								// since the last pause. Thus select an appropriate end frame
+								endFrame = (trackModel.getSelectionLength() > 1) ?
+										trackModel.getSelectionStart() + trackModel.getSelectionLength():
+											trackModel.getFrameCount() - 1;
+									
+								// Changed selection? it play range invalid?
+								if (endFrame <= startFrame || startFrame < trackModel.getSelectionStart()) {
+									startFrame = -1; // Play new selection (see below)
+								
+								} else if (endFrame >= trackModel.getFrameCount()) {
+									endFrame = trackModel.getFrameCount() - 1;
+								}		
+								
+							}
+						}
+						
+						// Play from beginning of selection to end of selection
+						if (startFrame < 0) {
+							startFrame = trackModel.getSelectionStart();
+							endFrame = (trackModel.getSelectionLength() > 1) ?
+									startFrame + trackModel.getSelectionLength():
+										trackModel.getFrameCount() - 1;
+						}
+	
+						// Safety clamp:
+						if (endFrame >= trackModel.getFrameCount()) {
+							endFrame = trackModel.getFrameCount() - 1;
+						}	
+						
+						if (startFrame < endFrame) {
+							SoundDesk.getInstance().playSampledTrackModel(
+									trackModel, 
+									trackMix.getChannelID(), 
+									startFrame, 
+									endFrame, 
+									0);
+						}
+	
+					} else { // pause
+						
+						TrackSequence ts = SoundDesk.getInstance().getTrackSequence(trackMix.getChannelID());
+
+						if (ts != null &&
+								ts.isPlaying()) {
+							
+							// Mark channel as paused.
+							SoundDesk.getInstance().setPaused(trackMix.getChannelID(), true);
+							
+							// Stop playback for this channel
+							ApolloPlaybackMixer.getInstance().stop(ts);
+							
+						}
+						
+					}
+					
+				} catch (LineUnavailableException e1) {
+					e1.printStackTrace();
+				}
+				
+			} else if (e.getSource() == stopButton) {
+				
+				TrackSequence ts = SoundDesk.getInstance().getTrackSequence(trackMix.getChannelID());
+
+				// reset any paused mark
+				SoundDesk.getInstance().setPaused(trackMix.getChannelID(), false);
+				
+				if (ts != null &&
+						ts.isPlaying()) {
+					// Stop playback
+					ApolloPlaybackMixer.getInstance().stop(ts);
+				}
+				
+			} else if (e.getSource() == rewindButton) {
+				
+				trackModel.setSelection(0, 0);
+				SoundDesk.getInstance().setPaused(trackMix.getChannelID(), false);
+				
+			} else if (e.getSource() == miscButton) {
+				expand(false);
+			}
+		}
+		
+		public Subject getObservedSubject() {
+			return null;
+		}
+
+		public void setObservedSubject(Subject parent) {
+		}
+
+		/**
+		 * Receives events from the track model OR from the observed track sequence.
+		 */
+		public void modelChanged(Subject source, SubjectChangedEvent event) {
+
+			// Synch GUI with track state
+			switch (event.getID()) {
+			
+			case ApolloSubjectChangedEvent.TRACK_SEQUENCE_CREATED: // from sound desk
+				
+				if (event.getState().equals(trackMix.getChannelID())) {
+					// The channel being played is the same as this one ...
+					// even if the track model is unloaded must enter into a playing state
+					// if the created track sequence will play
+					TrackSequence ts = SoundDesk.getInstance().getTrackSequence(trackMix.getChannelID());
+					assert(ts != null);
+					assert(!ts.hasFinished());
+					assert(!ts.isPlaying());
+					ts.addObserver(this);
+				}
+
+				break;
+				
+			case ApolloSubjectChangedEvent.PLAYBACK_STARTED: // From observed track sequence
+				stopButton.setEnabled(true);
+				rewindButton.setEnabled(false);
+				playPauseButton.setIcon(IconRepository.getIcon("pause.png"));
+				
+				invalidateStateIcons();
+
+				SampledTrack.this.setWidgetEdgeThickness(TrackWidgetCommons.PLAYING_TRACK_EDGE_THICKNESS);
+				//FrameGraphics.refresh(true);
+					break;
+ 
+				case ApolloSubjectChangedEvent.PLAYBACK_STOPPED: // From observed track sequence
+				
+				invalidateStateIcons();
+					
+				rewindButton.setEnabled(true);
+				stopButton.setEnabled(false);
+				playPauseButton.setIcon(IconRepository.getIcon("play.png"));
+				
+				// Note:
+				// No need to remove self from observing the dead track since the track references this
+				// and will get garbage collected
+				
+				SampledTrack.this.setWidgetEdgeThickness(TrackWidgetCommons.STOPPED_TRACK_EDGE_THICKNESS);
+
+				break;
+				
+			case ApolloSubjectChangedEvent.PAUSE_MARK_CHANGED: // When stopped or paused
+				/*
+				if (ae.getState().equals(trackMix.getChannelID())) {
+					
+					if (MixDesk.getInstance().isPaused(trackMix.getChannelID())) {
+						// Do nothing .. the paused mark is set prior to a stop
+					} else {
+						// Esnure that the GUI represents a stopped state
+						stopButton.setEnabled(false);
+						playPauseButton.setIcon(IconRepository.getIcon("play.png"));
+					}
+					
+				}*/
+				
+				break;
+				
+			case ApolloSubjectChangedEvent.VOLUME: // From obseved track mix
+				updateVolume((int)(100 * trackMix.getVolume()));
+				break;
+				
+			case ApolloSubjectChangedEvent.MUTE: // From obseved track mix
+				updateMute(trackMix.isMuted());
+				updateBorderColor();
+				break;
+				
+			case ApolloSubjectChangedEvent.SOLO_PREFIX_CHANGED: // From mix desk
+				updateSolo(SoundDesk.getInstance().isSolo(trackMix.getChannelID()));
+				updateBorderColor();
+				break;
+			}
+			
+	
+		
+		}
+
+		@Override
+		protected void volumeChanged() {
+			trackMix.setVolume(((float)volumeSlider.getValue()) / 100.0f);
+		}
+
+		@Override
+		protected void muteChanged() {
+			trackMix.setMuted(muteButton.isSelected());
+		}
+
+		@Override
+		protected void soloChanged() {
+			SoundDesk.getInstance().setSoloIDPrefix(soloButton.isSelected() ?
+					trackMix.getChannelID() : null
+					);
+		}
+
+	}
+	
+}
Index: unk/src_apollo/org/apollo/widgets/SampledTrackEvo4.java
===================================================================
--- /trunk/src_apollo/org/apollo/widgets/SampledTrackEvo4.java	(revision 317)
+++ 	(revision )
@@ -1,1869 +1,0 @@
-package org.apollo.widgets;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.FontMetrics;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Point;
-import java.awt.Rectangle;
-import java.awt.Shape;
-import java.awt.Stroke;
-import java.awt.event.ActionEvent;
-import java.awt.event.KeyEvent;
-import java.awt.event.KeyListener;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.awt.event.MouseMotionListener;
-import java.awt.geom.Rectangle2D;
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.util.LinkedList;
-import java.util.List;
-
-import javax.sound.sampled.AudioFormat;
-import javax.sound.sampled.LineUnavailableException;
-import javax.sound.sampled.UnsupportedAudioFileException;
-import javax.swing.SwingUtilities;
-
-import org.apollo.audio.ApolloPlaybackMixer;
-import org.apollo.audio.ApolloSubjectChangedEvent;
-import org.apollo.audio.SampledTrackModel;
-import org.apollo.audio.TrackSequence;
-import org.apollo.audio.structure.AudioStructureModel;
-import org.apollo.audio.structure.TrackGraphNode;
-import org.apollo.audio.util.FrameLayoutDaemon;
-import org.apollo.audio.util.SoundDesk;
-import org.apollo.audio.util.Timeline;
-import org.apollo.audio.util.TrackMixSubject;
-import org.apollo.gui.EditableSampledTrackGraphView;
-import org.apollo.gui.ExpandedTrackManager;
-import org.apollo.gui.PlaybackControlPopup;
-import org.apollo.gui.SampledTrackGraphView;
-import org.apollo.gui.SampledTrackGraphView.EffecientInvalidator;
-import org.apollo.io.AudioIO;
-import org.apollo.io.AudioPathManager;
-import org.apollo.io.IconRepository;
-import org.apollo.io.AudioIO.AudioFileLoader;
-import org.apollo.items.EmulatedTextItem;
-import org.apollo.items.EmulatedTextItem.TextChangeListener;
-import org.apollo.mvc.Observer;
-import org.apollo.mvc.Subject;
-import org.apollo.mvc.SubjectChangedEvent;
-import org.apollo.util.AudioMath;
-import org.apollo.util.PopupReaper;
-import org.apollo.util.TrackModelHandler;
-import org.apollo.util.TrackModelLoadManager;
-import org.apollo.util.TrackNameCreator;
-import org.expeditee.gui.Browser;
-import org.expeditee.gui.DisplayIO;
-import org.expeditee.gui.Frame;
-import org.expeditee.gui.FrameGraphics;
-import org.expeditee.gui.MouseEventRouter;
-import org.expeditee.gui.PopupManager;
-import org.expeditee.items.ItemParentStateChangedEvent;
-import org.expeditee.items.ItemUtils;
-import org.expeditee.items.Text;
-import org.expeditee.items.widgets.HeavyDutyInteractiveWidget;
-import org.expeditee.items.widgets.InteractiveWidget;
-import org.expeditee.items.widgets.InteractiveWidgetInitialisationFailedException;
-import org.expeditee.items.widgets.InteractiveWidgetNotAvailableException;
-
-/**
- * The sampled track widgets in apollo.
- * 
- * @author Brook Novak
- *
- */
-public class SampledTrackEvo4 extends HeavyDutyInteractiveWidget 
-	implements TrackModelHandler, EffecientInvalidator, Observer {
-
-	/** The observered subject. Can be null */
-	private SampledTrackModel trackModel = null;
-	
-	/** Used for the loading phase. Can change serveal times of a lifetime. for example after import and save it becomes local */
-	private String loadFilenameArgument = null; // preproceeds with ARG_IMPORT_TAG if importing.
-	
-	private String localFileName; // Immutable - assigned on constuction
-	
-	/** Used for dumping audio to a temp file when deleted - to free memory.
-	 * Protocol: Set when the widget is deleted. Unset when loaded. */
-	private File recoveryFile = null;
-	
-	private boolean shouldOmitAudioIndexing = false;
-	
-	private EditableSampledTrackGraphView fulltrackView;
-	private EmulatedTextItem nameLabel; 
-	private PlaybackPopup playbackControlPopup = null;
-	private TrackMixSubject trackMix; // immutable - like local filename
-	
-	/** The amount of load bar that is allocated to file loading */
-	private static final float FILE_LOADING_PERCENT_RANGE = 0.95f;
-
-	/** For parsing arguments */
-	public static final String ARG_IMPORT_TAG = "if=";
-	
-	/** For parsing metadata */
-	public static final String META_LOCALNAME_TAG = "ln=";
-	
-	private static final Stroke FREESPACE_OUTLINING = new BasicStroke(2.0f);
-	
-	private static final Color SEMI_TRANSPARENT_FREESPACE_BACKCOLOR = new Color(
-			FREESPACE_BACKCOLOR.getRed(), FREESPACE_BACKCOLOR.getGreen(),
-			FREESPACE_BACKCOLOR.getBlue(), 128);
-	
-	
-	/** If a track widget has a data string thaty contains META_SHOULD_INDEX_AUDIO_TAG, then
-	 * the track should not be indexed for searching the audio.... default isdoes not exist
-	 */
-	public static final String META_DONT_INDEX_AUDIO_TAG = "dontindexaudio";
-
-	/**
-	 * Constructor used for loading directly from given bytes.
-	 * 
-	 * @param source
-	 * 
-	 * @param audioBytes
-	 * 
-	 * @param format
-	 * 
-	 * @param mixTemplate
-	 * 		Can be null
-	 * 
-	 */
-	private SampledTrackEvo4(Text source, byte[] audioBytes, AudioFormat format, TrackMixSubject mixTemplate) {
-		
-		super(source, new EditableSampledTrackGraphView(), 
-				100, -1, 
-				FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, 
-				TrackWidgetCommons.CACHE_DEPTH,
-				true);
-
-		// Must set upon construction - always
-		localFileName = AudioPathManager.generateLocateFileName("wav");
-		updateData(META_LOCALNAME_TAG, META_LOCALNAME_TAG + localFileName);
-		
-		trackModel = new SampledTrackModel(audioBytes, format, localFileName);
-		
-		// Ensure that the model is marked as modiefied so that it will save
-		trackModel.setAudioModifiedFlag(true);
-		
-		trackModel.setName(getStrippedDataString(TrackWidgetCommons.META_NAME_TAG));
-
-		// Create the immutable mix subject
-		if (mixTemplate == null)
-			trackMix = SoundDesk.getInstance().getOrCreateMix(SoundDesk.createPureLocalChannelID(this));
-		
-		else trackMix = SoundDesk.getInstance().createMix(
-				SoundDesk.createPureLocalChannelID(this), 
-				mixTemplate.getVolume(), 
-				mixTemplate.isMuted(),
-				true);
-				
-		// Keep meta as constant as possible for best results
-		updateData(TrackWidgetCommons.META_RUNNINGMSTIME_TAG, TrackWidgetCommons.META_RUNNINGMSTIME_TAG + getRunningMSTimeFromRawAudio());
-		
-		createGUI();
-		
-		initObservers();
-		
-	}
-
-	/**
-	 * Constructor called by Expeditee. Eventually loads audio from file.
-	 * 
-	 * @param source
-	 * 
-	 * @param args
-	 * 		Can have a ARG_IMPORT_TAG argument...
-	 */
-	public SampledTrackEvo4(Text source, String[] args) {
-		super(source, new EditableSampledTrackGraphView(), 
-				100, -1, 
-				FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, 
-				TrackWidgetCommons.CACHE_DEPTH);
-		
-		// Read the metadata
-		localFileName = getStrippedDataString(META_LOCALNAME_TAG);
-
-		// Ensure the local filename is assigned - even if file does not exist...
-		// Also it could be importing a file...
-		if (localFileName == null) {
-			localFileName = AudioPathManager.generateLocateFileName("wav");
-			updateData(META_LOCALNAME_TAG, META_LOCALNAME_TAG + localFileName);
-		}
-
-		trackMix = SoundDesk.getInstance().getOrCreateMix(SoundDesk.createPureLocalChannelID(this));
-
-		loadFilenameArgument = localFileName;
-		if (args != null) { // parse args
-			for (String arg : args) {
-				if (arg != null && arg.length() > ARG_IMPORT_TAG.length()) {
-					loadFilenameArgument = arg;
-				}
-			}
-		}
-		
-		createGUI();
-
-	}
-	
-	private void initObservers() {
-		
-		// Listen for model events
-		trackModel.addObserver(this);
-		trackModel.addObserver(fulltrackView);
-		fulltrackView.setMix(trackMix); // use the same mix/channel for playback
-		ExpandedTrackManager.getInstance().addObserver(this);
-		
-		// Show graph as being selected if expanded / pending to expand.
-		if (ExpandedTrackManager.getInstance().isTrackInExpansionSelection(trackModel)) {
-			fulltrackView.setBackColor(new Color(100, 100, 100), new Color(120, 120, 120));
-		} else {
-			fulltrackView.setBackColor(SampledTrackGraphView.DEFAULT_BACKGROUND_COLOR, SampledTrackGraphView.DEFAULT_BACKGROUND_HIGHTLIGHTS_COLOR);
-		}
-
-	}
-
-	private void createGUI() {
-		
-		// Set widget as a fixed size widget- using width from last recorded width in the meta data.
-		int width = getStrippedDataInt(TrackWidgetCommons.META_LAST_WIDTH_TAG, -1);
-		if (width >= 0 && width < FrameLayoutDaemon.MIN_TRACK_WIDGET_WIDTH) {
-			width = FrameLayoutDaemon.MIN_TRACK_WIDGET_WIDTH;
-		}
-		
-		if (width < 0) {
-			setSize(-1, -1, 
-					FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, 
-					FrameLayoutDaemon.TRACK_WIDGET_DEFAULT_WIDTH, FrameLayoutDaemon.TRACK_WIDGET_HEIGHT);
-		} else {
-			setSize(width, width, 
-					FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, FrameLayoutDaemon.TRACK_WIDGET_HEIGHT, 
-					width, FrameLayoutDaemon.TRACK_WIDGET_HEIGHT);
-		}
-		
-		shouldOmitAudioIndexing = containsDataTrimmedIgnoreCase(META_DONT_INDEX_AUDIO_TAG);
-
-		playbackControlPopup = new PlaybackPopup();
-		
-		fulltrackView = (EditableSampledTrackGraphView)_swingComponent;
-		fulltrackView.setAlwaysFullView(true);
-		
-		/*fulltrackView.setInvalidator(new EffecientInvalidator() {
-			public void onGraphDirty(SampledTrackGraphView graph, Rectangle dirty) {
-		        dirty.translate(getX(), getY() - 1);
-		        FrameGraphics.invalidateArea(dirty); 
-		        FrameGraphics.refresh(true); 
-			}
-		});*/
-		
-		// Auto-show playback popup.
-		// Block messages if the track is expanded
-		fulltrackView.addMouseMotionListener(new MouseMotionListener() {
-
-			public void mouseDragged(MouseEvent e) {
-				if (nameLabel != null) {
-					if (nameLabel.onMouseDragged(e)) {
-						e.consume();
-					}
-				}
-			}
-
-			public void mouseMoved(MouseEvent e) {
-
-				if (nameLabel != null) {
-					nameLabel.onMouseMoved(e, fulltrackView);
-				}
-				
-				if (playbackControlPopup == null) {
-					playbackControlPopup = new PlaybackPopup();
-				} 
-
-				// Only show popup iff there is nothing expanded / expanding.
-				if (!PopupManager.getInstance().isShowing(playbackControlPopup) &&
-						!ExpandedTrackManager.getInstance().doesExpandedTrackExist()) {	
-					
-					// Get rid of all popups
-					PopupManager.getInstance().hideAutohidePopups();
-					
-					Rectangle animationSource = _swingComponent.getBounds();
-					
-					// Determine where popup should show
-					int x = SampledTrackEvo4.this.getX();
-					int y = SampledTrackEvo4.this.getY() - playbackControlPopup.getHeight() - 2; // by default show above
-					
-					// I get sick.dizzy from the popup expanding from the whole thing...
-					animationSource.height = 1;
-					animationSource.width = Math.min(animationSource.width, playbackControlPopup.getWidth());
-					
-					if (y < 0) {
-						y = SampledTrackEvo4.this.getY() + SampledTrackEvo4.this.getHeight() + 2;
-						animationSource.y = y - 2;
-					} 
-
-					// Animate the popup
-					PopupManager.getInstance().showPopup(
-							playbackControlPopup, 
-							new Point(x, y), 
-							fulltrackView,
-							PopupManager.getInstance().new ExpandShrinkAnimator(
-									animationSource,
-									Color.LIGHT_GRAY));
-					
-					PopupReaper.getInstance().initPopupLifetime(
-							playbackControlPopup, 
-							PopupManager.getInstance().new ExpandShrinkAnimator(
-									animationSource,
-									Color.LIGHT_GRAY), 
-									TrackWidgetCommons.POPUP_LIFETIME);
-					
-				} else {
-					PopupReaper.getInstance().revivePopup(playbackControlPopup, TrackWidgetCommons.POPUP_LIFETIME);
-				}
-				
-			}
-
-		});
-		
-		// Expand on double click
-		// Block messages if the track is expanded
-		fulltrackView.addMouseListener(new MouseListener() {
-
-			public void mouseClicked(MouseEvent e) {
-				if (trackModel == null) return;
-				
-				if (nameLabel != null) {
-					if (nameLabel.onMouseClicked(e)) {
-						e.consume();
-						return;
-					}
-				}
-				
-				if (e.getClickCount() >= 2) {
-					
-					expand(e.isControlDown());
-					
-				}
-
-			}
-
-			public void mouseEntered(MouseEvent e) {
-			}
-
-			public void mouseExited(MouseEvent e) {
-			}
-
-			public void mousePressed(MouseEvent e) {
-				if (nameLabel != null) {
-					if (nameLabel.onMousePressed(e)) {
-						e.consume();
-						return;
-					}
-				}
-				
-				// Consume events if track is selected for expansion
-				if (ExpandedTrackManager.getInstance().isTrackInExpansionSelection(trackModel) &&
-						e.getButton() != MouseEvent.BUTTON1) { // but allow selection only
-					e.consume();
-				}
-			}
-
-			public void mouseReleased(MouseEvent e) {
-				if (nameLabel != null) {
-					if (nameLabel.onMouseReleased(e)) {
-						e.consume();
-					}
-				}
-				
-				// Consume events if track is selected for expansion
-				if (ExpandedTrackManager.getInstance().isTrackInExpansionSelection(trackModel)) {
-					e.consume();
-				}
-				
-		
-			}
-		
-		});
-		
-		fulltrackView.addKeyListener(new KeyListener() {
-
-			public void keyPressed(KeyEvent e) {
-				if (nameLabel != null) {
-					if (nameLabel.onKeyPressed(e, fulltrackView)) {
-						e.consume();
-					}
-				}
-			}
-
-			public void keyReleased(KeyEvent e) {
-				if (nameLabel != null) {
-					if (nameLabel.onKeyReleased(e, fulltrackView)) {
-						e.consume();
-					}
-				}
-				
-				// Toggle indexing
-				if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_I) {
-					setShouldOmitIndexAudio(!shouldOmitIndexAudio());
-					FrameGraphics.refresh(true);
-				}
-
-			}
-
-			public void keyTyped(KeyEvent e) {
-			}
-			
-		});
-		
-		
-		
-		nameLabel = new EmulatedTextItem(_swingComponent, new Point(10, 20));
-		nameLabel.setBackgroundColor(Color.WHITE);	
-		
-		String metaName = getStrippedDataString(TrackWidgetCommons.META_NAME_TAG);
-		if (metaName == null) metaName = "Untitled";
-		nameLabel.setText(metaName); 
-
-		nameLabel.addTextChangeListener(new TextChangeListener() { //  a little bit loopy!
-
-			public void onTextChanged(Object source, String newLabel) {
-				if (trackModel != null && !nameLabel.getText().equals(trackModel.getName())) {
-					trackModel.setName(nameLabel.getText());
-					SampledTrackEvo4.this.updateData(TrackWidgetCommons.META_NAME_TAG, trackModel.getName());
-					
-				}
-			}
-
-		});
-		
-		// Make sure the above mouse listeners get first serve
-		fulltrackView.reAddListeners();
-		
-		// Make sure border color is correct
-		updateBorderColor();
-		setWidgetEdgeThickness(TrackWidgetCommons.STOPPED_TRACK_EDGE_THICKNESS);
-
-	}
-
-	/**
-	 * Creates a {@link SampledTrackEvo4} instantly from audio bytes in memory and adds it to a given frame.
-	 * @param audioBytes
-	 * 			The audio samples.
-	 * 
-	 * @param format
-	 * 			The format of the audio samples.
-	 * 
-	 * @param targetFrame
-	 * 			The frame that the widget will reside.
-	 * 
-	 * @param x
-	 * 
-	 * @param y
-	 * 
-	 * @param specificInitTime
-	 * 		Negative to determine init time from x position. Positive to override
-	 * 		and determine x position from given init time
-	 * 
-	 * @param name
-	 * 			The name of the new track. If null then a default name will be used.
-	 * 
-	 * @param mixTemplate 
-	 * 			The mix data to clone for the new sampled tracks mix.
-	 * 			If null then default mix settings will be used.
-	 * 
-	 * @return The {@link SampledTrackEvo4} instance added to the given frame.
-	 * 
-	 */
-	public static SampledTrackEvo4 createFromMemory(
-			byte[] audioBytes, 
-			AudioFormat format,
-			Frame targetFrame,
-			int x, int y,
-			long specificInitTime,
-			String name,
-			TrackMixSubject mixTemplate) {
-		
-		assert (targetFrame != null);
-		assert (audioBytes != null);
-		assert (format != null);
-		
-		Text source = new Text(targetFrame.getNextItemID());
-		source.setParent(targetFrame);
-		
-		
-		
-		long runningtime = AudioMath.framesToMilliseconds(
-				audioBytes.length / format.getFrameSize(), format);
-		
-		
-		Timeline tl = FrameLayoutDaemon.getInstance().getTimeline(targetFrame);
-		
-		int width = -1;
-		if (tl != null) {
-			width = (int)((double)runningtime / tl.getTimePerPixel());
-		}	
-		
-		// Clamp width to something reasonable.
-		if (width > 0 && width < FrameLayoutDaemon.MIN_TRACK_WIDGET_WIDTH) width = FrameLayoutDaemon.MIN_TRACK_WIDGET_WIDTH;
-
-		if (specificInitTime < 0) {
-			
-			if (tl == null) specificInitTime = 0;
-			else specificInitTime = tl.getMSTimeAtX(x);
-			
-		} else if (tl != null) { // if defined a specific ms time and a timeline is avialable
-			
-			x = tl.getXAtMSTime(specificInitTime);
-		} 
-		
-		source.setPosition(x, y);
-			
-		// Setup metadata
-		LinkedList<String> data = new LinkedList<String>();
-
-		data.add(TrackWidgetCommons.META_INITIATIONTIME_TAG + specificInitTime);
-		data.add(TrackWidgetCommons.META_LAST_WIDTH_TAG + width); // although layout manager will handle, just to quick set
-		if (name != null) data.add(TrackWidgetCommons.META_NAME_TAG + name);
-		
-		source.setData(data);
-		
-		SampledTrackEvo4 strack = new SampledTrackEvo4(source, audioBytes, format, mixTemplate);
-
-		return strack;
-	}
-	
-	
-	/**
-	 * Creates a {@link SampledTrackEvo4} from file - which is tyo be imported into apollos
-	 * 
-	 * @param targetFrame
-	 * 			The frame that the widget will reside.
-	 * 
-	 * @param file
-	 * 			The audio file to import
-	 * 
-	 * @return The {@link SampledTrackEvo4} instance added to the given frame.
-	 * 
-	 */
-	public static SampledTrackEvo4 createFromFile(File file, Frame targetFrame, int x, int y) {
-		assert (targetFrame != null);
-		assert (file != null);
-		
-	
-		String[] args = new String[] { 
-				ARG_IMPORT_TAG + file.getAbsolutePath()
-				};
-		
-		Text source = new Text(
-				targetFrame.getNextItemID(),
-				ItemUtils.GetTag(ItemUtils.TAG_IWIDGET) + ":" + formatArgs(args));
-		
-		source.setParent(targetFrame);
-		
-		source.setPosition(x, y);
-		
-		long initTime = FrameLayoutDaemon.getInstance().getMSAtX(x, targetFrame);
-
-		// Setup metadata
-		LinkedList<String> data = new LinkedList<String>();
-
-		data.add(TrackWidgetCommons.META_INITIATIONTIME_TAG + initTime);
-		data.add(TrackWidgetCommons.META_LAST_WIDTH_TAG + FrameLayoutDaemon.TRACK_WIDGET_DEFAULT_WIDTH); // once loaded then the layout will handle proper size
-		data.add(TrackWidgetCommons.META_NAME_TAG + file.getName());
-		
-		source.setData(data);
-
-		SampledTrackEvo4 strack = new SampledTrackEvo4(source, args);
-
-		return strack;
-	}
-
-	
-	@Override
-	protected String[] getArgs() {
-		return null;
-	}
-
-	@Override
-	protected List<String> getData() {
-
-		List<String> data = new LinkedList<String>();
-		
-		data.add(META_LOCALNAME_TAG + localFileName);
-		
-		if (shouldOmitAudioIndexing) data.add(META_DONT_INDEX_AUDIO_TAG);
-		
-		String lastName = getName();
-		
-		if (lastName != null)
-			data.add(TrackWidgetCommons.META_NAME_TAG + lastName);
-		
-		data.add(TrackWidgetCommons.META_LAST_WIDTH_TAG + getWidth());
-		
-		Long initTime = null;
-		
-		Frame f = getParentFrame();
-		if (f != null) {
-			TrackGraphNode tinf = AudioStructureModel.getInstance().getTrackGraphInfo(localFileName, f.getName());
-			if (tinf != null) {
-				initTime = tinf.getInitiationTime();
-			}
-		}
-		
-		if (initTime == null) initTime = getInitiationTimeFromMeta(); // old meta
-		if (initTime == null) initTime = 0L;
-
-		data.add(TrackWidgetCommons.META_INITIATIONTIME_TAG + initTime);
-		
-
-		return data;
-	}
-	
-	
-	@Override
-	public InteractiveWidget copy() 
-		throws InteractiveWidgetNotAvailableException, InteractiveWidgetInitialisationFailedException {
-		
-		if (trackModel == null) {
-			return super.copy();
-			
-		} else {
-
-			return SampledTrackEvo4.createFromMemory(
-					trackModel.getAllAudioBytesCopy(),
-					trackModel.getFormat(), 
-					getSource().getParentOrCurrentFrame(), 
-					getX(), 
-					getY(),
-					-1,
-					TrackNameCreator.getNameCopy(trackModel.getName()),
-					trackMix);
-		}
-		
-	}
-
-	@Override
-	public int getLoadDelayTime() {
-		return 0;
-	}
-
-	@Override
-	protected float loadWidgetData() {
-		
-		// Load audio from file
-		File f = null;
-		boolean isImporting = false;
-		
-		if (recoveryFile != null) { // are we recovering?
-			
-			setLoadScreenMessage("Recovering audio file...");
-			
-			if (recoveryFile.exists()) f = recoveryFile;
-		}
-		
-		if (f == null) { // must be importing or loading from local repository
-
-			if (loadFilenameArgument != null) {
-				if (loadFilenameArgument.startsWith(ARG_IMPORT_TAG)) { // importing a file?
-					
-					setLoadScreenMessage("Importing audio file...");
-					
-					isImporting = true;
-					f = new File(loadFilenameArgument.substring(ARG_IMPORT_TAG.length()));
-					
-				} else {
-					assert(loadFilenameArgument == localFileName);
-					
-					setLoadScreenMessage("Loading audio file...");
-					
-					f = new File(
-							AudioPathManager.AUDIO_HOME_DIRECTORY + loadFilenameArgument);
-				}
-				
-				if (!f.exists() || !f.isFile()) f = null;
-			} 
-			
-		}
-
-		if (f == null) {
-			if (recoveryFile != null) {
-				setLoadScreenMessage("Recovery file missing");
-			} else {
-				setLoadScreenMessage("File missing");
-			}
-			
-			return LOAD_STATE_FAILED;
-		}
-		
-		if (trackModel != null && trackModel.getFilepath() != null &&
-				trackModel.equals(f.getPath())) {
-			
-			// already have loaded
-			assert(!isImporting);
-			
-		} else {
-
-			try {
-				
-				trackModel = TrackModelLoadManager.getInstance().load(f.getPath(), localFileName, this, false);
-				
-				if (trackModel == null) { // load operation canceled
-					assert(hasCancelBeenRequested()); 
-					return LOAD_STATE_INCOMPLETED;
-					
-				} else if(isImporting) { // ensure that file path is null - since not yet saved
-					trackModel.setFilepath(null);
-					
-				} else if (recoveryFile != null) {
-					
-					// If recovering - might be recovering an existing track that had been
-					// saved to the repository .. thus re-use the old file
-					trackModel.setFilepath(AudioPathManager.AUDIO_HOME_DIRECTORY + localFileName);
-				}
-				
-			} catch (IOException e) {
-				e.printStackTrace();
-				setLoadScreenMessage("Failed to load audio file");
-				return LOAD_STATE_FAILED;
-				
-			} catch (UnsupportedAudioFileException e) {
-				e.printStackTrace();
-				setLoadScreenMessage("Format not supported");
-				return LOAD_STATE_FAILED;
-			} catch (OutOfMemoryError e) {
-				e.printStackTrace();
-				setLoadScreenMessage("Out of memory");
-				return LOAD_STATE_FAILED;
-			}
-			
-		}
-		
-		// If was imported / recovered, then set as being modified
-		if (isImporting || recoveryFile != null) {
-			trackModel.setAudioModifiedFlag(true);
-		}
-		
-		// Set the name for this track
-		String name = getStrippedDataString(TrackWidgetCommons.META_NAME_TAG);
-		if (name != null) 
-			trackModel.setName(name);
-		
-		initObservers(); // sets default name if non set
-		
-		// If was recovering - get rid of temp data
-		if (recoveryFile != null) {
-			recoveryFile.delete();
-			recoveryFile = null; // ensure that out of a recovery state
-		}
-		
-		// Keep meta as constant as possible for best results
-		updateData(TrackWidgetCommons.META_RUNNINGMSTIME_TAG, TrackWidgetCommons.META_RUNNINGMSTIME_TAG + getRunningMSTimeFromRawAudio());
-		
-		// Must make sure that this track is on the track graph model
-		try {
-			SwingUtilities.invokeAndWait(new Runnable() {
-				
-				public void run() {
-					
-					Frame parent = getParentFrame();
-					String pfname = (parent != null) ?  parent.getName() : null;
-					
-					TrackGraphNode tinf = AudioStructureModel.getInstance().getTrackGraphInfo(localFileName, pfname);
-					if (tinf == null) {
-
-						// Determine new initation time according to position
-						long initTime = (parent != null) ? 
-								FrameLayoutDaemon.getInstance().getMSAtX(getX(), parent)
-								: 0;
-
-						// Keep TrackGraphModel consistant
-						AudioStructureModel.getInstance().onTrackWidgetAnchored(
-								localFileName, 
-								pfname, 
-								initTime, 
-								AudioMath.framesToMilliseconds(trackModel.getFrameCount(), trackModel.getFormat()),
-								getName());
-						
-					}
-				}
-			});
-		} catch (InterruptedException e) {
-			e.printStackTrace();
-		} catch (InvocationTargetException e) {
-			e.printStackTrace();
-		}
-		
-		
-		// Notify layout manager - not really needed but to be extra safe force the daemon
-		// to be super consistant
-		FrameLayoutDaemon.getInstance().forceRecheck();
-
-		return LOAD_STATE_COMPLETED;
-	}
-	
-	/**
-	 * Used by save manager
-	 */
-	public boolean doesNeedSaving() {
-		return (trackModel != null && (trackModel.isAudioModified() || trackModel.getFilepath() == null));
-	}
-
-	/**
-	 * Used by save manager
-	 */
-	public String getSaveName() {
-		
-		if (trackModel != null && trackModel.getName() != null)
-			return "Sampled Track: " + trackModel.getName();
-		else return "A Sampled Track";
-		
-	}
-
-	/**
-	 * Saves the audio bytes to file
-	 */
-	@Override
-	protected void saveWidgetData() {
-		
-		if (trackModel == null) return; // nothing to save
-		
-		// If saving for the file time then get a filename
-		if (trackModel.getFilepath() == null) {
-			trackModel.setFilepath(AudioPathManager.AUDIO_HOME_DIRECTORY + localFileName);
-			loadFilenameArgument = localFileName; // set to now local, next load will be local
-		}
-		
-		// Save audio bytes.
-		try {
-			
-			AudioIO.savePCMAudioToWaveFile(
-					trackModel.getFilepath(), 
-					trackModel.getAllAudioBytes(), // Safe: arrays are immutable
-					trackModel.getFormat());
-			
-			// Reset modified flag
-			trackModel.setAudioModifiedFlag(false);
-			
-		} catch (IOException e) {
-			e.printStackTrace();
-		} catch (UnsupportedAudioFileException e) {
-			e.printStackTrace();
-		}
-
-		// Ensure audio bytes can be collected if this has expired
-		if (isExpired()) {
-
-			try {
-				SwingUtilities.invokeAndWait(new Runnable() {
-					public void run() {
-						releaseMemory(true);
-					}
-				});
-			} catch (InterruptedException e) {
-				e.printStackTrace();
-			} catch (InvocationTargetException e) {
-				e.printStackTrace();
-			}
-			
-		}
-		
-	
-	}
-
-	@Override
-	public void onDelete() {
-		super.onDelete();
-		// Its nice to keep everything layed out as much as possible
-		FrameLayoutDaemon.getInstance().resumeLayout(this);
-	}
-
-	@Override
-	protected void unloadWidgetData() {
-
-		// Still needs to save?
-		if (doesNeedSaving() || trackModel == null)
-			return; // Release memory later when saved
-
-		try {
-			// Release memory - on swing thread to avoid nullified model data wil painting / editing.
-			SwingUtilities.invokeAndWait(new Runnable() {
-				public void run() {
-					releaseMemory(true);
-				}
-			});
-		} catch (InterruptedException e) {
-			e.printStackTrace();
-		} catch (InvocationTargetException e) {
-			e.printStackTrace();
-		}
-
-		
-	}
-	
-	@Override
-	protected void tempUnloadWidgetData() {
-
-		// Need unloading?
-		if (trackModel == null) return;
-		
-		// Get rid of old temporary files
-		if (recoveryFile != null && recoveryFile.exists()) {
-			recoveryFile.delete();
-		}
-		
-		// Dump memory if needs saving
-		if (trackModel.isAudioModified() || trackModel.getFilepath() == null) {
-
-			try {
-				// Always be unique
-				String uniqueID = AudioPathManager.generateLocateFileName("wav");
-				recoveryFile = File.createTempFile("APOLLO_BACKUP" + uniqueID, null);
-				
-			} catch (IOException e) {
-				e.printStackTrace();
-				return; // cannot dump ... must keep in memory.
-			}
-
-			// Dump audio to file
-			try {
-				AudioIO.savePCMAudioToWaveFile(
-						recoveryFile.getAbsolutePath(), 
-						trackModel.getAllAudioBytes(), 
-						trackModel.getFormat());
-			} catch (IOException e1) {
-				e1.printStackTrace();
-				return; // cannot dump ... must keep in memory.
-			} catch (UnsupportedAudioFileException e1) {
-				e1.printStackTrace();
-				return; // cannot dump ... must keep in memory.
-			}
-		}
-		
-		try {
-			// Release memory - on swing thread to avoid nullified model data wil painting / editing.
-			SwingUtilities.invokeAndWait(new Runnable() {
-				public void run() {
-					releaseMemory(false);
-				}
-			});
-		} catch (InterruptedException e) {
-			e.printStackTrace();
-		} catch (InvocationTargetException e) {
-			e.printStackTrace();
-		}
-
-	}
-
-	/**
-	 * To be called by the swing thread only
-	 * This is nessessary to avoid runtime memory leaks!!
-	 */
-	private void releaseMemory(boolean onlyIfExpired) {
-		
-		if (!onlyIfExpired || (onlyIfExpired && isExpired())) {
-			
-			if (trackModel != null) {
-				
-				trackModel.removeObserver(fulltrackView);
-				trackModel.removeObserver(this);
-				trackModel = null;
-				
-				assert (fulltrackView.getObservedSubject() == null);
-				assert (fulltrackView.getMix() == null);
-			}
-			
-			fulltrackView.releaseBuffer();
-			
-			ExpandedTrackManager.getInstance().removeObserver(this);
-
-		}
-		
-	}
-
-	/**
-	 * Global Track model re-use ....
-	 */
-	public SampledTrackModel getSharedSampledTrackModel(String localfilename) {
-		if (trackModel != null &&
-				trackModel.getLocalFilename().equals(localfilename)) {
-			assert(localfilename.equals(localFileName));
-			return trackModel;
-		}
-		return null;
-
-	}
-	
-	/**
-	 * @see #getAudioFormat()
-	 * 
-	 * @return
-	 * 		The audio bytes for this track widget. Null if not loaded.
-	 */
-	public byte[] getAudioBytes() {
-		return (trackModel != null) ? trackModel.getAllAudioBytes() : null;
-	}
-	
-	/**
-	 * @see #getAudioBytes()
-	 * 
-	 * @return
-	 * 		The audio format of the audio bytes for this track widget. Null if not loaded.
-	 */
-	public AudioFormat getAudioFormat() {
-		return (trackModel != null) ? trackModel.getFormat() : null;
-	}
-	
-	/**
-	 * <b>Warning</b> if the path happens to be a path of a recovery file
-	 * then it is deleted if the widget is reloaded.
-	 * 
-	 * @return 
-	 * 		A full filepath to load the audio from. Never null. The path	
-	 * 		may lead to a out of date file or no file.
-	 */
-	public String getLatestSavedAudioPath() {
-		
-		if (recoveryFile != null && recoveryFile.exists()) {
-			return recoveryFile.getAbsolutePath();
-		}
-			
-		return AudioPathManager.AUDIO_HOME_DIRECTORY + localFileName;
-		
-		
-	}
-
-	public void onGraphDirty(SampledTrackGraphView graph, Rectangle dirty) {
-        dirty.translate(getX(), getY() - 1);
-        FrameGraphics.invalidateArea(dirty); 
-        FrameGraphics.refresh(true);
-	}
-
-	public Subject getObservedSubject() {
-		return null;
-	}
-	
-
-	@Override
-	protected void onSizeChanged() {
-		super.onSizeChanged();
-		// Keep meta as constant as possible for best reults
-		updateData(TrackWidgetCommons.META_LAST_WIDTH_TAG, TrackWidgetCommons.META_LAST_WIDTH_TAG + getWidth());
-	}
-
-	/**
-	 * Responds to model changed events by updating the GUI ...
-	 */
-	public void modelChanged(Subject source, SubjectChangedEvent event) {
-		
-		// If the expansion selection has changed - check to see if was for this
-		if (source == ExpandedTrackManager.getInstance()) {
-			
-			// Show graph as being selected if expanded / pending to expand.
-			if (trackModel != null &&
-					ExpandedTrackManager.getInstance().isTrackInExpansionSelection(trackModel)) {
-				fulltrackView.setBackColor(new Color(100, 100, 100), new Color(120, 120, 120));
-			} else {
-				fulltrackView.setBackColor(SampledTrackGraphView.DEFAULT_BACKGROUND_COLOR, SampledTrackGraphView.DEFAULT_BACKGROUND_HIGHTLIGHTS_COLOR);
-			}
-			
-			return;
-		}
-		
-		Frame parent = null;
-
-		switch (event.getID()) {
-		
-			case ApolloSubjectChangedEvent.LOAD_STATUS_REPORT:
-				// If this widget is loading - then update the load status
-				if (isInLoadProgress()) {
-					
-					// If the load has been cancelled, then cancel the loader
-					if (hasCancelBeenRequested()) {
-						
-						AudioFileLoader loader = (AudioFileLoader)source;
-						loader.cancelLoad();
-						
-					} else {
-						float perc = ((Float)event.getState()).floatValue();
-						if (perc > 1.0f) perc = 1.0f;
-						perc *= FILE_LOADING_PERCENT_RANGE; // Dont stretch load to all of bar - still will have more work to do
-						updateLoadPercentage(perc);
-					}
-					
-				}
-				
-				break;
-				
-			case ApolloSubjectChangedEvent.NAME_CHANGED:
-				if (trackModel != null && !nameLabel.getText().equals(trackModel.getName())) {
-					nameLabel.setText(trackModel.getName());
-				}
-				
-				// Get graph model consistant
-				parent = getParentFrame();
-				String pfname = (parent != null) ?  parent.getName() : null;
-				
-				AudioStructureModel.getInstance().onTrackWidgetNameChanged(
-						localFileName, 
-						pfname, 
-						getName());
-						
-				break;
-				
-			case ApolloSubjectChangedEvent.AUDIO_INSERTED:
-			case ApolloSubjectChangedEvent.AUDIO_REMOVED:
-				
-				long newRunningTime = getRunningMSTimeFromRawAudio();
-				assert(newRunningTime > 0);
-				
-				long oldRunningTime = getRunningMSTimeFromMeta();
-				
-				// Keep meta as constant as possible for best reults
-				updateData(TrackWidgetCommons.META_RUNNINGMSTIME_TAG, TrackWidgetCommons.META_RUNNINGMSTIME_TAG + newRunningTime);
-
-				if (trackModel != null) {
-					
-					parent = getParentFrame();
-					
-					// Keep TrackGraphModel consistant
-					AudioStructureModel.getInstance().onTrackWidgetAudioEdited(
-							localFileName,
-							(parent != null) ? parent.getName() : null, 
-							newRunningTime);
-					
-					if (trackModel.getSelectionStart() == 0 && oldRunningTime > newRunningTime) {
-						long inittime = getInitiationTimeFromMeta();
-						inittime += (oldRunningTime - newRunningTime);
-						updateData(TrackWidgetCommons.META_INITIATIONTIME_TAG, 
-								TrackWidgetCommons.META_INITIATIONTIME_TAG + inittime);
-						
-						AudioStructureModel.getInstance().onTrackWidgetRemoved(localFileName, (parent != null) ? parent.getName() : null);
-						AudioStructureModel.getInstance().onTrackWidgetAnchored(localFileName, (parent != null) ? parent.getName() : null, 
-								inittime, newRunningTime, getName());
-					}
-					
-				}
-				
-				break;
-				
-
-		}
-
-	}
-	
-	public void setObservedSubject(Subject parent) {
-	}
-
-	@Override
-	public void paintInFreeSpace(Graphics g) {
-		paintInFreeSpace(g, false);
-	}
-	
-	public void paintInFreeSpace(Graphics g, boolean isAtFinalPass) { 
-
-		if (isLoaded()) { 
-			
-			
-			if (ExpandedTrackManager.getInstance().isAnyExpandedTrackVisible() &&
-					!isAtFinalPass) {
-				// If a expanded track is in view .. then must render lastly
-				return;
-			} 
-		
-			// Check to see if dragging over a EditableSampledTrackGraphView
-			MouseEvent me = MouseEventRouter.getCurrentMouseEvent();
-			if (me != null) {
-			
-				if (me.getComponent() != fulltrackView && 
-						me.getComponent() instanceof EditableSampledTrackGraphView &&
-						!((EditableSampledTrackGraphView)me.getComponent()).isPlaying()) {
-					
-
-					Point containerPoint = SwingUtilities.convertPoint(me.getComponent(), 
-							new Point(0,0), Browser._theBrowser.getContentPane());
-					
-					Shape clipBackUp = g.getClip();
-					g.setClip(null);
-					
-					g.setColor(Color.ORANGE);
-					((Graphics2D)g).setStroke(EditableSampledTrackGraphView.GRAPH_BAR_STROKE);
-					g.drawLine(
-							containerPoint.x + me.getX(),
-							containerPoint.y, 
-							containerPoint.x + me.getX(),
-							containerPoint.y + me.getComponent().getHeight());
-					
-					FrameGraphics.invalidateArea(new Rectangle(
-							containerPoint.x + me.getX(), 
-							containerPoint.y, 
-							1,
-							containerPoint.y + me.getComponent().getHeight() + 1));
-			
-					// Restore clip
-					g.setClip(clipBackUp);
-					
-					g.setColor(SEMI_TRANSPARENT_FREESPACE_BACKCOLOR);
-					g.fillRect(getX(), getY(), getWidth(), getHeight());
-					
-					if (isAtFinalPass) { // final pass does not draw the borders... so must manually draw them
-						g.setColor(Color.BLACK);
-						((Graphics2D)g).setStroke(FREESPACE_OUTLINING);
-						g.drawRect(getX(), getY(), getWidth(), getHeight());
-					}
-		
-					return;
-				}
-				
-			}
-		
-			
-			
-		}
-		
-		super.paintInFreeSpace(g);
-		
-		if (isLoaded()) {
-			
-			Shape clipBackUp = g.getClip();
-			Rectangle tmpClip = (clipBackUp != null) ? clipBackUp.getBounds() : 
-				new Rectangle(0, 0, 
-						Browser._theBrowser.getContentPane().getWidth(), 
-						Browser._theBrowser.getContentPane().getHeight());
-				
-			g.setClip(tmpClip.intersection(getBounds()));
-		
-			// Draw the name
-			String name = getName();
-			if (name == null) name = "Unnamed";
-
-			g.setFont(TrackWidgetCommons.FREESPACE_TRACKNAME_FONT);
-			g.setColor(TrackWidgetCommons.FREESPACE_TRACKNAME_TEXT_COLOR);
-			
-			// Center track name
-			FontMetrics fm   = g.getFontMetrics(TrackWidgetCommons.FREESPACE_TRACKNAME_FONT);
-			Rectangle2D rect = fm.getStringBounds(name, g);
-			
-			g.drawString(
-					name, 
-					this.getX() + (int)((getWidth() - rect.getWidth()) / 2), 
-					this.getY() + (int)((getHeight() - rect.getHeight()) / 2) + (int)rect.getHeight()
-					);
-			
-			g.setClip(clipBackUp);
-		
-		}
-		
-	}
-	
-	
-
-	@Override
-	public void paint(Graphics g) {
-		super.paint(g);
-		
-		if (isLoaded() && nameLabel != null) {
-			nameLabel.paint(g);
-			
-			if (shouldOmitIndexAudio()) {
-				
-				int shiftOffset = SoundDesk.getInstance().isPlaying(trackMix.getChannelID()) ?
-						-20 : 0;
-				
-				IconRepository.getIcon("omitindexed.png").paintIcon(
-						_swingComponent, 
-						g, 
-						getX() + getWidth() - EditableSampledTrackGraphView.LOCK_ICON_CORNER_OFFSET + shiftOffset, 
-						getY() + EditableSampledTrackGraphView.LOCK_ICON_CORNER_OFFSET - 16);
-				
-			}
-		}
-		
-	}
-	
-	private MouseEvent lastInsertME = null;
-	
-	@Override
-	protected void onParentStateChanged(int eventType) {
-		super.onParentStateChanged(eventType);
-
-		Frame parent = null;
-
-		switch (eventType) {
-
-			// Logic for injecting audio tracks into EditableSampledTrackGraphView's
-			case ItemParentStateChangedEvent.EVENT_TYPE_ADDED:
-			case ItemParentStateChangedEvent.EVENT_TYPE_ADDED_VIA_OVERLAY:
-
-				// Resume any layouts suspended by this track widget
-				FrameLayoutDaemon.getInstance().resumeLayout(this);
-				
-				if (trackModel != null) {
-
-					parent = getParentFrame();
-
-					// Determine new initation time according to anchored position
-					long initTime = (parent != null) ? 
-							FrameLayoutDaemon.getInstance().getMSAtX(getX(), parent)
-							: 0;
-
-					// Keep TrackGraphModel consistant
-					AudioStructureModel.getInstance().onTrackWidgetAnchored(
-							localFileName, 
-							(parent != null) ? parent.getName() : null, 
-							initTime, 
-							AudioMath.framesToMilliseconds(trackModel.getFrameCount(), trackModel.getFormat()),
-							getName());
-					
-					
-					MouseEvent me = MouseEventRouter.getCurrentMouseEvent();
-					
-					if (me != null && me != lastInsertME &&
-							(me.getButton() == MouseEvent.BUTTON2 ||
-									me.getButton() == MouseEvent.BUTTON3)) {
-						
-						// Although widgets filter out multiple parent changed events per mouse event - 
-						// it also considers the event type, and because this it remove itself from the frame/freespace
-						// it jumbles up the filtering so that this will be invoked for each corner.
-						lastInsertME = me;
-					
-						if (me.getComponent() != fulltrackView && 
-								me.getComponent() instanceof EditableSampledTrackGraphView) {
-
-							EditableSampledTrackGraphView editableSampledTrack = 
-								(EditableSampledTrackGraphView)me.getComponent();
-
-							// Inject this into the widget
-							try {
-								injectAudio(editableSampledTrack, me.getX(), true);
-							} catch (IOException ex) {
-								ex.printStackTrace();
-							}
-
-						}
-							
-					} else if (me == lastInsertME) {
-						// Note due to a injection removing this widget while in the midst of
-						// anchoring, the widget parent event filtering will not work thus
-						// must keep removing self until the mouse event has done.
-						removeSelf();
-					}
-				}
-				
-			case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN:
-			case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN_VIA_OVERLAY:
-
-				
-/*
-				// Listen for volume or mute changed events
-				trackMix.addObserver(this);
-				
-				// Listen for solo events and track sequence creation events
-				MixDesk.getInstance().addObserver(this);
-				*/
-				
-			break; 
-			
-
-			case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED:
-			case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED_VIA_OVERLAY:
-
-				// If yanking this from the frame into free space suspend the layout daemon
-				// for this frame
-				if (MouseEventRouter.getCurrentMouseEvent() != null &&
-						MouseEventRouter.getCurrentMouseEvent().getButton() == MouseEvent.BUTTON2 &&
-						MouseEventRouter.getCurrentMouseEvent() != lastInsertME) {
-					Frame suspended = DisplayIO.getCurrentFrame();
-					if (suspended != null) {
-						FrameLayoutDaemon.getInstance().suspendLayout(suspended, this);
-					}
-				}
-				
-				if (trackModel != null) {
-					parent = getParentFrame();
-					// Keep TrackGraphModel consistant
-					AudioStructureModel.getInstance().onTrackWidgetRemoved(localFileName, 
-							(parent != null) ? parent.getName() : null);
-				}
-				
-			case ItemParentStateChangedEvent.EVENT_TYPE_HIDDEN:
-				/*
-				trackMix.removeObserver(this);
-				MixDesk.getInstance().removeObserver(this);*/
-				
-				break; 
-				
-		}
-
-	}
-
-	/**
-	 * Injects this widgets bytes into a EditableSampledTrackGraphView
-	 * and removes this widget from freespace/parent-frame.
-	 * 
-	 * Doesn't inject bytes if the target EditableSampledTrackGraphView is in a playing state.
-	 * 
-	 * @param target
-	 * 		The target EditableSampledTrackGraphView to inject this widgets bytes into
-	 * 
-	 * @param graphX
-	 * 		The X pixel in the target's graph. Must be in valid range.
-	 * 
-	 * @param destroySelf
-	 * 		If want to destroy this widget
-	 * 
-	 * @throws IOException
-	 * 		If the insert failed ... can occur if bytes need to be
-	 * 		converted into targets format.
-	 */
-	public void injectAudio(EditableSampledTrackGraphView target, int graphX, boolean destroySelf)
-		throws IOException {
-		assert(target != null);
-		assert(graphX >= 0);
-		assert(graphX <= target.getWidth());
-		
-		// Cannot inject into EditableSampledTrackGraphView's while playing, although
-		// won't break anything, this would be confusing for the user.
-		if (target.isPlaying()) return;
-		
-		// Inject the audio at the graph poistion
-		int insertFramePoint = target.frameAtX(graphX);
-
-		// Inject audio 
-		target.insertAudio(
-				trackModel.getAllAudioBytes(), 
-				trackModel.getFormat(), 
-				insertFramePoint); 
-
-		// Note: if removed from free space .. then there should be no
-		// more references to this item therefore the memory will be freed
-		// eventually after this invoke
-		if (destroySelf) removeSelf();
-	}
-	
-	/**
-	 * 
-	 * @return
-	 * 		The unique loval filename for this track. Auto-assigned - and rememered. Never null.
-	 */
-	public String getLocalFileName() {
-		return localFileName;
-	}
-
-	/**
-	 * Determines the running time from the raw audio in memory.
-	 * 
-	 * @return
-	 * 		The running time or this track in MS.
-	 * 		-1 if not loaded.
-	 */
-	public long getRunningMSTimeFromRawAudio()
-	{
-		if (this.trackModel != null) {
-			return AudioMath.framesToMilliseconds(trackModel.getFrameCount(), trackModel.getFormat());
-		}
-		
-		return -1;
-	}
-	
-	/**
-	 * Determines the running time from the meta data.
-	 * 
-	 * @return
-	 * 		The running time or this track in MS.
-	 * 		-1 if unavilable.
-	 */
-	public long getRunningMSTimeFromMeta() {
-		return getStrippedDataLong(TrackWidgetCommons.META_RUNNINGMSTIME_TAG, new Long(-1));
-	}
-	
-	/**
-	 * @return
-	 * 		The name given to this widget... can be null.
-	 */
-	public String getName() {
-		if (this.trackModel != null)
-			return trackModel.getName();
-		else if (this.nameLabel != null) {
-			return nameLabel.getText();
-		} 
-		return getStrippedDataString(TrackWidgetCommons.META_NAME_TAG);
-	}
-	
-	/**
-	 * Determines the initiation time from the meta data.
-	 * 
-	 * @return
-	 * 		The initiation time or this track in MS.
-	 * 		null if unavilable.
-	 */
-	public Long getInitiationTimeFromMeta() {
-		return getStrippedDataLong(TrackWidgetCommons.META_INITIATIONTIME_TAG, null);
-	}
-	
-	private void updateBorderColor() {
-		
-		// Get border color currently used
-		Color oldC = getSource().getBorderColor();
-		
-		Color newC = TrackWidgetCommons.getBorderColor(
-				SoundDesk.getInstance().isSolo(trackMix.getChannelID()), 
-				trackMix.isMuted());
-		
-		// Update the color
-		if (!newC.equals(oldC)) {
-			setWidgetEdgeColor(newC);
-		}
-	}
-	
-	/**
-	 * State icons live at the top right corner
-	 *
-	 */
-	private void invalidateStateIcons() {
-		this.invalidateSelf(); // TODO
-	}
-	
-	private void setShouldOmitIndexAudio(boolean shouldOmit) {
-		this.shouldOmitAudioIndexing = shouldOmit;
-		if (!shouldOmit) {
-			removeData(META_DONT_INDEX_AUDIO_TAG);
-		} else {
-			addDataIfCaseInsensitiveNotExists(META_DONT_INDEX_AUDIO_TAG);
-		}
-		invalidateStateIcons();
-	}
-	
-	public boolean shouldOmitIndexAudio() {
-		return shouldOmitAudioIndexing;
-	}
-	
-//	/**
-//	 * Invalidates.
-//	 * @param shouldLayout
-//	 */
-//	private void setShouldLayout(boolean shouldLayout) {
-//		
-//		if (shouldLayout) {
-//			removeData(TrackWidgetCommons.META_OMIT_LAYOUT_TAG);
-//		} else {
-//			addDataIfCaseInsensitiveNotExists(TrackWidgetCommons.META_OMIT_LAYOUT_TAG);
-//		}
-//		
-//		invalidateStateIcons();
-//	}
-//	
-
-//	
-//	public boolean shouldLayout() {
-//		
-//		return (!containsDataTrimmedIgnoreCase(TrackWidgetCommons.META_OMIT_LAYOUT_TAG));
-//	}
-	
-	/**
-	 * Doesn't expand if not anchored ... 
-	 * @param addToExpand
-	 */
-	private void expand(boolean addToExpand) {
-		
-		Frame parent = getParentFrame();
-		String pfname = (parent != null) ?  parent.getName() : null;
-		if (pfname == null) return;
-
-		if (!ExpandedTrackManager.getInstance().isTrackInExpansionSelection(trackModel)) {
-			
-			if (addToExpand) {
-				
-				// Show the expanded view for this track once control has been released
-				ExpandedTrackManager.getInstance().addTrackToSelection(
-						trackModel,
-						pfname,
-						_swingComponent.getBounds(),
-						trackMix);
-
-			} else {
-
-				// Get rid of all popups
-				PopupManager.getInstance().hideAutohidePopups();
-				
-				int start = trackModel.getSelectionStart();
-				int length = trackModel.getSelectionLength();
-				
-				if (length <= 1) {
-					start = 0;
-					length = trackModel.getFrameCount();
-				}
-				
-				// Show the expanded view for this track
-				ExpandedTrackManager.getInstance().expandSingleTrack(
-						trackModel, 
-						_swingComponent.getBounds(),
-						trackMix,
-						pfname,
-						start,
-						length
-						);
-			
-			}
-			
-		} else {
-			
-			// Take away track from being selected.
-			ExpandedTrackManager.getInstance().removeTrackFromSelection(trackModel);
-		}
-		
-	}
-
-	/**
-	 * The small popup for common actions.
-	 * 
-	 * @author Brook Novak
-	 *
-	 */
-	private class PlaybackPopup extends PlaybackControlPopup implements Observer {
-
-		private static final long serialVersionUID = 1L;
-		
-		public PlaybackPopup() {
-			miscButton.setActionCommand("expand");
-			miscButton.setIcon(IconRepository.getIcon("expand.png"));
-			miscButton.setToolTipText("Expand");
-
-		}
-
-		@Override
-		public void onHide() {
-			super.onHide();
-			
-			// Listen for volume or mute changed events
-			trackMix.removeObserver(this);
-			
-			// Listen for solo events and track sequence creation events
-			SoundDesk.getInstance().removeObserver(this);
-		}
-
-		@Override
-		public void onShow() {
-			super.onShow();
-			// Listen for volume or mute changed events
-			trackMix.addObserver(this);
-			
-			// Listen for solo events and track sequence creation events
-			SoundDesk.getInstance().addObserver(this);
-			updateVolume((int)(100 * trackMix.getVolume()));
-			updateMute(trackMix.isMuted());
-			updateSolo(SoundDesk.getInstance().isSolo(trackMix.getChannelID()));
-		}
-
-		public void actionPerformed(ActionEvent e) {
-			if (trackModel == null) return;
-			
-			if (e.getSource() == playPauseButton) {
-
-				try {
-	
-					if (!SoundDesk.getInstance().isPlaying(trackMix.getChannelID())) { // play / resume
-						
-						int startFrame = -1, endFrame = -1;
-						
-						// Resume playback?
-						if (SoundDesk.getInstance().isPaused(trackMix.getChannelID())) {
-							startFrame = SoundDesk.getInstance().getLastPlayedFramePosition(trackMix.getChannelID());
-							if (startFrame >= 0 && startFrame < trackModel.getFrameCount()) {
-				
-								// The user may have edited the audio track and reselected it
-								// since the last pause. Thus select an appropriate end frame
-								endFrame = (trackModel.getSelectionLength() > 1) ?
-										trackModel.getSelectionStart() + trackModel.getSelectionLength():
-											trackModel.getFrameCount() - 1;
-									
-								// Changed selection? it play range invalid?
-								if (endFrame <= startFrame || startFrame < trackModel.getSelectionStart()) {
-									startFrame = -1; // Play new selection (see below)
-								
-								} else if (endFrame >= trackModel.getFrameCount()) {
-									endFrame = trackModel.getFrameCount() - 1;
-								}		
-								
-							}
-						}
-						
-						// Play from beginning of selection to end of selection
-						if (startFrame < 0) {
-							startFrame = trackModel.getSelectionStart();
-							endFrame = (trackModel.getSelectionLength() > 1) ?
-									startFrame + trackModel.getSelectionLength():
-										trackModel.getFrameCount() - 1;
-						}
-	
-						// Safety clamp:
-						if (endFrame >= trackModel.getFrameCount()) {
-							endFrame = trackModel.getFrameCount() - 1;
-						}	
-						
-						if (startFrame < endFrame) {
-							SoundDesk.getInstance().playSampledTrackModel(
-									trackModel, 
-									trackMix.getChannelID(), 
-									startFrame, 
-									endFrame, 
-									0);
-						}
-	
-					} else { // pause
-						
-						TrackSequence ts = SoundDesk.getInstance().getTrackSequence(trackMix.getChannelID());
-
-						if (ts != null &&
-								ts.isPlaying()) {
-							
-							// Mark channel as paused.
-							SoundDesk.getInstance().setPaused(trackMix.getChannelID(), true);
-							
-							// Stop playback for this channel
-							ApolloPlaybackMixer.getInstance().stop(ts);
-							
-						}
-						
-					}
-					
-				} catch (LineUnavailableException e1) {
-					e1.printStackTrace();
-				}
-				
-			} else if (e.getSource() == stopButton) {
-				
-				TrackSequence ts = SoundDesk.getInstance().getTrackSequence(trackMix.getChannelID());
-
-				// reset any paused mark
-				SoundDesk.getInstance().setPaused(trackMix.getChannelID(), false);
-				
-				if (ts != null &&
-						ts.isPlaying()) {
-					// Stop playback
-					ApolloPlaybackMixer.getInstance().stop(ts);
-				}
-				
-			} else if (e.getSource() == rewindButton) {
-				
-				trackModel.setSelection(0, 0);
-				SoundDesk.getInstance().setPaused(trackMix.getChannelID(), false);
-				
-			} else if (e.getSource() == miscButton) {
-				expand(false);
-			}
-		}
-		
-		public Subject getObservedSubject() {
-			return null;
-		}
-
-		public void setObservedSubject(Subject parent) {
-		}
-
-		/**
-		 * Receives events from the track model OR from the observed track sequence.
-		 */
-		public void modelChanged(Subject source, SubjectChangedEvent event) {
-
-			// Synch GUI with track state
-			switch (event.getID()) {
-			
-			case ApolloSubjectChangedEvent.TRACK_SEQUENCE_CREATED: // from sound desk
-				
-				if (event.getState().equals(trackMix.getChannelID())) {
-					// The channel being played is the same as this one ...
-					// even if the track model is unloaded must enter into a playing state
-					// if the created track sequence will play
-					TrackSequence ts = SoundDesk.getInstance().getTrackSequence(trackMix.getChannelID());
-					assert(ts != null);
-					assert(!ts.hasFinished());
-					assert(!ts.isPlaying());
-					ts.addObserver(this);
-				}
-
-				break;
-				
-			case ApolloSubjectChangedEvent.PLAYBACK_STARTED: // From observed track sequence
-				stopButton.setEnabled(true);
-				rewindButton.setEnabled(false);
-				playPauseButton.setIcon(IconRepository.getIcon("pause.png"));
-				
-				invalidateStateIcons();
-
-				SampledTrackEvo4.this.setWidgetEdgeThickness(TrackWidgetCommons.PLAYING_TRACK_EDGE_THICKNESS);
-				//FrameGraphics.refresh(true);
-					break;
- 
-				case ApolloSubjectChangedEvent.PLAYBACK_STOPPED: // From observed track sequence
-				
-				invalidateStateIcons();
-					
-				rewindButton.setEnabled(true);
-				stopButton.setEnabled(false);
-				playPauseButton.setIcon(IconRepository.getIcon("play.png"));
-				
-				// Note:
-				// No need to remove self from observing the dead track since the track references this
-				// and will get garbage collected
-				
-				SampledTrackEvo4.this.setWidgetEdgeThickness(TrackWidgetCommons.STOPPED_TRACK_EDGE_THICKNESS);
-
-				break;
-				
-			case ApolloSubjectChangedEvent.PAUSE_MARK_CHANGED: // When stopped or paused
-				/*
-				if (ae.getState().equals(trackMix.getChannelID())) {
-					
-					if (MixDesk.getInstance().isPaused(trackMix.getChannelID())) {
-						// Do nothing .. the paused mark is set prior to a stop
-					} else {
-						// Esnure that the GUI represents a stopped state
-						stopButton.setEnabled(false);
-						playPauseButton.setIcon(IconRepository.getIcon("play.png"));
-					}
-					
-				}*/
-				
-				break;
-				
-			case ApolloSubjectChangedEvent.VOLUME: // From obseved track mix
-				updateVolume((int)(100 * trackMix.getVolume()));
-				break;
-				
-			case ApolloSubjectChangedEvent.MUTE: // From obseved track mix
-				updateMute(trackMix.isMuted());
-				updateBorderColor();
-				break;
-				
-			case ApolloSubjectChangedEvent.SOLO_PREFIX_CHANGED: // From mix desk
-				updateSolo(SoundDesk.getInstance().isSolo(trackMix.getChannelID()));
-				updateBorderColor();
-				break;
-			}
-			
-	
-		
-		}
-
-		@Override
-		protected void volumeChanged() {
-			trackMix.setVolume(((float)volumeSlider.getValue()) / 100.0f);
-		}
-
-		@Override
-		protected void muteChanged() {
-			trackMix.setMuted(muteButton.isSelected());
-		}
-
-		@Override
-		protected void soloChanged() {
-			SoundDesk.getInstance().setSoloIDPrefix(soloButton.isSelected() ?
-					trackMix.getChannelID() : null
-					);
-		}
-
-	}
-	
-}
