<file_sep>package smp.presenters.buttons;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import smp.models.staff.StaffNoteLine;
import smp.models.staff.StaffSequence;
import smp.models.stateMachine.ProgramState;
import smp.models.stateMachine.StateMachine;
import smp.models.stateMachine.Variables;
import smp.presenters.api.button.ImagePushButton;
import smp.views.OptionsWindowController;

/**
 * This is the options button. It currently doesn't do anything.
 *
 * @author RehdBlob
 * @since 2013.12.25
 */
public class OptionsButtonPresenter extends ImagePushButton {

	//TODO: auto-add these model comments
	//====Models====
	private ObjectProperty<StaffSequence> theSequence;
	private IntegerProperty defaultVelocity;
	private StringProperty currentSoundset;
	private BooleanProperty songModified;

    /**
     * Default constructor.
     *
     * @param optionsButton
     *            The image that we want to link this to.
     * @param ct
     *            The FXML controller object.
     * @param im
     *            The Image loader object.
     */
    public OptionsButtonPresenter(ImageView optionsButton) {
        super(optionsButton);
        this.defaultVelocity = Variables.defaultVelocity;
        this.theSequence = Variables.theSequence;
        this.currentSoundset = StateMachine.getCurrentSoundset();
        this.songModified = StateMachine.getSongModified();
    }

    @Override
    protected void reactPressed(MouseEvent event) {
        ProgramState curr = StateMachine.getState().get();
        if (curr == ProgramState.EDITING)
			try {
				options();
			} catch (IOException e) {
				e.printStackTrace();
			}
    }

    @Override
    protected void reactReleased(MouseEvent event) {
        // do nothing.
    }

    /** Opens up an options dialog. 
     * @throws IOException */
    private void options() throws IOException {
        final Stage dialog = new Stage();
        initializeDialog(dialog);
        
        FXMLLoader loader = new FXMLLoader();
        OptionsWindowController owc = new OptionsWindowController();
        loader.setController(owc);
        loader.setLocation(new File(owc.getFXML()).toURI().toURL());
        
        Scene scene1 = new Scene((Parent) loader.load());
        dialog.setScene(scene1);
        dialog.showAndWait();
        while (dialog.isShowing()) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                // Do nothing
            }
        }
        updateValues();
    }

	/**
     * Sets the height, width, and other basic properties of this dialog box.
     *
     * @param dialog
     *            The dialog box that we are setting up.
     */
    private void initializeDialog(Stage dialog) {
        dialog.setHeight(280);
        dialog.setWidth(250);
        dialog.setResizable(false);
        dialog.setTitle("Options");
        dialog.initStyle(StageStyle.UTILITY);
    }

    /** Updates the different values of this program. */
    private void updateValues() {
        changeDefaultVol();
        multiplyTempo();
        changeSoundfont();
    }

    /** Updates the default volume of the program notes. */
    private void changeDefaultVol() {
        int vol = Variables.optionsDefaultVolume.get();
        this.defaultVelocity.set(vol >= 128 ? 127 : vol);
    }

    /** Updates the tempo from the options dialog. */
    private void multiplyTempo() {
        String txt = Variables.optionsTempoMultiplier.get();
        if (txt != null) {
            int num = 1;
            try {
                num = Integer.parseInt(txt);
            } catch (NumberFormatException e) {
                return;
            }
            if (num <= 1)
                return;
            ObservableList<StaffNoteLine> s = this.theSequence.get().getTheLines();
            ArrayList<StaffNoteLine> n = new ArrayList<StaffNoteLine>();
            for (int i = 0; i < s.size(); i++) {
                n.add(s.get(i));
                for (int j = 0; j < num - 1; j++)
                    n.add(new StaffNoteLine());
            }
            s.clear();
            s.addAll(n);
            DoubleProperty tempo = this.theSequence.get().getTempo();
            tempo.set(tempo.get() * num);
            
            //TODO:
//            controller.getModifySongManager().execute(new MultiplyTempoCommand(theStaff, num));
//            controller.getModifySongManager().record();
        }
    }
    
    /** Updates the program's soundfont, bind to song if checked. */
    private void changeSoundfont() {
    	//TODO:
//		SoundfontLoader sfLoader = controller.getSoundfontLoader();
//		String selectedSoundfont = soundfontsMenu.getSelectionModel().getSelectedItem();
//		try {
//			sfLoader.loadFromAppData(selectedSoundfont);
//		} catch (InvalidMidiDataException | IOException | MidiUnavailableException e) {
//			e.printStackTrace();
//		}
		
		String newSoundset = Variables.optionsBindedSoundfont.get();
		if(!theSequence.get().getSoundset().get().equals(newSoundset)) {
			theSequence.get().setSoundset(newSoundset);
			songModified.set(true);
		}
    }
}
<file_sep>package smp.components.buttons;

import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.text.Text;
import smp.ImageLoader;
import smp.components.general.ImageToggleButton;
import smp.fx.SMPFXController;
import smp.stateMachine.ProgramState;
import smp.stateMachine.StateMachine;

/**
 * The button that changes the mode of Super Mario Paint between arranger and
 * song modes.
 *
 * @author RehdBlob
 * @since 2014.05.21
 */
public class ModeButton extends ImageToggleButton {

    /**
     * This is the text field that displays the current mode of the program.
     */
    private Text modeDisp;

    /**
     * This creates a new ModeButton object.
     *
     * @param i
     *            This <code>ImageView</code> object that you are trying to link
     *            this button with.
     * @param ct
     *            The FXML controller object.
     * @param im
     *            The Image loader object.
     */
    public ModeButton(ImageView i, Text t, SMPFXController ct, ImageLoader im) {
        super(i, ct, im);
        modeDisp = t;
        modeDisp.setText("Song");
    }

    @Override
    public void reactPressed(MouseEvent event) {
        ProgramState curr = StateMachine.getState();
        if (curr != ProgramState.ARR_PLAYING && curr != ProgramState.SONG_PLAYING) {
            if (curr == ProgramState.EDITING) {
                modeDisp.setText("Arr");
                theStaff.setArrangerMode(true);
                controller.getClipboardLabel().setVisible(false);
                controller.getClipboardButton().setVisible(false);
            } else if (curr == ProgramState.ARR_EDITING) {
                modeDisp.setText("Song");
                theStaff.setArrangerMode(false);
                controller.getClipboardLabel().setVisible(true);
                controller.getClipboardButton().setVisible(true);
            }
        }
    }

}
<file_sep>package smp.models.stateMachine;

/**
 * These are the time signatures that we can select when using Super Mario
 * Paint.
 *
 * @author RehdBlob
 * @since 2013.06.28
 */
public enum TimeSignature {

    /**
     * These are different time signatures that we can have for the program.
     */
    TWO_FOUR("2/4"), THREE_FOUR("3/4"), FOUR_FOUR("4/4"), SIX_FOUR("6/4"), THREE_EIGHT(
            "3/8"), SIX_EIGHT("6/8"), TWELVE_EIGHT("12/8");

    /** The number on the top. */
    private int top;

    /** The number on the bottom. */
    private int bottom;

    /** What happens when you try to display the time signature. */
    private String displayName;

    /** Sets up the display name of the time signature type. */
    private TimeSignature(String disp) {
        displayName = disp;
        top = Integer.parseInt(disp.substring(0, disp.indexOf("/")));
        bottom = Integer.parseInt(disp.substring(disp.indexOf("/") + 1));
    }

    /** @return The integer on the top. */
    public int top() {
        return top;
    }

    /** @return The integer on the bottom. */
    public int bottom() {
        return bottom;
    }

    @Override
    public String toString() {
        return displayName;
    }

}
<file_sep>package smp;

import java.io.File;

import com.sun.javafx.application.LauncherImpl;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.application.Preloader.ProgressNotification;
import javafx.application.Preloader.StateChangeNotification;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.WindowEvent;
import smp.components.Values;
import smp.fx.SplashScreen;
import smp.stateMachine.Settings;
import smp.stateMachine.StateMachine;
import smp.views.MainWindowController;

/**
 * Super Mario Paint <br>
 * Based on the old SNES game from 1992, Mario Paint <br>
 * Inspired by:<br>
 * MarioSequencer (2002) <br>
 * TrioSequencer <br>
 * <NAME>'s Mario Paint Composer 1.0 / 2.0 (2007-2008) <br>
 * FordPrefect's Advanced Mario Sequencer (2009) <br>
 * The GUI is primarily written with JavaFX <br>
 *
 * Dev team: 
 * RehdBlob (2012 - current)
 * j574y923 (2017 - current)
 *
 *
 * @author RehdBlob
 * @author j574y923
 * 
 * @since 2012.08.16
 * @version 1.1.1
 */
public class TestMain extends Application {

    /**
     * Location of the Main Window fxml file.
     */
    private String mainFxml = "./MainWindow.fxml";

    /**
     * Location of the Advanced Mode (super secret!!) fxml file.
     */
    @SuppressWarnings("unused")
    private String advFxml = "./AdvWindow.fxml";

    /**
     * The number of threads that are running to load things for Super Mario
     * Paint.
     */
    private static final int NUM_THREADS = 2;

    /**
     * Loads all the sprites that will be used in Super Mario Paint.
     */
    public static Loader imgLoader = new ImageLoader();

    /**
     * Loads the soundfonts that will be used in Super Mario Paint.
     */
    public static Loader sfLoader = new SoundfontLoader();

    /** Image Loader thread. */
    private Thread imgLd;

    /** Soundfont loader thread. */
    private Thread sfLd;

    /** This is the main application stage. */
    private Stage primaryStage;

    /** This is the primary Scene on the main application stage. */
    private Scene primaryScene;

    /** This is the loaded FXML file. */
    private Parent root;

    /**
     * The controller class for the FXML.
     */
    private MainWindowController controller = new MainWindowController();

    /** Whether we are done loading the application or not. */
    private BooleanProperty ready = new SimpleBooleanProperty(false);

    /**
     * This should hopefully get something up on the screen quickly. This is
     * taken from http://docs.oracle.com/javafx/2/deployment/preloaders.htm
     */
    private void longStart() {

        // long init in background
        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                sfLd.start();
                imgLd.start();
                do {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    double imgStatus = imgLoader.getLoadStatus();
                    double sfStatus = sfLoader.getLoadStatus();
                    double ld = (imgStatus + sfStatus) * 100 / NUM_THREADS
                            * 0.5;
                    notifyPreloader(new ProgressNotification(ld));
                } while (imgLd.isAlive() || sfLd.isAlive());
                FXMLLoader loader = new FXMLLoader();
                loader.setController(controller);
                loader.setLocation(new File(controller.getFXML()).toURI().toURL());
                root = (Parent) loader.load();
                ready.setValue(Boolean.TRUE);
                return null;
            }
        };
        new Thread(task).start();
    }

    /** Explicitly create constructor without arguments. */
    public TestMain() {

    }

    /**
     * Starts three <code>Thread</code>s. One of them is currently a dummy
     * splash screen, the second an <code>ImageLoader</code>, and the third one
     * a <code>SoundfontLoader</code>.
     *
     * @see ImageLoader
     * @see SoundfontLoader
     * @see SplashScreen
     */
    @Override
    public void init() {
        imgLd = new Thread(imgLoader);
        sfLd = new Thread(sfLoader);
    }
    
    /**
     * Starts the application and loads the FXML file that contains a lot of the
     * class hierarchy.
     *
     * @param primaryStage
     *            The primary stage that will be showing the main window of
     *            Super Mario Paint.
     */
    @Override
    public void start(Stage ps) {
        primaryStage = ps;

        longStart();
        

        ready.addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> ov,
                    Boolean t, Boolean t1) {
                if (Boolean.TRUE.equals(t1)) {
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                primaryStage.setTitle("Super Mario Paint " + Settings.version);
                                setupCloseBehaviour(primaryStage);
                                primaryStage.setResizable(false);
                                primaryStage.setHeight(Values.DEFAULT_HEIGHT);
                                primaryStage.setWidth(Values.DEFAULT_WIDTH);
                                primaryScene = new Scene(root, 800, 600);
                                primaryStage.setScene(primaryScene);
                                new KeyboardListeners(primaryStage);
                                notifyPreloader(new ProgressNotification(1));
                                notifyPreloader(new StateChangeNotification(
                                        StateChangeNotification.Type.BEFORE_START));
                                primaryStage.show();
                            } catch (Exception e) {
                                e.printStackTrace();
                                System.exit(1);
                            }
                        }
                    });
                }
            }
        });
        ;
    }

    /**
     * Protip for JavaFX users: Make sure you define your application close
     * behavior.
     */
    @Override
    public void stop() {
//    	Platform.exit();
        System.exit(0);
    }

    /**
     * Got this off of https://community.oracle.com/thread/2247058?tstart=0 This
     * appears quite useful as a 'really exit?' type thing. This dialog
     * currently needs some work, so we're not going to include it in the alpha
     * release.
     *
     * @param primaryStage
     *            The main stage of interest.
     */
    private void setupCloseBehaviour(final Stage primaryStage) {
        primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {

            @Override
            public void handle(WindowEvent event) {
                if (StateMachine.isSongModified()
                        || StateMachine.isArrModified()) {
                    final Stage dialog = new Stage();
                    dialog.setHeight(100);
                    dialog.setWidth(300);
                    dialog.setResizable(false);
                    dialog.initStyle(StageStyle.UTILITY);
                    Label label = new Label();
                    label.setMaxWidth(300);
                    label.setWrapText(true);
                    if (StateMachine.isSongModified()
                            && StateMachine.isArrModified()) {
                        label.setText("The song and arrangement have\n"
                                + "both not been saved! Really exit?");
                    } else if (StateMachine.isSongModified()) {
                        label.setText("The song has not been saved! "
                                + "Really exit?");
                    } else if (StateMachine.isArrModified()) {
                        label.setText("The arrangement has not been saved! "
                                + "Really exit?");
                    }
                    Button okButton = new Button("Yes");
                    okButton.setOnAction(new EventHandler<ActionEvent>() {

                        @Override
                        public void handle(ActionEvent event) {
                            dialog.close();
                            stop();

                        }
                    });
                    Button cancelButton = new Button("No");
                    cancelButton.setOnAction(new EventHandler<ActionEvent>() {

                        @Override
                        public void handle(ActionEvent event) {
                            dialog.close();
                        }
                    });
                    FlowPane pane = new FlowPane(10, 10);
                    pane.setAlignment(Pos.CENTER);
                    pane.getChildren().addAll(okButton, cancelButton);
                    VBox vBox = new VBox(10);
                    vBox.setAlignment(Pos.CENTER);
                    vBox.getChildren().addAll(label, pane);
                    Scene scene1 = new Scene(vBox);
                    dialog.setScene(scene1);
                    dialog.show();
                } else {
                    stop();
                }
                event.consume();
            }
        });
    }
    
    /**
     * Launches the application.
     *
     * @param args
     *            Sets debug options on or off.
     */
    public static void main(String[] args) {
        if (args.length == 0) {
            try {
                LauncherImpl.launchApplication(TestMain.class,
                        SplashScreen.class, args);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if (args.length > 0
                && (args[0].equals("--debug") || args[0].equals("--d"))) {
            if (args[1] != null) {
                try {
                    Settings.setDebug(Integer.parseInt(args[1]));
                } catch (NumberFormatException e) {
                    Settings.setDebug(1);
                }
            } else {
                Settings.setDebug(1);
            }
            try {
                LauncherImpl.launchApplication(TestMain.class,
                        SplashScreen.class, args);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

	}
}
<file_sep>package smp.presenters.staff;

import java.util.ArrayList;

import javafx.beans.property.DoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import smp.ImageIndex;
import smp.ImageLoader;
import smp.models.stateMachine.StateMachine;

public class StaffPlayBarsPresenter {

	// TODO: auto-add these model comments
	// ====Models====
	private DoubleProperty measureLineNumber;

	private HBox staffPlayBars;

	private ArrayList<ImageView> staffPlayBarsIV;

	// TODO: set
	private ImageLoader il;

	public StaffPlayBarsPresenter(HBox staffPlayBars) {
		this.staffPlayBars = staffPlayBars;
		initializeStaffPlayBars(this.staffPlayBars);

		this.measureLineNumber = StateMachine.getMeasureLineNum();
		setupViewUpdater();
	}

	/**
	 * Sets up the note highlighting functionality.
	 *
	 * @param staffPlayBars
	 *            The bars that move to highlight different notes.
	 */
	private void initializeStaffPlayBars(HBox playBars) {
		staffPlayBarsIV = new ArrayList<ImageView>();
		for (Node n : playBars.getChildren()) {
			ImageView i = (ImageView) n;
			i.setImage(il.getSpriteFX(ImageIndex.PLAY_BAR1));
			i.setVisible(false);
			staffPlayBarsIV.add(i);
		}
	}

	private void setupViewUpdater() {
		this.measureLineNumber.addListener(new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				// TODO:
			}
		});
	}
}
<file_sep>package smp.presenters.buttons;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StreamCorruptedException;
import java.text.ParseException;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.stage.FileChooser;
import smp.fx.Dialog;
import smp.models.staff.StaffArrangement;
import smp.models.staff.StaffSequence;
import smp.models.stateMachine.ProgramState;
import smp.models.stateMachine.StateMachine;
import smp.models.stateMachine.Variables;
import smp.presenters.api.button.ImagePushButton;
import smp.presenters.api.load.MPCDecoder;
import smp.presenters.api.load.Utilities;

/**
 * This is the button that loads a song.
 *
 * @author RehdBlob
 * @since 2013.09.28
 */
public class LoadButtonPresenter extends ImagePushButton {

	//TODO: auto-add these model comments
	//====Models====
	private ObjectProperty<StaffSequence> theSequence;
	private ObjectProperty<StaffArrangement> theArrangement;
    private StringProperty theSequenceName;
	private BooleanProperty songModified;
	private BooleanProperty arrModified;
	private ObjectProperty<File> currentDirectory;
	private StringProperty theArrangementName;
	private ObjectProperty<ProgramState> programState;

	/**
     * Default constructor.
     *
     * @param loadButton
     *            This is the <code>ImageView</code> object that will house the
     *            Load button.
     * @param ct
     *            The FXML controller object.
     * @param im
     *            The Image loader object.
     */
    public LoadButtonPresenter(ImageView loadButton) {
        super(loadButton);
        this.theSequenceName = Variables.theSequenceName;
        this.theSequence = Variables.theSequence;
        this.theArrangementName = Variables.theArrangementName;
        this.theArrangement = Variables.theArrangement;
        this.songModified = StateMachine.getSongModified();
        this.arrModified = StateMachine.getArrModified();
        this.currentDirectory = StateMachine.getCurrentDirectory();
        this.programState = StateMachine.getState();
    }

	@Override
    protected void reactPressed(MouseEvent event) {
        load();
    }

    @Override
    protected void reactReleased(MouseEvent event) {
        // do nothing.
    }

    /** This loads the song or arrangement. */
    private void load() {
        ProgramState curr = this.programState.get();
        if (curr == ProgramState.EDITING)
            loadSong();
        else if (curr == ProgramState.ARR_EDITING)
            loadArrangement();
    }

    /** This loads a song. */
    private void loadSong() {
        boolean cont = true;
        if (this.songModified.get())
            cont = Dialog
                    .showYesNoDialog("The current song has been modified!\n"
                            + "Load anyway?");
        File inputFile = null;
        if (cont) {
            try {
                FileChooser f = new FileChooser();
                f.setInitialDirectory(this.currentDirectory.get());
                inputFile = f.showOpenDialog(null);
                if (inputFile == null)
                    return;
                this.currentDirectory.set(new File(inputFile.getParent()));
                loadSong(inputFile);
            } catch (Exception e) {
                Dialog.showDialog("Not a valid song file.");
                e.printStackTrace();
            }
        }
    }

    /** This loads a song, given a file. */
    private void loadSong(File inputFile) {
    	//TODO: only set a new staffsequence for the theSequence model,
    	//TODO: let all the other presenters update themselves...
        try {
            StaffSequence loaded = null;
            try {
                loaded = MPCDecoder.decode(inputFile);
            } catch (ParseException e1) {
                loaded = Utilities.loadSong(inputFile);
            }
            if (loaded == null) {
                throw new IOException();
            }
//            String fname = Utilities.populateStaff(loaded, inputFile, false,
//                    theStaff, controller);
            boolean mpc = false;
			String fname = inputFile.getName();
			if (mpc)
				fname = fname.substring(0, fname.lastIndexOf(']'));
			else
				fname = fname.substring(0, fname.lastIndexOf("."));
			
			this.theSequence.set(loaded);
            this.theSequenceName.set(fname);
            this.songModified.set(false);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            Dialog.showDialog("Problem loading file!");
            e.printStackTrace();
        } catch (Exception e) {
            Dialog.showDialog("Not a valid song file.");
            e.printStackTrace();
        }
    }

    /** This loads an arrangement. */
    private void loadArrangement() {
    	//TODO: only set a new staffarrangement for the theArrangement model,
    	//TODO: let all the other presenters update themselves...
        boolean cont = true;
        if (this.songModified.get() || this.arrModified.get()) {
            if (this.songModified.get() && this.arrModified.get()) {
                cont = Dialog
                        .showYesNoDialog("The current song and arrangement\n"
                                + "have both been modified!\nLoad anyway?");
            } else if (this.songModified.get()) {
                cont = Dialog
                        .showYesNoDialog("The current song has been modified!\n"
                                + "Load anyway?");
            } else if (this.arrModified.get()) {
                cont = Dialog
                        .showYesNoDialog("The current arrangement has been\n"
                                + "modified! Load anyway?");
            }
        }
        File inputFile = null;
        if (cont) {
            try {
                FileChooser f = new FileChooser();
                f.setInitialDirectory(this.currentDirectory.get());
                inputFile = f.showOpenDialog(null);
                if (inputFile == null)
                    return;
                this.currentDirectory.set(new File(inputFile.getParent()));
                StaffArrangement loaded = Utilities.loadArrangement(inputFile);
                Utilities.normalizeArrangement(loaded, inputFile);
//                Utilities.populateStaffArrangement(loaded, inputFile, false,
//                        theStaff, controller);
                this.theArrangement.set(loaded);
                this.theArrangementName.set(inputFile.getName());
                this.songModified.set(false);
                this.arrModified.set(false);
            } catch (ClassNotFoundException | StreamCorruptedException
                    | NullPointerException e) {
                try {
                    StaffArrangement loaded = MPCDecoder
                            .decodeArrangement(inputFile);
                    StateMachine.setCurrentDirectory(new File(inputFile.getParent()));
                    Utilities.normalizeArrangement(loaded, inputFile);
//                    Utilities.populateStaffArrangement(loaded, inputFile, true,
//                            theStaff, controller);
                    this.theArrangement.set(loaded);
                    this.theArrangementName.set(inputFile.getName());
                    this.songModified.set(false);
                } catch (Exception e1) {
                    e1.printStackTrace();
                    Dialog.showDialog("Not a valid arrangement file.");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
<file_sep>package smp.presenters.api.clipboard;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Slider;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import smp.components.Values;
import smp.stateMachine.StateMachine;

/**
 * The rubber band is just a rectangle that gets resized by the mouse. It is a
 * model to hold boundary information. The bounds can then be used to represent
 * a region encapsulating notes useful for clipboard functions like copy.
 * 
 * Adapted from https://github.com/varren/JavaFX-Resizable-Draggable-Node
 */
public class StaffRubberBand extends Rectangle {

    private double xOrigin;
    private double yOrigin;
 
	private double lineMinBound = 0;
	private double lineMaxBound = 0;
	private double positionMinBound = 0;
	private double positionMaxBound = 0;
	private double volumeYMaxCoord = 0;
    private double lineSpacing = 0;
    private double positionSpacing = 0;
	private double marginVertical = 0;
	private double marginHorizontal = 0;
	private int scrollOffset = 0;
	private int originLine = 0;
	
	private Text outsideBoundText = new Text();

    public StaffRubberBand() {
        super();
        this.setFill(StaffClipboard.HIGHLIGHT_FILL);
        outsideBoundText.setFill(Color.RED);
    }
    
	/**
	 * Adds a change listener to slider. Every change that occurs will
	 * horizontally resize the rubberband.
	 * 
	 * @param slider
	 */
	public void setScrollBarResizable(Slider slider) {

		slider.valueProperty().addListener(new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> arg0, Number oldVal, Number newVal) {		
				
				// only xOrigin will move so we need to get the other x bound that will remain the same	
			    double sameEndX = 0;
				if (getTranslateX() < xOrigin) {
					sameEndX = getTranslateX();
				} else {// if (rbRef.getTranslateX() == xOrigin)
					sameEndX = getTranslateX() + getWidth();
				}
        		
				int change = newVal.intValue() - oldVal.intValue();
				applyScroll(change);

				// resizeBand(sameEndX, getTranslateY()); doesn't work for some reason...
				// do this instead
		        if (sameEndX >= xOrigin) {
		            setTranslateX(xOrigin);
		            setWidth(sameEndX - xOrigin);
		        } else {
		            setTranslateX(sameEndX);
		            setWidth(xOrigin - sameEndX);
		        }
		        
		        displayBoundsText();
		        
			}

        });
    }

	/**
	 * Displays bounds text at either the left or right edge of the rubberband,
	 * whichever is cut off. This tells us where the line bound is. Adds the out
	 * of bounds text in the rubberBandLayer if bound goes off the window.
	 * Removes the out of bounds text if in the window.
	 */
	public void displayBoundsText() {
		Pane rubberBandLayer = (Pane) getParent();
		if (rubberBandLayer == null)
    		return;
    	
		int relativeScrollOffset = scrollOffset + originLine;
		if (relativeScrollOffset < 0 || Values.NOTELINES_IN_THE_WINDOW < relativeScrollOffset) {
			if (!rubberBandLayer.getChildren().contains(outsideBoundText)) {
				rubberBandLayer.getChildren().add(outsideBoundText);

				double transX = relativeScrollOffset < 0 ? getTranslateX() + 10 : getTranslateX() + getWidth() - 40;
				outsideBoundText.setTranslateX(transX);
				outsideBoundText.setTranslateY(getTranslateY() + getHeight());
			}

			if(relativeScrollOffset < 0){
				int outsideBoundLineNum = (relativeScrollOffset + StateMachine.getMeasureLineNum()) / 4 + 1;
				int numerator = (relativeScrollOffset + StateMachine.getMeasureLineNum()) % 4 + 1;
				outsideBoundText.setText(outsideBoundLineNum + " " + getFraction(numerator) + " . . .");
			} else {
				int outsideBoundLineNum = (relativeScrollOffset + StateMachine.getMeasureLineNum() - 1) / 4 + 1;
				int numerator = (relativeScrollOffset + StateMachine.getMeasureLineNum() - 1) % 4 + 1;
				outsideBoundText.setText(". . . " + outsideBoundLineNum + " " + getFraction(numerator));
			}

		} else {
			rubberBandLayer.getChildren().remove(outsideBoundText);
		}
	}
	
	private String getFraction(int numerator) {
		char numeratorCharacter;
		switch (numerator) {
		case 1:
			numeratorCharacter = '\u2071';
			break;
		case 2:
			numeratorCharacter = '\u00B2';
			break;
		case 3:
			numeratorCharacter = '\u00B3';
			break;
		case 4:
			numeratorCharacter = '\u2074';
			break;
		default:
			numeratorCharacter = '\u2071';
			break;
		}
		//return "x/4"
		return numeratorCharacter + "\u2044\u2084";
	}
	
    /**
     * Updates scrollOffset with change then updates xOrigin.
     * 
     * @param change scroll delta
     */
    public void applyScroll(int change) {
		//note -change because xOrigin moves other way
		int begPos = scrollOffset + originLine;
		int endPos = scrollOffset - change + originLine;
		int xOriginChange = Math.max(0, Math.min(endPos, Values.NOTELINES_IN_THE_WINDOW))
				- Math.max(0, Math.min(begPos, Values.NOTELINES_IN_THE_WINDOW));
		
		xOrigin = Math.max(lineMinBound, Math.min(xOrigin + lineSpacing * xOriginChange, lineMaxBound));
		
		//if xOrigin moves out of bounds still, set min and max bound positioning
		if(xOriginChange == 0) {
			if(Values.NOTELINES_IN_THE_WINDOW < endPos)
				xOrigin = lineMaxBound;
			else if(endPos < 0)
				xOrigin = lineMinBound;
		}
		
		scrollOffset -= change;
    }
    
    /**
	 * Makes rubber band visible. First, call this to begin drawing the rubber
	 * band.
	 *
	 * @param x
	 *            x-coord to begin rectangle shape at
	 * @param y
	 *            y-coord to begin rectangle shape at
	 */
	public void begin(double x, double y) {

		if (x < lineMinBound - marginHorizontal || x > lineMaxBound + marginHorizontal
				|| y < positionMinBound - marginVertical
				|| y > Math.max(volumeYMaxCoord, positionMaxBound + marginVertical)) {
			return;
		}  
		
        this.setWidth(0);
        this.setHeight(0);
        this.setTranslateX(x);
        this.setTranslateY(y);
        this.setVisible(true);
        
        xOrigin = x;
        yOrigin = y;

        scrollOffset = 0;    
        originLine = getLineBegin();
    }

	/**
	 * Redraws the rubber band to a new size. Second call this.
	 * 
	 * Note: this method isn't called resize because the JavaFX API uses a
	 * function of that name.
	 *
	 * @param x
	 *            x-coord to resize to
	 * @param y
	 *            y-coord to resize to
	 */
	public void resizeBand(double x, double y) {
        if (x > lineMaxBound + marginHorizontal) {
            x = lineMaxBound + marginHorizontal;
        } else if (x < lineMinBound - marginHorizontal) {
            x = lineMinBound - marginHorizontal;
        }
        if (y > Math.max(volumeYMaxCoord, positionMaxBound + marginVertical)) {
            y = Math.max(volumeYMaxCoord, positionMaxBound + marginVertical);
        } else if (y < positionMinBound - marginVertical) {
            y = positionMinBound - marginVertical;
        }

        if (x >= xOrigin) {
            this.setTranslateX(xOrigin);
            this.setWidth(x - xOrigin);
        } else {
            this.setTranslateX(x);
            this.setWidth(xOrigin - x);
        }

        if (y >= yOrigin) {
            this.setTranslateY(yOrigin);
            this.setHeight(y - yOrigin);
        } else {
            this.setTranslateY(y);
            this.setHeight(yOrigin - y);
        }
    }
    
    /**
     * Second call this to redraw the rubber band to a new size. Alters only x 
     * dimension.
     *
     * @param x x-coord to resize to
     */
    @Deprecated
    public void resizeX(double x){
        
//        if (x > Constants.WIDTH_DEFAULT) {
//            x = Constants.WIDTH_DEFAULT;
//        } else if (x < 0) {
//            x = 0;
//        }

        if (x >= xOrigin) {
            this.setTranslateX(xOrigin);
            this.setWidth(x - xOrigin);
        } else {
            this.setTranslateX(x);
            this.setWidth(xOrigin - x);
        }
    }
    
    /**
     * Second call this to redraw the rubber band to a new size. Alters only y 
     * dimension.
     *
     * @param y y-coord to resize to
     */
    @Deprecated
    public void resizeY(double y){

//        if (y > Constants.HEIGHT_DEFAULT) {
//            y = Constants.HEIGHT_DEFAULT;
//        } else if (y < 0) {
//            y = 0;
//        }
        
        if (y >= yOrigin) {
            this.setTranslateY(yOrigin);
            this.setHeight(y - yOrigin);
        } else {
            this.setTranslateY(y);
            this.setHeight(yOrigin - y);
        }
        
    }

	/**
	 * Ends drawing the rubber band and hide it. Sets rubber band invisible.
	 * Lastly call this.
	 */
	public void end() {
        this.setVisible(false);

        Pane rubberBandLayer = (Pane)this.getParent();
        if(rubberBandLayer != null)
        	rubberBandLayer.getChildren().remove(outsideBoundText);
    }

    /**
     * 
     * @return beginning line of rubberband relative to the current frame of lines
     */
	public int getLineBegin() {

		return scrollOffset < 0 ? getLineBeginRaw() + scrollOffset + originLine : getLineBeginRaw();
	}

	private int getLineBeginRaw() {

		double bandMinX = this.getTranslateX();
		
		if (bandMinX < lineMinBound + lineSpacing / 2) {
			return 0;
		} else if (bandMinX > lineMaxBound - lineSpacing / 2) {
			return Values.NOTELINES_IN_THE_WINDOW;
		} else {

			double firstLineX = (lineMinBound + lineSpacing / 2);
			return (int) ((bandMinX - firstLineX) / lineSpacing) + 1;
		}
	}

    /**
     * 
     * @return ending line of rubberband relative to the current frame of lines
     */
	public int getLineEnd() {
		return scrollOffset > 0 ? getLineEndRaw() + scrollOffset + originLine : getLineEndRaw();
	}
	
	private int getLineEndRaw() {

		double bandMaxX = this.getTranslateX() + this.getWidth();
		if (bandMaxX < lineMinBound + lineSpacing / 2) {
			return -1;
		} else if (bandMaxX > lineMaxBound - lineSpacing / 2) {
			return Values.NOTELINES_IN_THE_WINDOW - 1;
		} else {

			double firstLineX = (lineMinBound + lineSpacing / 2);
			return (int) ((bandMaxX - firstLineX) / lineSpacing);
		}
	}

    public int getPositionBegin() {  
    	
    	double bandBottomPosY = this.getTranslateY() + this.getHeight();
        if (bandBottomPosY < positionMinBound + positionSpacing / 2) {
            return Values.NOTES_IN_A_LINE - 1;
        } else if (bandBottomPosY > positionMaxBound - positionSpacing / 2) {
            return 0;//-1;
        } else {
        	
        	double firstPosY = positionMinBound;// + positionSpacing / 2;
        	//this is half because positions overlap one another
        	double positionHalfSpacing = positionSpacing / 2;
            return Values.NOTES_IN_A_LINE - (int)((bandBottomPosY - firstPosY) / positionHalfSpacing);// - 1;
        }
    }

	public int getPositionEnd() {
		
		double bandTopPosY = this.getTranslateY();
		if (bandTopPosY <  positionMinBound + positionSpacing / 2) {
			return Values.NOTES_IN_A_LINE - 1;
		} else if (bandTopPosY > positionMaxBound - positionSpacing / 2) {
			return 0;//0;
		} else {
        	
        	double firstPosY = positionMinBound;// + positionSpacing / 2;
        	//this is half because positions overlap one another
        	double positionHalfSpacing = positionSpacing / 2;
            return Values.NOTES_IN_A_LINE - (int)((bandTopPosY - firstPosY) / positionHalfSpacing) - 1;
		}
	}
	
	public void setLineMinBound(double x) {
		lineMinBound = x;
	}

	public void setLineMaxBound(double x) {
		lineMaxBound = x;
	}

	public void setPositionMinBound(double y) {
		positionMinBound = y;
	}

	public void setPositionMaxBound(double y) {
		positionMaxBound = y;
	}

	public void setVolumeYMaxCoord(double y) {
		volumeYMaxCoord = y;
	}

	public void setLineSpacing(double dx) {
		lineSpacing = dx;
	}

	public void setPositionSpacing(double dy) {
		positionSpacing = dy;
	}

	public void setMarginVertical(double m) {
		marginVertical = m;
	}
	
	public void setMarginHorizontal(double m) {
		marginHorizontal = m;
	}
}
<file_sep>package smp.presenters;

import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.text.Text;
import smp.models.staff.StaffSequence;
import smp.models.stateMachine.Variables;
import smp.presenters.api.reattachers.SequenceReattacher;

public class TempoIndicatorPresenter {

	//TODO: auto-add these model comments
	//====Models====
	private ObjectProperty<StaffSequence> theSequence;
	
	private Text tempoIndicator;

	private SequenceReattacher sequenceReattacher;

	public TempoIndicatorPresenter(Text tempoIndicator) {
		this.tempoIndicator = tempoIndicator;

		this.theSequence = Variables.theSequence;
		this.sequenceReattacher = new SequenceReattacher(this.theSequence);
		setupViewUpdater();
	}

	private void setupViewUpdater() {
		tempoIndicator.textProperty().bind(theSequence.get().getTempo().asString());
		sequenceReattacher.setOnReattachListener(new ChangeListener<StaffSequence>() {

			@Override
			public void changed(ObservableValue<? extends StaffSequence> observable, StaffSequence oldValue,
					StaffSequence newValue) {
				StaffSequence newSequence = (StaffSequence) newValue;
				tempoIndicator.textProperty().unbind();
				tempoIndicator.textProperty().bind(newSequence.getTempo().asString());
			}
		});
	}
}
<file_sep>package smp.components.buttons;

import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import smp.ImageIndex;
import smp.ImageLoader;
import smp.components.general.ImageRadioButton;
import smp.fx.SMPFXController;
import smp.stateMachine.ProgramState;
import smp.stateMachine.StateMachine;

/**
 * Wrapper class for an ImageView that holds the stop button image. Pressing the
 * button changes the image and also changes the state of the program.
 *
 * @author RehdBlob
 * @since 2012.09.14
 */
public class StopButton extends ImageRadioButton {

    /**
     * Instantiates the stop button.
     *
     * @param i
     *            The ImageView that will be manipulated by this class.
     * @param ct
     *            The FXML controller object.
     * @param im
     *            The Image loader object.
     */
    public StopButton(ImageView i, SMPFXController ct, ImageLoader im) {
        super(i, ct, im);
        getImages(ImageIndex.STOP_PRESSED, ImageIndex.STOP_RELEASED);
        pressImage();
        isPressed = true;
    }
    

    @Override
    public void reactPressed(MouseEvent e) {
        if (e != null) {
        	theStaff.stopSounds();
        }
        if (isPressed)
            return;
        super.reactPressed(e);
        if (StateMachine.getState() == ProgramState.SONG_PLAYING) {
            StateMachine.setState(ProgramState.EDITING);
            theStaff.stopSong();
        } else if (StateMachine.getState() == ProgramState.ARR_PLAYING) {
            StateMachine.setState(ProgramState.ARR_EDITING);
            theStaff.stopSong();
        }
    }


}
<file_sep>package smp.presenters;

import java.util.ArrayList;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;

import javax.sound.midi.MidiChannel;

import smp.ImageIndex;
import smp.ImageLoader;
import smp.SoundfontLoader;
import smp.TestMain;
import smp.components.Values;
import smp.components.InstrumentIndex;
import smp.components.staff.sequences.Note;
import smp.models.staff.StaffSequence;
import smp.models.stateMachine.Settings;
import smp.models.stateMachine.Variables;
import smp.presenters.api.reattachers.SequenceReattacher;

/**
 * The line of buttons that appear for the Instrument Line at the top of the
 * user interface for Super Mario Paint. The user selects the instrument that
 * they're using here. Clicking on a portrait causes a sound to play while the
 * active instrument is changed.
 *
 * @author RehdBlob
 * @since 2012.08.21
 */
public class InstLinePresenter {

	//TODO: auto-add these model comments
	//====Models====
	private ObjectProperty<StaffSequence> theSequence;
	private ObjectProperty<InstrumentIndex> selectedInstrument;
	
    /**
     * The default note number.
     */
    private static int DEFAULT_NOTE;

    /**
     * An ArrayList of ImageView objects being used as buttons.
     */
    private ArrayList<ImageView> buttons = new ArrayList<ImageView>();

    /** This is a list of names for the different instrument line instruments. */
    private ArrayList<String> instrumentLineImages = new ArrayList<String>();

    /** This is the image loader class. */
    private ImageLoader il = (ImageLoader) TestMain.imgLoader;

    /**
     * The MidiChannel array objects that will be holding references for
     * sound-playing capabilities.
     */
    private MidiChannel[] chan;
    
	private SequenceReattacher sequenceReattacher;


    /**
     * Initializes the ImageView ArrayList.
     *
     * @param i
     *            An ArrayList of ImageView references intended to be displayed
     *            as an instrument line.
     */
    public InstLinePresenter(HBox instLine) {
        for (Node i : instLine.getChildren())
            buttons.add((ImageView) i);
        setupButtons();
        DEFAULT_NOTE = Note.A3.getKeyNum();
        /*
         * For some reason, the piranha and coin are flipped in all soundfonts.
         * The solution here is unfortunately to just flip the images.
         */
        ObservableList<Node> n = instLine.getChildren();
        Node nd = n.remove(15);
        n.add(16, nd);
        
        this.theSequence = Variables.theSequence;
        this.selectedInstrument = Variables.selectedInstrument;
        this.sequenceReattacher = new SequenceReattacher(this.theSequence);
        setupViewUpdater();
    }

    /**
     * Initializes the buttons with event handlers.
     */
    public void setupButtons() {
        int ind = 0;
        for (final InstrumentIndex i : InstrumentIndex.values()) {
            if (ind > Values.NUMINSTRUMENTS - 1) {
                break;
            }
            instrumentLineImages.add(i.toString());
            buttons.get(i.getChannel() - 1).setOnMousePressed(
                    new EventHandler<MouseEvent>() {

                        @Override
                        public void handle(MouseEvent event) {
                            if (event.isShiftDown()) {
                                toggleNoteExtension(i);
                                event.consume();
                            } else {
                            	selectedInstrument.set(i);
                                playSound(i);
                                event.consume();
                            }
                        }

                    });
            ind++;
        }
        try {
            chan = SoundfontLoader.getChannels();
        } catch (NullPointerException e) {
            System.err.println("Unable to load soundfont channels.");
            System.exit(1);
        }
    }

    /**
     * Plays the default "A" sound when selecting an instrument.
     *
     * @param i
     *            The InstrumentIndex for the instrument
     */
    public void playSound(InstrumentIndex i) {
        int ind = i.getChannel() - 1;
        if (chan[ind] != null) {
            chan[ind].noteOn(DEFAULT_NOTE, Values.MAX_VELOCITY);
            if (Settings.debug > 0)
                System.out.println("Channel " + (ind + 1) + " Instrument: "
                        + i.name());
        }
    }

    /**
     * Updates the note extensions display at the instrument selection line.
     */
    @Deprecated
    public void updateNoteExtensions() {
        for (int j = 0; j < Values.NUMINSTRUMENTS; j++) {
//                changePortrait(j, ext[j].get());
        }
    }

    /**
     * Sets the selected instrument to extend mode.
     *
     * @param i
     *            The instrument that we are trying to set to extend.
     */
    private void toggleNoteExtension(InstrumentIndex i) {
        BooleanProperty[] ext = this.theSequence.get().getNoteExtensions();
        ext[i.getChannel() - 1].set(!ext[i.getChannel() - 1].get());
    }

    /**
     * Toggles the portrait display of the instrument index.
     *
     * @param i
     *            The index at which we want to modify.
     * @param b
     *            If we want note extensions, the box will be of a different
     *            color.
     */
    private void changePortrait(int i, boolean b) {
        if (!b) {
            buttons.get(i).setImage(
                    il.getSpriteFX(ImageIndex.valueOf(instrumentLineImages
                            .get(i) + "_SM")));
        } else {
            buttons.get(i).setImage(
                    il.getSpriteFX(ImageIndex.valueOf(
                            instrumentLineImages.get(i) + "_SM").alt()));
        }
    }

    /**
     * @param n
     *            The Note that all of the InstrumentLine instruments will play
     *            when clicked on.
     */
    public void setDefaultNote(Note n) {
        DEFAULT_NOTE = n.getKeyNum();
    }
    
    private void setupViewUpdater() {
		for (int i = 0; i < Values.NUMINSTRUMENTS; i++) {
			final int index = i;
			this.sequenceReattacher.setNewNoteExtensionListener(index, new ChangeListener<Boolean>() {

				@Override
				public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
					changePortrait(index, newValue.booleanValue());
				}
			});
		}
		this.sequenceReattacher.setOnReattachListener(new ChangeListener<StaffSequence>() {

			@Override
			public void changed(ObservableValue<? extends StaffSequence> observable, StaffSequence oldValue,
					StaffSequence newValue) {
				BooleanProperty[] ext = newValue.getNoteExtensions();
				for (int i = 0; i < Values.NUMINSTRUMENTS; i++) {
					changePortrait(i, ext[i].get());
				}
			}
		});
    }
}<file_sep>package smp.presenters.buttons;

import java.io.File;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import smp.models.staff.StaffArrangement;
import smp.models.staff.StaffSequence;
import smp.models.stateMachine.ProgramState;
import smp.models.stateMachine.Settings;
import smp.models.stateMachine.StateMachine;
import smp.models.stateMachine.Variables;
import smp.presenters.api.button.ImagePushButton;

/**
 * This is a button that moves a song on an arrangement.
 *
 * @author RehdBlob
 * @since 2014.07.27
 */
public class DownButtonPresenter extends ImagePushButton {

	//TODO: auto-add these model comments
	//====Models====
	private IntegerProperty arrangementListSelectedIndex;
	private BooleanProperty arrModified;
	private ObjectProperty<StaffArrangement> theArrangement;
	private ObjectProperty<ProgramState> programState;
	
    /** The amount to move a song up or down. */
    private int moveAmt = 0;

    /**
     * Default constructor.
     *
     * @param downButton
     *            The <code>ImageView</code> object that we are going to make
     *            into a button.
     * @param ct
     *            The FXML controller object.
     * @param im
     *            The Image loader object.
     */
    public DownButtonPresenter(ImageView downButton) {
        super(downButton);
        moveAmt = -1;
        
        this.arrangementListSelectedIndex = Variables.arrangementListSelectedIndex;
        this.arrModified = StateMachine.getArrModified();
        this.theArrangement = Variables.theArrangement;
        this.programState = StateMachine.getState();
        setupViewUpdater();
    }

    @Override
    protected void reactPressed(MouseEvent event) {
        ProgramState curr = StateMachine.getState().get();
        if (curr != ProgramState.ARR_PLAYING) {
            if ((Settings.debug & 0b100000) != 0)
                System.out.println("Move song " + moveAmt);
            ObservableList<String> l = this.theArrangement.get().getTheSequenceNames();
            int x = this.arrangementListSelectedIndex.get();
            if (x != -1) {
            	this.arrModified.set(true);
                Object[] o = this.theArrangement.get().remove(x);
                String s = l.remove(x);
                StaffSequence ss = (StaffSequence) o[0];
                File f = (File) o[1];
                int moveTo = x - moveAmt;
                if (moveTo > l.size())
                    moveTo = l.size();
                if (moveTo < 0)
                    moveTo = 0;
                l.add(moveTo, s);
				this.theArrangement.get().add(moveTo, ss, f);
				this.arrangementListSelectedIndex.set(moveTo);
            }
        }
    }

    @Override
    protected void reactReleased(MouseEvent event) {

    }

    private void setupViewUpdater() {
		theImage.setVisible(false);
    	this.programState.addListener(new ChangeListener<Object>() {

			@Override
			public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
				if (newValue.equals(ProgramState.EDITING))
					theImage.setVisible(false);
				else if (newValue.equals(ProgramState.ARR_EDITING))
					theImage.setVisible(true);
			}
		});
	}
}
<file_sep>package smp.models.stateMachine;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * A loadable settings file that determines whatever is supposed
 * to display eventually on the screen.
 * @author RehdBlob
 * @since 2012.08.28
 */
public class Settings {

    /** The current version number of this program. */
    public final static String version = "v1.1.2";

    /** The name of the file that we write to. */
    private final static String settingsFile = "settings.data";

    /** Change this to true for old functionality of serializing objects. */
    public final static boolean SAVE_OBJECTS = false;

    /**
     * Classic debug on/off. Level 1.
     * 0b10000 - Print staff value every time it changes.
     * Set to -1 for max debug.
     */
    public static int debug = 0;

    /**
     * If Advanced mode is unlocked, this is set to <b>true</b>
     */
    private static boolean advModeUnlocked = false;


    /**
     * Limit the number of notes per line to 5.
     */
    public static boolean LIM_NOTESPERLINE = true;

    /**
     * Limit the number of measures to 96.
     */
    public static boolean LIM_96_MEASURES = true;

    /**
     * Limit the number of volume control lines to
     * 1 per line.
     */
    public static boolean LIM_VOLUME_LINE = true;

    /**
     * Enable or disable the Low A note as a usable
     * note. Default is disabled.
     */
    public static boolean LIM_LOWA = true;

    /**
     * Enable or disable the High D note as a usable
     * note. Default is disabled.
     */
    public static boolean LIM_HIGHD = true;

    /**
     * Turn on the Low A Glitch or not.
     */
    public static boolean LOW_A_ON = true;

    /**
     * Cause fun things to happen when negative tempo is
     * entered. Otherwise, the tempo will be set to ludicrous
     * speeds if this is not enabled.
     */
    public static boolean NEG_TEMPO_FUN = false;

    /**
     * Turn tempo gaps on or off. Most likely people won't want this
     * and maybe it'll be removed.
     */
    public static boolean LIM_TEMPO_GAPS = false;

    /**
     * Be able to resize the window to get to the Secret Button.
     */
    public static boolean RESIZE_WIN = false;

    /**
     * Advanced mode. All of these options don't matter when this is pressed
     * because a completely different interface is loaded when this is set to
     * true...
     */
    public static boolean ADV_MODE = false;

    /**
     * Sets whether we want to see debug mode or not.
     * @param b Debug level.
     */
    public static void setVerbose(int b) {
        debug = b;
    }

    /** Used to save settings via object serialization. */
    private static class SettingsSaver implements Serializable {

        /**
         * Generated Serial ID.
         */
        private static final long serialVersionUID = -3844082395768911493L;

        /** The number of setting types that we have. */
        private transient final int NUM_SETTINGS = 11;

        /** The array of settings that we are going to initialize. */
        public boolean[] set = new boolean[NUM_SETTINGS];

        /** Initializes all of the fields in here. */
        public SettingsSaver() {
            set[0] = advModeUnlocked;
            set[1] = LIM_NOTESPERLINE;
            set[2] = LIM_96_MEASURES;
            set[3] = LIM_VOLUME_LINE;
            set[4] = LIM_LOWA;
            set[5] = LIM_HIGHD;
            set[6] = LOW_A_ON;
            set[7] = NEG_TEMPO_FUN;
            set[8] = LIM_TEMPO_GAPS;
            set[9] = RESIZE_WIN;
            set[10] = ADV_MODE;
        }

    }

    /**
     * Saves the settings of the program.
     * @throws IOException If we can't write the object.
     */
    public static void save() throws IOException {
        FileOutputStream f_out = new
                FileOutputStream(settingsFile);
        ObjectOutputStream o_out = new
                ObjectOutputStream(f_out);
        SettingsSaver s = new SettingsSaver();
        o_out.writeObject(s);
        o_out.close();
        f_out.close();
    }

    /**
     * Unserializes this Settings file.
     * @throws IOException If for some reason we can't load
     * the settings.
     * @throws ClassNotFoundException If for some reason we can't find
     * the settings file file.
     */
    public static void load()
            throws IOException, ClassNotFoundException {
        FileInputStream f_in = new
                FileInputStream (settingsFile);
        ObjectInputStream o_in = new
                ObjectInputStream(f_in);
        SettingsSaver loaded = (SettingsSaver) o_in.readObject();
        advModeUnlocked  = loaded.set[0];
        LIM_NOTESPERLINE = loaded.set[1];
        LIM_96_MEASURES  = loaded.set[2];
        LIM_VOLUME_LINE  = loaded.set[3];
        LIM_LOWA         = loaded.set[4];
        LIM_HIGHD        = loaded.set[5];
        LOW_A_ON         = loaded.set[6];
        NEG_TEMPO_FUN    = loaded.set[7];
        LIM_TEMPO_GAPS   = loaded.set[8];
        RESIZE_WIN       = loaded.set[9];
        ADV_MODE         = loaded.set[10];
        o_in.close();
        f_in.close();
    }

    /**
     * Sets the debug switch on or off.
     * @param b Debug level.
     */
    public static void setDebug(int b) {
        debug = b;
    }


}

<file_sep>package smp.models.stateMachine;

import java.io.File;
import java.util.HashSet;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
import javafx.scene.input.KeyCode;
import smp.components.Values;
import smp.models.staff.StaffSequence;

/**
 * This is the state machine that keeps track of what state the main window is
 * in. This class keeps track of a bunch of variables that the program generally
 * uses.
 *
 * @author RehdBlob
 * @since 2012.08.07
 */
public class StateMachine {

    /** This tells us whether we have modified the song or not. */
    private static BooleanProperty modifiedSong = new SimpleBooleanProperty(false);

    /** This tells us whether we have modified the arrangement or not. */
    private static BooleanProperty modifiedArr = new SimpleBooleanProperty(false);

    /** This keeps track of whether we have pressed the loop button or not. */
    private static BooleanProperty loopPressed = new SimpleBooleanProperty(false);

	/** This keeps track of whether we have pressed the mute button or not. */
	private static BooleanProperty mutePressed = new SimpleBooleanProperty(false);

    /**
     * This keeps track of whether we have pressed the low A mute button or not.
     */
    private static BooleanProperty muteAPressed = new SimpleBooleanProperty(false);

	/**
	 * This keeps track of whether we have pressed the clipboard button or not.
	 */
	private static BooleanProperty clipboardPressed = new SimpleBooleanProperty(false);

    /**
     * The file directory that we are currently located in. We'll start in the
     * user directory.
     */
    private static ObjectProperty<File> currentDirectory = new SimpleObjectProperty<>(new File(System.getProperty("user.dir")));

	/** Set of currently-pressed buttons. */
	private static ObservableSet<KeyCode> buttonsPressed = FXCollections
			.synchronizedObservableSet(FXCollections.observableSet(new HashSet<KeyCode>()));

	/**
	 * The default state that the program is in is the EDITING state, in which
	 * notes are being placed on the staff.
	 */
    private static ObjectProperty<ProgramState> currentState = new SimpleObjectProperty<>(ProgramState.EDITING);

    /**
     * The default time signature that we start out with is 4/4 time.
     */
    private static ObjectProperty<TimeSignature> currentTimeSignature = new SimpleObjectProperty<>(TimeSignature.FOUR_FOUR);
    
    public static class CurrentLineProperty extends SimpleDoubleProperty {
    	//TODO: auto-add these model comments
    	//====Models====
    	private ObjectProperty<StaffSequence> theSequence;
		public CurrentLineProperty(double initialValue) {
    		super(initialValue);
    		this.theSequence = Variables.theSequence;
		}
		@Override
    	public void set(double value) {
			value = value > 0 ? value : 0;
			int max = this.theSequence.get().getTheLinesSize().get() - Values.NOTELINES_IN_THE_WINDOW;
			value = value < max ? value : max;
            super.set(value);
    	}
    }
    /**
     * The current measure line number that the program is on. Typically a
     * number between 0 and 383. This is zero by default.
     */
    private static DoubleProperty currentLine = new CurrentLineProperty(0);

	/**
	 * The current soundset name. This should change when a new soundfont is
	 * loaded.
	 */
	private static StringProperty currentSoundset = new SimpleStringProperty(Values.DEFAULT_SOUNDFONT);

    /**
     * Do not make an instance of this class! The implementation is such that
     * several classes may check the overall state of the program, so there
     * should only ever be just the class and its static variables and methods
     * around.
     *
     * @deprecated
     */
    private StateMachine() {
    }

    /**
     * Get the current <code>State</code> of the <code>StateMachine</code>
     *
     * @return The current <code>State</code>.
     */
    public static ObjectProperty<ProgramState> getState() {
        return currentState;
    }

    /**
     * @param s
     *            Set the <code>StateMachine</code> to a certain State.
     */
    public static void setState(ProgramState s) {
        currentState.set(s);
    }

    /**
     * Sets the state back to "Editing" by default.
     */
    public static void resetState() {
        currentState.set(ProgramState.EDITING);
    }

    /**
     * @return The current time signature that we are running at.
     */
    public static ObjectProperty<TimeSignature> getTimeSignature() {
        return currentTimeSignature;
    }

    /**
     * Sets the time signature to whatever that we give this method.
     *
     * @param t
     *            The new time signature.
     */
    public static void setTimeSignature(TimeSignature t) {
        currentTimeSignature.set(t);
    }

    /** Sets the time signature back to "4/4" by default. */
    public static void resetTimeSignature() {
        currentTimeSignature.set(TimeSignature.FOUR_FOUR);
    }

    /**
     * Gets the current line number that we're on. Typically a value between 0
     * and 383 for most files unless you've done fun stuff and removed the
     * 96-measure limit.
     *
     * @return The current line number (left justify)
     */
    public static DoubleProperty getMeasureLineNum() {
        return currentLine;
    }

    /**
     * Sets the current line number to whatever is given to this method.
     *
     * @param num
     *            The number that we're trying to set our current line number
     *            to.
     */
    public static void setMeasureLineNum(int num) {
        currentLine.set(num);
    }
    
    /** Sets that the song is now loop-enabled. */
    public static void setLoopPressed(boolean b) {
        loopPressed.set(b);
    }

    /** @return Whether the loop button is pressed or not. */
    public static BooleanProperty getLoopPressed() {
        return loopPressed;
    }

    /** Sets the fact that mute notes are now enabled. */
	public static void setMutePressed(boolean b) {
		mutePressed.set(b);
	}

    /** @return Whether the mute button is pressed or not. */
    public static BooleanProperty getMutePressed() {
        return mutePressed;
    }

    /**
     * Sets the modified flag to true or false.
     *
     * @param b
     *            Whether we have modified a song or not.
     */
    public static void setSongModified(boolean b) {
        modifiedSong.set(b);
    }

    /**
     * @return Whether we have modified the current song or not.
     */
    public static BooleanProperty getSongModified() {
        return modifiedSong;
    }

    /**
     * @param b
     *            Whether we have modified an arrangement or not.
     */
    public static void setArrModified(boolean b) {
        modifiedArr.set(b);
    }

    /**
     * @return Whether we have modified the current arrangement or not.
     */
    public static BooleanProperty getArrModified() {
        return modifiedArr;
    }

    /**
     * @param b
     *            Whether we have pressed the low A mute button.
     */
    public static void setMuteAPressed(boolean b) {
        muteAPressed.set(b);
    }

    /**
     * @return Whether our mute-all button is pressed or not.
     */
    public static BooleanProperty getMuteAPressed() {
        return muteAPressed;
    }
    
    /**
     * @since 08.2017
     */
    public static void setClipboardPressed(boolean b) {
    	clipboardPressed.set(b);
    }
    public static BooleanProperty getClipboardPressed() {
    	return clipboardPressed;
    }
    
    /**
     * @return Set of currently-pressed buttons.
     */
    public static ObservableSet<KeyCode> getButtonsPressed() {
        return buttonsPressed;
    }

    /**
     * Clears the set of key presses in this program.
     */
    public static void clearKeyPresses() {
        buttonsPressed.clear();
    }

    /** @return Last directory we accessed. */
    public static ObjectProperty<File> getCurrentDirectory() {
        return currentDirectory;
    }

    /** @param cDir Set current directory to this. */
    public static void setCurrentDirectory(File cDir) {
        StateMachine.currentDirectory.set(cDir);
    }

	/**
	 * @return The current soundset name.
	 * @since v1.1.2
	 */
	public static StringProperty getCurrentSoundset() {
		return currentSoundset;
	}

	/**
	 * @param soundset
	 *            Set current soundset to this.
	 * @since v1.1.2
	 */
	public static void setCurrentSoundset(String soundset) {
		StateMachine.currentSoundset.set(soundset);
	}
}
<file_sep>Super Mario Paint
==========
* Current Version: 1.4.2

Help would be very much appreciated! If you manage to debug something, submit a pull request and I'll review it and (hopefully) merge it in.

Based off of the original Mario Paint on the SNES from 1992, MarioSequencer (2002), TrioSequencer, Mario Paint Composer 1.0 / 2.0 (<NAME>, 2007-2008), and FordPrefect86's Advanced Mario Sequencer (2009). This will be a free program when completed, open to those who want to download it.  


Accepting cryptocurrency donations!  
* Bitcoin (BTC): 1TyycsRVPUMgW1QDgafNSUpB2emh3p4TL
* Litecoin (LTC): LX35qPaA7NAdWNjUnWcugZqd8zbNkMvpia


Major Releases:
-----
See "Version History.txt" for semantic versioning and more release notes.
* June 7, 2020 - v1.4.1 release with some backend upgrades carried over from v1.3.0
* June 2, 2020 - v1.4.0 release 'Giant Mario Paint', second major functionality upgrade
* March 2019 - Addition of team members Seymour & CyanSMP64
* March 9, 2019 - v1.3.0 release
* March 4, 2019 - v1.2.1 release
* February 4, 2018 - v1.1.1 release
* January 29, 2018 - First relatively major functionality upgrade, v1.1.0
* June 2017 - Addition of team member j574y923 
* March 14, 2016 - First full functionality release, v1.0.0
* January 5, 2015 - v0.95~0.951 - Beta Release 
* January 1-3, 2015 - v0.94~0.942 - Pre-Beta (Arranger) 
* December 31, 2014 - v0.921 - Alpha (Mac + Windows)
* December 30, 2014 - v0.92 - Alpha (Stability Release + Java 7u71 / Java 8u25)
* January 4, 2014 - v0.91 - Alpha (Update Release)
* December 25, 2013 - v0.90 - Initial Alpha Release
* August 7, 2012 - Project started

Confirmed Operating System Configurations:
* Windows 7 + Java 8u25
* Windows 7 + Java 8u73
* Windows 8.1 + Java 8u25
* Windows 10 + Java 8u73
* Windows 10 + Java 8u161
* Windows 10 + Java 8u251
* Mac OSX 10.9.5 + Java 8u25
* Ubuntu 14.04-18.04 + Java 8

Deprecated Operating System Configurations:
-----
Java 7 is no longer supported as of v1.1.0
* Windows 7 + Java 7u71
* Windows Vista + Java 7u71
* Mac OSX 10.9.5 + Java 7u71


Contributors to Operating Systems / Tests
-----
* SomeonePlaymc
* VolcanBrimstone
* Pokesonicddrninja
* Adolfobaez
* Cakewut
* SupraTheHedgehog
* The Mario Paint Community
* Many others...

Tasklist:
-----
* Build user's guide... probably on this README or bundled with releases
* Build contributor's guide... probably also on this README
* Import AMS songs
* Speedmarks
* Bookmarks
* SoundFont creator
* Fix audio desync error when returning to program after sleep
* Mario Paint Recorder?
* Update for JDK14? (optional)
* Merge backend between Super Mario Paint & Giant Mario Paint (done - June 2, 2020)
* Splash screen animation and other extras (done - May 30, 2020)
* More aesthetic buttons and button layout (done - March 4, 2019)
* Hotkey for mute note/instrument and others (done - March 4, 2019)
* More advanced Options dialog (done - January 29, 2018) 
* Instrument replacing option (done - January 29, 2018)
* Undo/redo (done - January 19, 2018)
* Release SMP v1.0.0 (done - March 14, 2016)
* Aesthetic program edits, stability checks, arranger file stability check (done - March 13, 2016)
* Solve some song playing thread stability issues (done - February 28, 2016)
* Human-readable arranger files (done - February 27, 2016)
* Human-readable save files (done - May 27, 2015)
* Release to a test group (v0.95 - **Beta Release** - January 5, 2015)
* Staff ledger lines (done - January 4, 2015)
* Push new version of StaffSequence with note extensions - Maintain backwards compatibility (done - January 4, 2015)
* Fix measure addition and subtraction (done - January 4, 2015)
* Import MPC arrangements (done - January 4, 2015)
* Arrangement files use relative paths (uses the ol' "Prefs" folder) (done - January 4, 2015)
* Import MPC songs (done - January 3, 2015)
* Arranger mode (done - January 1, 2015)
* Splash screen is actually a splash screen (done - December 31, 2014)
* Test run on more operating systems (done - December 31, 2014)
* Tempo changing option (done - February 14, 2014)
* Rudimentary Options dialog (done - January 19, 2014)
* End-of-File Behaviour fix (done - January 3, 2014)
* Remove limit on song length (done - January 1, 2014)
* Tempo selector interface (done - December 27, 2013)
* Measure line numbers (done - December 27, 2013)
* Release to a test group (v0.9 - **Alpha Release** - December 25, 2013)
* Loop button loops the song (done - December 18, 2013)
* Save & Load songs (done - December 18, 2013)
* Volume bars implemented (done - December 17, 2013)
* Release to a small test group (v0.70-0.73 - obsolete)
* Play back notes on the staff reliably (done - September 28, 2013)
* Play and stop buttons start and stop the song (done - September 28, 2013)
* Play, stop, and loop buttons are able to be pressed (done - August 29, 2013)
* Play, stop, and loop buttons appear (done - August 29, 2013)
* Navigate the staff (done - August 23, 2013)
* Staff navigation framework (done - August 23, 2013)
* Images display normally on the staff (done - August 13, 2013)
* Instruments stack on the staff (done - August 6, 2013)
* Flats and sharps implemented on the staff (done - August 4, 2013)
* Hear notes played on the staff (done - July 26, 2013)
* Place notes on the staff (done - July 26, 2013)
* Staff notes images framework (done - May 31, 2013)
* Settings framework (done - October 31, 2012)
* Latency fix (done - September 6, 2012)
* Sprites rework (done - August 30, 2012)
* User interface draft II (done - August 22, 2012)
* Sounds play (done - August 22, 2012)
* User interface draft (done - August 17, 2012)
* JavaFX migration (done - August 17, 2012)
* Window appears (done - August 12, 2012)
<file_sep>package smp.components.buttons;

import javafx.event.EventHandler;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import smp.ImageIndex;
import smp.ImageLoader;
import smp.components.general.ImageToggleButton;
import smp.fx.SMPFXController;
import smp.stateMachine.StateMachine;

/**
 * This is a button that allows us to mute all of one type of instrument, much
 * like the low A glitch note in MPC.
 * 
 * @author RehdBlob
 * @since 2013.12.24
 */
public class MuteInstButton extends ImageToggleButton {

    /** The mute button that is linked to this button. */
    private MuteButton mt;

    /**
     * This creates a new MuteButton object.
     * 
     * @param i
     *            This <code>ImageView</code> object that you are trying to link
     *            this button with.
     * @param ct
     *            The FXML controller object.
     * @param im
     *            The Image loader object.
     */
    public MuteInstButton(ImageView i, SMPFXController ct, ImageLoader im) {
        super(i, ct, im);
        getImages(ImageIndex.MUTE_A_PRESSED, ImageIndex.MUTE_A_RELEASED);
        releaseImage();
        isPressed = false;
        
		// TODO: create getMuteInstButton() somewhere so adding a hotkey can be done elsewhere
        // @since v1.1.2 per request of seymour schlong
		ct.getBasePane().addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {

			@Override
			public void handle(KeyEvent event) {
				if (controller.getNameTextField().focusedProperty().get()) return; // Disable while textfield is focused
				if(event.getCode() == KeyCode.M)
					reactPressed(null);
			}
		});
    }

    @Override
    protected void reactClicked(MouseEvent event) {
        // Do nothing

    }

    @Override
    public void reactPressed(MouseEvent event) {
        if (isPressed) {
            isPressed = false;
            releaseImage();
            StateMachine.setMuteAPressed(false);
        } else {
            if (mt.isPressed())
                mt.reactPressed(null);
            isPressed = true;
            pressImage();
            StateMachine.setMuteAPressed(true);
        }

    }

    /**
     * @param im
     *            The mute button that we want to set.
     */
    public void setMuteButton(MuteButton im) {
        mt = im;
    }

    /** @return The mute button that this muteA button is linked to. */
    public ImageToggleButton getMuteButton() {
        return mt;
    }

}
<file_sep>package smp.presenters.buttons;

import javafx.beans.property.BooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import smp.ImageIndex;
import smp.models.stateMachine.StateMachine;
import smp.presenters.api.button.ImageToggleButton;

/**
 * This is the button that, when pressed, toggles whether you are placing a mute
 * note or not.
 * 
 * @author RehdBlob
 * @since 2013.11.10
 *
 */
public class MutePresenter extends ImageToggleButton {

	//TODO: auto-add these model comments
	//====Models====
	private BooleanProperty mutePressed;
	private BooleanProperty muteAPressed;

    /**
     * This creates a new MuteButton object.
     * 
     * @param mute
     *            This <code>ImageView</code> object that you are trying to link
     *            this button with.
     * @param ct
     *            The FXML controller object.
     * @param im
     *            The Image loader object.
     */
    public MutePresenter(ImageView mute) {
        super(mute);
        getImages(ImageIndex.MUTE_PRESSED, ImageIndex.MUTE_RELEASED);
        releaseImage();
        isPressed = false;
        
        //TODO: add hotkeys in standalone hotkeys class
		// TODO: create getMuteButton() somewhere so adding a hotkey can be done elsewhere
        // @since v1.1.2 per request of seymour schlong
//		ct.getBasePane().addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
//
//			@Override
//			public void handle(KeyEvent event) {
//				/** alt+n is for deselecting notes @see <code>StaffRubberBandEventHandler</code> */
//				if(event.isAltDown())
//					return;
//				if(event.getCode() == KeyCode.N)
//					reactPressed(null);
//			}
//		});
        
        this.mutePressed = StateMachine.getMutePressed();
        this.muteAPressed = StateMachine.getMuteAPressed();
        setupViewUpdater();
    }

    private void setupViewUpdater() {
		muteAPressed.addListener(new ChangeListener<Boolean>() {

			@Override
			public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
				if (newValue.equals(true)) {
		            isPressed = false;
		            releaseImage();
		            mutePressed.set(false);
				}
			}
		});
	}

	@Override
    protected void reactClicked(MouseEvent event) {
        // Do nothing

    }

    @Override
    public void reactPressed(MouseEvent event) {
        if (isPressed) {
            isPressed = false;
            releaseImage();
            this.mutePressed.set(false);
        } else {
            isPressed = true;
            pressImage();
            this.mutePressed.set(true);
        }
    }
}
<file_sep>package smp.components.staff.sequences;
import smp.stateMachine.Settings;

/**
 * An event on the Super Mario Paint staff.
 * @author RehdBlob
 * @since 2012.09.24
 */
public abstract class AbstractStaffEvent implements StaffEvent {

    /**
     * Generated serial ID.
     */
    private static final long serialVersionUID = -6303076919558802033L;

    /** The line number that this staff event occurs at. */
    protected int lineNum;

    /**
     * The integer representation of the measure that this staff
     * event occurs at.
     */
    protected int measureNum;

    /**
     * Denotes the location of this event within a measure.
     */
    protected int measureLineNum;

    /**
     * Sets up an event at measure <code>num</code>.
     * @param num The measure number that we're starting at.
     */
    public AbstractStaffEvent(int num) {
        lineNum = num;
        setMeasureNum(num);
        setMeasureLineNum(num);
    }

    /**
     * Sets the measure number of this staff event given
     * an integer that denotes the line number that this
     * event occurs at.
     * @param num An integer representation that denotes
     * the location of the event.
     */
    protected void setMeasureNum(int num) {
        if (isMeasureNumValid(num))
            measureNum = num;
    }

    /**
     * Tells us whether this measure number is valid.
     * @param num The measure number we want to check.
     * @return If the measure number is greater than or equal to 0. If the
     * Settings feature for limiting to 96 measures is on, then we check for
     * that too.
     */
    protected static boolean isMeasureNumValid(int num) {
        return (!Settings.LIM_96_MEASURES && num >= 0) || (num >= 0 && num <= 96);
    }

    /**
     * Sets the measure line number of this staff event
     * given an integer that denotes the line number
     * that this event occurs at.
     * @param num An integer representation that denotes the
     * location of the event.
     */
    protected void setMeasureLineNum(int num) {
        ;
    }

    @Override
    public int getLineNum() {
        return lineNum;
    }

    @Override
    public int getMeasureNum() {
        return measureNum;
    }

    @Override
    public int getMeasureLineNum() {
        return measureLineNum;
    }


}
<file_sep>package smp.presenters.api.clipboard;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javafx.scene.effect.Blend;
import javafx.scene.effect.BlendMode;
import javafx.scene.effect.ColorInput;
import smp.ImageLoader;
import smp.commandmanager.ModifySongManager;
import smp.commandmanager.commands.AddNoteCommand;
import smp.commandmanager.commands.AddVolumeCommand;
import smp.commandmanager.commands.RemoveNoteCommand;
import smp.commandmanager.commands.RemoveVolumeCommand;
import smp.components.InstrumentIndex;
import smp.components.Values;
import smp.components.staff.Staff;
import smp.components.staff.StaffVolumeEventHandler;
import smp.components.staff.sequences.StaffNote;
import smp.components.staff.sequences.StaffNoteLine;
import smp.stateMachine.StateMachine;

/**
 * The API will contain functions for <code>StaffClipboard</code>. These include
 * copy, cut, delete, insert, move, paste.
 * 
 * Since v1.1.2, the API will also contain <code>StaffRubberBand</code>
 * functions such as resizing.
 *
 * @author j574y923
 */
public class StaffClipboardAPI {

	private Staff theStaff;
	private StaffClipboard theStaffClipboard;
	private ImageLoader il;
	private ModifySongManager commandManager;
	
	/* Corresponds with the first selection line */
	private int selectionLineBegin = Integer.MAX_VALUE;
	
	private Blend highlightBlend;

	/*
	 * If false, the selected notes remain in selection but are unhighlighted
	 * and will not be copied
	 */
	private boolean selectNotesFlag = true;
	/*
	 * If false, the selected volumes remain in selection but are unhighlighted
	 * and will not be copied but there WILL still be volume data.
	 * ignoreVolumesFlag will toggle on telling not to paste those volumes in
	 * copy().
	 */
	private boolean selectVolumesFlag = true;
	/*
	 * Because there is always volume data, this flag will indicate when to
	 * ignore pasting those volumes. If !selectVolumesFlag then toggle on when
	 * copying and toggle off when clearing copiedData.
	 */
	private boolean ignoreVolumesFlag = false;
	
	public StaffClipboardAPI(StaffClipboard sc, Staff st, ImageLoader i, ModifySongManager cm) {
		theStaffClipboard = sc;
		theStaff = st;
		il = i;
		commandManager = cm;
		
		highlightBlend = new Blend(
	            BlendMode.SRC_OVER,
	            null,
	            new ColorInput(
	                    0,
	                    0,
	                    il.getSpriteFX(InstrumentIndex.BOAT.imageIndex()).getWidth(),
	                    il.getSpriteFX(InstrumentIndex.BOAT.imageIndex()).getHeight(),
	                    StaffClipboard.HIGHLIGHT_FILL
	            )
	    );
	}

	/**
	 * Get all notes from selection and copy them into data. lines are relative
	 * to selectionLineBegin. they are not absolute.
	 * 
	 * also copy volumes.
	 */
	public void copy() {
		//if there's something new selected, make way for new data
		//else just use old data
		if(!theStaffClipboard.getSelection().isEmpty() && (selectNotesFlag || selectVolumesFlag))
			clearCopiedData();

		for (Map.Entry<Integer, StaffNoteLine> noteLine : theStaffClipboard.getSelection().entrySet()) {
			int line = noteLine.getKey();
			ArrayList<StaffNote> ntList = noteLine.getValue().getNotes();
			if (selectNotesFlag)
				for(StaffNote note : ntList) 
					//relative index
					copyNote(line - selectionLineBegin, note);
			
			if (selectVolumesFlag)
				copyVolume(line - selectionLineBegin, noteLine.getValue().getVolume());
		}
		
		if (!selectVolumesFlag)
			ignoreVolumesFlag = true;
	}

    /**
     * Copy selected notes and volumes and delete selected notes.
     */
    public void cut() {
        copy();
        delete();
    }

    /**
     * Delete selected notes (only notes, not volume).
     */
    public void delete() {

		for (Map.Entry<Integer, StaffNoteLine> noteLine : theStaffClipboard.getSelection().entrySet()) {
			int line = noteLine.getKey();
			ArrayList<StaffNote> ntList = noteLine.getValue().getNotes();
			
			StaffNoteLine lineDest = theStaff.getSequence().getLine(line);
			
			for(StaffNote note : ntList){
				lineDest.remove(note);
	            StateMachine.setSongModified(true);
	            commandManager.execute(new RemoveNoteCommand(lineDest, note));

				if (lineDest.isEmpty() && 0 <= line - StateMachine.getMeasureLineNum()
						&& line - StateMachine.getMeasureLineNum() < Values.NOTELINES_IN_THE_WINDOW) {
					StaffVolumeEventHandler sveh = theStaff.getNoteMatrix()
							.getVolHandler(line - StateMachine.getMeasureLineNum());
					sveh.setVolumeVisible(false);
					commandManager.execute(new RemoveVolumeCommand(lineDest, lineDest.getVolume()));
				}
			}
			// idk why but redraw needs to be called every line or else weird
			// stuff happens (like some notes don't get added)
			theStaff.redraw();
		}
		
		clearSelection();
        commandManager.record();
    }

	/**
	 * Paste copied notes and volumes.
	 * 
	 * @param lineMoveTo starting line to paste data at
	 */
	public void paste(int lineMoveTo) {

		HashMap<Integer, StaffNoteLine> copiedData = theStaffClipboard.getCopiedData();
		
		for (Map.Entry<Integer, StaffNoteLine> lineCopy : copiedData.entrySet()) {
			int line = lineMoveTo + lineCopy.getKey();
			
			StaffNoteLine lineDest = theStaff.getSequence().getLine(line);
			StaffNoteLine lineSrc = lineCopy.getValue();
			for(StaffNote note : lineSrc.getNotes()) {
				
				// see StaffInstrumentEventHandler's placeNote function
				StaffNote theStaffNote = new StaffNote(note.getInstrument(), note.getPosition(), note.getAccidental());
				theStaffNote.setMuteNote(note.muteNoteVal());
		        if (theStaffNote.muteNoteVal() == 0) {
		            theStaffNote.setImage(il.getSpriteFX(note.getInstrument().imageIndex()));
		        } else if (theStaffNote.muteNoteVal() == 1) {
		            theStaffNote.setImage(il.getSpriteFX(note.getInstrument().imageIndex().alt()));
		        } else if (theStaffNote.muteNoteVal() == 2) {
		            theStaffNote.setImage(il.getSpriteFX(note.getInstrument().imageIndex()
		                    .silhouette()));
		        }

				if (lineDest.isEmpty()) {
					lineDest.setVolumePercent(((double) Values.DEFAULT_VELOCITY) / Values.MAX_VELOCITY);
					
					if (line - StateMachine.getMeasureLineNum() < Values.NOTELINES_IN_THE_WINDOW) {
						StaffVolumeEventHandler sveh = theStaff.getNoteMatrix()
								.getVolHandler(line - StateMachine.getMeasureLineNum());
						sveh.updateVolume();
					}

		            commandManager.execute(new AddVolumeCommand(lineDest, Values.DEFAULT_VELOCITY));
				}

				if (!lineDest.contains(theStaffNote)) {
		        	lineDest.add(theStaffNote);
		            StateMachine.setSongModified(true);
		            commandManager.execute(new AddNoteCommand(lineDest, theStaffNote));
				}
			}
			
			// paste volume
			if(!ignoreVolumesFlag) {
	            commandManager.execute(new RemoveVolumeCommand(lineDest, lineDest.getVolume()));
				lineDest.setVolume(lineSrc.getVolume());
	            commandManager.execute(new AddVolumeCommand(lineDest, lineDest.getVolume()));
				
				if (line - StateMachine.getMeasureLineNum() < Values.NOTELINES_IN_THE_WINDOW) {
					StaffVolumeEventHandler sveh = theStaff.getNoteMatrix()
							.getVolHandler(line - StateMachine.getMeasureLineNum());
					sveh.updateVolume();
				}
	            StateMachine.setSongModified(true);
	            
			}
			
		}

        theStaff.redraw();
        commandManager.record();
	}

	/**
	 * get all notes in the line and position bounds that are filtered and put
	 * them into the selection map
	 * 
	 * @param lineBegin
	 * @param positionBegin
	 *            (<= positionEnd, i.e. positionBegin could be lower notes)
	 * @param lineEnd
	 * @param positionEnd
	 *            (>= positionBegin, i.e. positionEnd could be higher notes)
	 */
	public void select(int lineBegin, int positionBegin, int lineEnd, int positionEnd) {
		StaffClipboardFilter instFilter = theStaffClipboard.getInstrumentFilter();

		for (int line = lineBegin; line <= lineEnd; line++) {
			StaffNoteLine lineSrc = theStaff.getSequence().getLine(line);

			ArrayList<StaffNote> ntList = lineSrc.getNotes();
			for (StaffNote note : ntList) {
				if (positionBegin <= note.getPosition() && note.getPosition() <= positionEnd
						&& instFilter.isFiltered(note.getInstrument())) {
					// store the copied note at the relative line
					selectNote(line, note);
					// store the staffnoteline's volume at the relative line
					selectVolume(line, lineSrc.getVolume());
					updateSelectionLineBegin(line);
				}
			}
		}
    }
	
	public void clearCopiedData() {
		theStaffClipboard.getCopiedData().clear();
		ignoreVolumesFlag = false;
	}

	/**
	 * Unhighlight all notes and volumes and clear the selection map.
	 */
	public void clearSelection() {
		//unhighlight notes
		HashMap<Integer, StaffNoteLine> selection = theStaffClipboard.getSelection();
		for(StaffNoteLine line : selection.values()) 
			for(StaffNote note : line.getNotes())
				highlightNote(note, false);

		//unhighlight volumes
		theStaffClipboard.getHighlightedVolumes().clear();
		theStaffClipboard.getHighlightedVolumesRedrawer().changed(null, 0, StateMachine.getMeasureLineNum());

		selection.clear();
		selectionLineBegin = Integer.MAX_VALUE;
		selectNotesFlag = true;
		selectVolumesFlag = true;
	}
	
	/**
	 * set selectionLineBegin to line if line < selectionLineBegin.
	 * selectionLineBegin gets subtracted from copied data lines to get relative
	 * bounds.
	 * 
	 * 
	 * make selection line lower to copy more blank lines before actual content.
	 * 
	 * @param line
	 *            to update selectionLineBegin to
	 */
	public void updateSelectionLineBegin(int line) {
		if (line < selectionLineBegin)
			selectionLineBegin = line;
	}
	
	public int getSelectionLineBegin() {
		return selectionLineBegin;
	}
	
	/**
	 * Copy information from note. Add new note into copiedData.
	 * 
	 * Note: the line information is where the note occurs in copiedData, NOT
	 * where it occurs in the staff.
	 * 
	 * @param line
	 *            where the note will be placed in copiedData
	 * @param note
	 *            info to be copied
	 */
	public void copyNote(int line, StaffNote note) {
		StaffNote newNote = new StaffNote(note.getInstrument(), note.getPosition(), note.getAccidental());
		newNote.setMuteNote(note.muteNoteVal());
		HashMap<Integer, StaffNoteLine> copiedData = theStaffClipboard.getCopiedData();
		if(!copiedData.containsKey(line))
			copiedData.put(line, new StaffNoteLine());
		copiedData.get(line).add(newNote);
	}
	
	/**
	 * Select note. Add existing note into selection. Highlight note.
	 * 
	 * @param line
	 *            where the note occurs
	 * @param note
	 *            that will be placed into selection
	 */
	public void selectNote(int line, StaffNote note) {
		HashMap<Integer, StaffNoteLine> selection = theStaffClipboard.getSelection();
		if(!selection.containsKey(line))
			selection.put(line, new StaffNoteLine());
		selection.get(line).add(note);
		highlightNote(note, true);
	}
	
	public void highlightNote(StaffNote note, boolean highlight) {
		if(highlight)
			note.setEffect(highlightBlend);
		else
			note.setEffect(null);
	}
	
	public void copyVolume(int line, int volume) {
		HashMap<Integer, StaffNoteLine> copiedData = theStaffClipboard.getCopiedData();
		if(!copiedData.containsKey(line))
			copiedData.put(line, new StaffNoteLine());
		copiedData.get(line).setVolume(volume);
	}
	
	public void selectVolume(int line, int volume) {
		HashMap<Integer, StaffNoteLine> selection = theStaffClipboard.getSelection();
		if(!selection.containsKey(line))
			selection.put(line, new StaffNoteLine());
		selection.get(line).setVolume(volume);
		highlightVolume(line, true);
	}

	public void highlightVolume(int line, boolean highlight) {
		if(highlight) 
			theStaffClipboard.getHighlightedVolumes().add(line);
		else 
			theStaffClipboard.getHighlightedVolumes().remove(line);
		
		// trigger the ChangeListener that will set the highlight effect
		if (StateMachine.getMeasureLineNum() <= line
				&& line < StateMachine.getMeasureLineNum() + Values.NOTELINES_IN_THE_WINDOW)
			theStaffClipboard.getHighlightedVolumesRedrawer().changed(null, 0, StateMachine.getMeasureLineNum());
	}
	
	public void selectNotesToggle(boolean selectNotes) {
		selectNotesFlag = selectNotes;
		if(selectNotesFlag) {
			//highlight notes
			for(StaffNoteLine line : theStaffClipboard.getSelection().values()) 
				for(StaffNote note : line.getNotes())
					highlightNote(note, true);
		} else {
			//unhighlight notes
			for(StaffNoteLine line : theStaffClipboard.getSelection().values()) 
				for(StaffNote note : line.getNotes())
					highlightNote(note, false);
		}
	}
	
	public void selectVolumesToggle(boolean selectVolumes) {
		selectVolumesFlag = selectVolumes;
		if(selectVolumesFlag) {
			for(Integer line : theStaffClipboard.getSelection().keySet())
				highlightVolume(line, true);
		} else {
			//unhighlight volumes
			theStaffClipboard.getHighlightedVolumes().clear();
			theStaffClipboard.getHighlightedVolumesRedrawer().changed(null, 0, StateMachine.getMeasureLineNum());
		}
	}
	
	public boolean isSelectNotesOn() {
		return selectNotesFlag;
	}
	
	public boolean isSelectVolumesOn() {
		return selectVolumesFlag;
	}

	/**
	 * Begins the first point for the clipboard's rubberband at the specified x
	 * and y coords. Adds the rubberband to the rubberBandLayer so it actually
	 * shows up.
	 * 
	 * @param x
	 *            the first x coord
	 * @param y
	 *            the first y coord
	 * @since v1.1.2
	 */
	public void beginBand(double x, double y) {
		StaffRubberBand rubberBand = theStaffClipboard.getRubberBand();
    	theStaffClipboard.getRubberBandLayer().getChildren().add(rubberBand);
    	rubberBand.begin(x, y);
	}
	
	/**
	 * Draws and resizes the rubberband from the first point from beginBand() to
	 * the next point specified by x and y. beginBand() should be called before
	 * this.
	 * 
	 * @param x
	 *            the second x coord
	 * @param y
	 *            the second y coord
	 * @since v1.1.2
	 */
	public void resizeBand(double x, double y) {
		theStaffClipboard.getRubberBand().resizeBand(x, y);
	}	

	/**
	 * Ends the band and selects all notes inside the region. Notes selected are
	 * modified by the <code>StaffClipboardFilter</code>. resizeBand() should be
	 * called before this.
	 * 
	 * @since v1.1.2
	 */
	public void endBand() {
		StaffRubberBand rubberBand = theStaffClipboard.getRubberBand();
		rubberBand.end();
		theStaffClipboard.getRubberBandLayer().getChildren().remove(rubberBand);

		int lb = rubberBand.getLineBegin() + StateMachine.getMeasureLineNum();
		int pb = rubberBand.getPositionBegin();
		int le = rubberBand.getLineEnd() + StateMachine.getMeasureLineNum();
		int pe = rubberBand.getPositionEnd();
		select(lb, pb, le, pe);
	}
	
	/**
	 * @return if rubberband is in the rubberbandlayer
	 * @since v1.1.2
	 */
	public boolean isRubberBandActive() {
		return theStaffClipboard.getRubberBandLayer().getChildren().contains(theStaffClipboard.getRubberBand());
	}
}
<file_sep>package smp.components.general;

import java.util.ArrayList;

import smp.ImageLoader;
import smp.fx.SMPFXController;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;

/**
 * Image radio button that toggles between a list
 * of buttons. This class holds a list of references
 * to other ImageRadioButtons that it's linked to.
 * @author RehdBlob
 * @since 2012.08.30
 */
public abstract class ImageRadioButton extends AbstractImageButton {

    /**
     * The list of radio buttons that are linked to this button.
     */
    protected ArrayList<ImageRadioButton> linkedButtons;

    /**
     * @param i The ImageView passed to the Button
     * wrapper.
     * @param controller The FXML controller object.
     */
    public ImageRadioButton(ImageView i, SMPFXController controller, ImageLoader im) {
        super(i, controller, im);
        linkedButtons = new ArrayList<ImageRadioButton>();
    }

    /**
     * Links the ImageRadioButton to another ImageRadioButton
     * such that if it is depressed, the other buttons in the list will
     * be reverted to an unpressed state.
     * @param b The <code>ImageRadioButton</code> to link this
     * <code>ImageRadioButton</code> to.
     */
    public void link(ImageRadioButton b) {
        linkedButtons.add(b);
    }

    /**
     * Sets this button's <code>isPressed</code> to true and also presses
     * the button image down.
     */
    @Override
    protected void reactPressed(MouseEvent e) {
        pressImage();
        isPressed = true;
        for (ImageRadioButton i : linkedButtons) {
            i.releaseImage();
            i.isPressed = false;
        }
    }


}
<file_sep>package smp.presenters.staff;

import java.util.ArrayList;

import javafx.beans.property.DoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.layout.HBox;
import javafx.scene.text.Text;
import smp.components.Values;
import smp.models.stateMachine.StateMachine;

public class StaffMeasureNumbersPresenter {

	// TODO: auto-add these model comments
	// ====Models====
	private DoubleProperty measureLineNumber;

	private HBox staffMeasureNumbers;

	ArrayList<Text> measureNums;

	public StaffMeasureNumbersPresenter(HBox staffMeasureNumbers) {
		this.staffMeasureNumbers = staffMeasureNumbers;

		this.measureLineNumber = StateMachine.getMeasureLineNum();
		initializeStaffMeasureNums(this.staffMeasureNumbers);
		setupViewUpdater();
	}

	/**
	 * These are the numbers above each successive measure.
	 */
	private void initializeStaffMeasureNums(HBox mNums) {
		ArrayList<HBox> measureNumBoxes = new ArrayList<HBox>();
		measureNums = new ArrayList<Text>();
        for (Node num : mNums.getChildren())
            measureNumBoxes.add((HBox) num);
        int counter = 1;
        for (int i = 0; i < measureNumBoxes.size(); i++) {
            HBox theBox = measureNumBoxes.get(i);
            Text t = new Text();
            theBox.getChildren().add(t);
            measureNums.add(t);
            if (i % Values.TIMESIG_BEATS == 0) {
                t.setText(String.valueOf(counter));
                counter++;
            } else
                continue;
        }
    }
    

    /**
	 * @see smp.components.staff.StaffImages.updateStaffMeasureLines(int)
	 */
	public void setupViewUpdater() {
		this.measureLineNumber.addListener(new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				int currLine = newValue.intValue();
		        int counter = 0;
		        for (int i = 0; i < measureNums.size(); i++) {
		            Text currText = measureNums.get(i);
		            if ((currLine + i) % Values.TIMESIG_BEATS == 0) {
		                currText.setText(String.valueOf((int) (Math.ceil(currLine
		                        / (double) Values.TIMESIG_BEATS) + 1 + counter)));
		                counter++;
		            } else {
		                currText.setText("");
		            }
		        }
			}
		});
    }
}
<file_sep>package smp.models.staff;

import java.io.Serializable;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import smp.components.Values;
import smp.models.stateMachine.TimeSignature;

/**
 * We might not even need MIDI to do this sequencing stuff. This class keeps
 * track of whatever is displaying on the staff right now.
 *
 * @author RehdBlob
 * @since 2013.08.23
 */
public class StaffSequence implements Serializable {

    /**
     * Generated serial ID.
     */
    private static final long serialVersionUID = 5752285850525402081L;

    /**
     * The tempo of this sequence.
     */
    private DoubleProperty tempo  = new SimpleDoubleProperty(Values.DEFAULT_TEMPO);

    /** These are all of the lines on the staff. */
    private ObservableList<StaffNoteLine> theLines = FXCollections.observableArrayList();
    
    /** @since v1.1.3*/
    private ReadOnlyIntegerProperty theLinesSize = new SimpleListProperty<>(theLines).sizeProperty();

    /** This tells us which notes are extended (green highlight) or not. */
    private BooleanProperty[] noteExtensions = new BooleanProperty[Values.NUMINSTRUMENTS];

    /** The time signature of this sequence. */
    private ObjectProperty<TimeSignature> t = new SimpleObjectProperty<TimeSignature>(TimeSignature.FOUR_FOUR);
    
    /** The soundset bound to and should be loaded for this sequence. */
    private StringProperty soundsetBinding = new SimpleStringProperty("");

    /** Default constructor. Makes an empty song. */
    public StaffSequence() {
        for (int i = 0; i < Values.DEFAULT_LINES_PER_SONG; i++)
            theLines.add(new StaffNoteLine());
        for (int i = 0; i < Values.NUMINSTRUMENTS; i++)
        	noteExtensions[i] = new SimpleBooleanProperty();
    }

    /**
     * @param i
     *            The index that we want to get some line from.
     * @return Gets a <code>StaffNoteLine</code> that resides at index i.
     */
    public StaffNoteLine getLine(int i) {
        try {
            return theLines.get(i);
        } catch (IndexOutOfBoundsException e) {
            theLines.add(new StaffNoteLine());
            try {
                return theLines.get(i);
            } catch (IndexOutOfBoundsException e2) {
                return getLine(i);
            }
        }
    }

    /**
     * @return The entire list of the StaffNoteLines of this song.
     */
    public ObservableList<StaffNoteLine> getTheLines() {
        return theLines;
    }

    /**
     * @param i
     *            The index that we want to modify.
     * @param s
     *            The StaffNoteLine that we want to place at this index.
     */
    public void setLine(int i, StaffNoteLine s) {
        theLines.set(i, s);
    }

    /**
     * Adds a line into this sequence.
     *
     * @param s
     *            The StaffNoteLine that we want to add.
     */
    public void addLine(StaffNoteLine s) {
        theLines.add(s);
    }

    /**
     * Adds a line into this sequence.
     *
     * @param i
     *            The index at which we want to add this line.
     * @param s
     *            The StaffNoteLine that we want to add.
     */
    public void addLine(int i, StaffNoteLine s) {
        theLines.add(i, s);
    }

    /**
     * Removes a line from this sequence.
     *
     * @param s
     *            The StaffNoteLine that we want to delete.
     */
    public void deleteLine(StaffNoteLine s) {
        theLines.remove(s);
    }

    /**
     * Removes a line from this sequence.
     *
     * @param i
     *            The line index we want to delete from.
     */
    public void deleteLine(int i) {
        theLines.remove(i);
    }

    /** @return The tempo of this sequence. */
    public DoubleProperty getTempo() {
        return tempo;
    }

    /**
     * Sets the tempo of this sequence.
     *
     * @param t
     *            The tempo of this sequence.
     */
    public void setTempo(double t) {
        tempo.set(t);
    }

    /**
     * @param i
     *            The note extensions bitfield that we want to set.
     */
    public void setNoteExtensions(boolean[] i) {
    	for(int idx = 0; idx < Values.NUMINSTRUMENTS; idx++)
    		noteExtensions[idx].set(i[idx]);
    }    
    
    /**
     * @param i
     *            The note extensions bitfield that we want to set.
     */
    public void setNoteExtensions(BooleanProperty[] i) {
    	for(int idx = 0; idx < Values.NUMINSTRUMENTS; idx++)
    		noteExtensions[idx].set(i[idx].get());
    }

    /** @return The bitfield denoting which notes are extended. */
    public BooleanProperty[] getNoteExtensions() {
        return noteExtensions;
    }

    /**
     * @param s
     *            The time signature to set this <code>StaffSequence</code> to.
     */
    public void setTimeSignature(String s) {
        int top = Integer.parseInt(s.substring(0, s.indexOf("/")));
        int bottom = Integer.parseInt(s.substring(s.indexOf("/") + 1));
        for (TimeSignature tSig : TimeSignature.values()) {
            if (tSig.bottom() == bottom && tSig.top() == top) {
                t.set(tSig);
                break;
            }
        }
        if (t.get() == null) {
            t.set(TimeSignature.FOUR_FOUR);
        }
    }

    /** @return The time signature of this sequence. */
    public ObjectProperty<TimeSignature> getTimeSignature() {
        return t;
    }

	/**
	 * Sets the soundset for this sequence which should be loaded with the
	 * sequence.
	 * @since v1.1.2
	 */
	public void setSoundset(String soundset) {
		soundsetBinding.set(soundset);
    }

	/**
	 * @return The soundset bound to this sequence.
	 * @since v1.1.2
	 */
	public StringProperty getSoundset() {
		return soundsetBinding;
    }
	
	/**
	 * @return IntegerProperty of theLines's size
	 * @since v1.1.3
	 */
	public ReadOnlyIntegerProperty getTheLinesSize() {
		return theLinesSize;
	}
    
    @Override
    public String toString() {
        StringBuilder out = new StringBuilder();
        out.append("Tempo = " + tempo + "\n");
        out.append("Extensions = " + noteExtensions + "\n");
        out.append(theLines.toString() + "\n");
        return out.toString();
    }

}
<file_sep>package smp.components.buttons;

import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import smp.ImageIndex;
import smp.ImageLoader;
import smp.components.general.ImageRadioButton;
import smp.fx.SMPFXController;
import smp.stateMachine.ProgramState;
import smp.stateMachine.StateMachine;

/**
 * Wrapper class for an ImageView that holds the play button image. Pressing the
 * button changes the image and also changes the state of the program.
 *
 * @author RehdBlob
 * @since 2012.08.28
 */
public class PlayButton extends ImageRadioButton {

    /**
     * Instantiates the Play button on the staff
     *
     * @param i
     *            The ImageView object that will be manipulated by this class.
     * @param ct
     *            The FXML controller object.
     * @param im
     *            The Image loader object.
     */
    public PlayButton(ImageView i, SMPFXController ct, ImageLoader im) {
        super(i, ct, im);
        getImages(ImageIndex.PLAY_PRESSED, ImageIndex.PLAY_RELEASED);
        releaseImage();
        isPressed = false;
    }

    @Override
    public void reactPressed(MouseEvent e) {
        if (isPressed)
            return;
        super.reactPressed(e);
        if (StateMachine.getState() == ProgramState.EDITING) {
            StateMachine.setState(ProgramState.SONG_PLAYING);
            theStaff.startSong();
        } else if (StateMachine.getState() == ProgramState.ARR_EDITING) {
            StateMachine.setState(ProgramState.ARR_PLAYING);
            theStaff.startArrangement();
        }
    }

}
<file_sep>package smp.components.staff.sounds;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.sound.midi.Instrument;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Patch;
import javax.sound.midi.Receiver;
import javax.sound.midi.Soundbank;
import javax.sound.midi.Synthesizer;
import javax.sound.midi.Transmitter;
import javax.sound.midi.VoiceStatus;

import com.sun.media.sound.SoftSynthesizer;

/**
 * Class for multiple synthesizers in one class.
 * Should make it simpler to call synthesizers with more than
 * 16 MIDI channels. One can add multiple different types of
 * Synthesizers into this class because of the abstraction
 * that was implemented; this class holds Synthesizer type
 * objects.
 * @author RehdBlob
 * @since 2012.08.24
 */
public class MultiSynthesizer implements Synthesizer {

    /**
     * Indicates whether this Synthesizer is actually initialized
     * or not. Many methods will not work if this is not <b>True</b>.
     */
    protected boolean initialized;

    /**
     * The list of synthesizers that this class holds.
     */
    protected ArrayList<Synthesizer> theSynths;

    /**
     * Initializes the ArrayList of Synthesizers and adds the default
     * Synthesizer into the ArrayList.
     * @throws MidiUnavailableException If MidiSystem.getSynthesizer()
     * fails.
     */
    public MultiSynthesizer() throws MidiUnavailableException {
        theSynths = new ArrayList<Synthesizer>();
        addDefaultSynthesizer();
        initialized = true;

    }

    /**
     * Add a synthesizer to the list of synthesizers for this
     * aggregate class.
     * @param s1 The Synthesizer to be added.
     * @throws MidiUnavailableException If the MultiSynthesizer isn't
     * initialized.
     */
    public void addSynths(Synthesizer s1) throws MidiUnavailableException {
        if (!initialized || !isOpen())
            throw new MidiUnavailableException();
        for (Instrument i : s1.getLoadedInstruments())
            s1.unloadInstrument(i);
        for (Instrument inst : this.getLoadedInstruments())
            s1.loadInstrument(inst);
        theSynths.add(s1);
    }

    /**
     * Adds synthesizers to the list of synthesizers for this
     * aggregate class.
     * @param s1 The first Synthesizer to be added.
     * @param s2 The second Synthesizer to be added.
     * @throws MidiUnavailableException If the MultiSynthesizer is not
     * initialized.
     */
    public void addSynths(Synthesizer s1, Synthesizer s2)
            throws MidiUnavailableException {
        addSynths(s1);
        addSynths(s2);
    }

    /**
     * Adds synthesizers to the list of synthesizers for this
     * aggregate class.
     * @param s1 The first Synthesizer to be added.
     * @param s2 The second Synthesizer to be added.
     * @param s As many more Synthesizer objects as needed.
     * @throws MidiUnavailableException If the MultiSynthesizer is not
     * initialized.
     */
    public void addSynths(Synthesizer s1, Synthesizer s2, Synthesizer... s)
            throws MidiUnavailableException {
        addSynths(s1);
        addSynths(s2);
        for (Synthesizer more : s)
            addSynths(more);
    }

    /**
     * Adds the default Synthesizer into the list of Synthesizers, and also
     * unloads all of the instruments that are by default included.
     * @throws MidiUnavailableException If the MidiSystem.getSynthesizer()
     * call fails.
     */
    private void addDefaultSynthesizer() throws MidiUnavailableException {
        Synthesizer s = MidiSystem.getSynthesizer();
        theSynths.add(s);
    }

    /**
     * Gets the device info of the first Synthesizer in the Synthesizer list.
     */
    @Override
    public Info getDeviceInfo() {
        return theSynths.get(0).getDeviceInfo();
    }

    /**
     * Opens all of the Synthesizers in the list.
     * @throws MidiUnavailableException If for some reason any of the .open()
     * calls doesn't work, or if the MultiSynthesizer is not initialized.
     */
    @Override
    public void open() throws MidiUnavailableException {
        if (!initialized)
            throw new MidiUnavailableException();
        for (Synthesizer s : theSynths)
            s.open();
    }

    /**
     * Closes all of the Synthesizers in the Synthesizer list.
     */
    @Override
    public void close() {
        for (Synthesizer s : theSynths)
            s.close();
    }

    /**
     * Checks if all of the Synthesizers are open.
     */
    @Override
    public boolean isOpen() {
        for (Synthesizer s : theSynths)
            if (!s.isOpen())
                return false;
        return true;
    }

    /**
     * Gets the microsecond position of the first Synthesizer in the
     * Synthesizer list.
     */
    @Override
    public long getMicrosecondPosition() {
        return theSynths.get(0).getMicrosecondPosition();
    }

    /**
     * @return The maximum number of receivers that the Synthesizers in the
     * list can hold, determined by the Synthesizer that can hold the least
     * number of Receivers.
     */
    @Override
    public int getMaxReceivers() {
        ArrayList<Integer> numbers = new ArrayList<Integer>();
        for (Synthesizer s : theSynths)
            numbers.add(s.getMaxReceivers());
        Collections.sort(numbers);
        return numbers.get(numbers.size() - 1);
    }

    /**
     * @return The maximum number of Transmitters that the Synthesizers in the
     * list can hold, determined by the Synthesizer that can hold the least
     * number of Transmitters.
     */
    @Override
    public int getMaxTransmitters() {
        ArrayList<Integer> numbers = new ArrayList<Integer>();
        for (Synthesizer s : theSynths)
            numbers.add(s.getMaxTransmitters());
        Collections.sort(numbers);
        return numbers.get(numbers.size() - 1);
    }

    /**
     * @return The Receiver of the first Synthesizer in the list of
     * Synthesizers. Not useful in the context of multiple Synthesizers.
     */
    @Deprecated
    @Override
    public Receiver getReceiver() throws MidiUnavailableException {
        return theSynths.get(0).getReceiver();
    }

    /**
     * @return All of the Receivers of all of the Synthesizers in the list
     * of Synthesizers.
     */
    @Override
    public List<Receiver> getReceivers() {
        ArrayList<Receiver> all = new ArrayList<Receiver>();
        for (Synthesizer s : theSynths)
            all.addAll(s.getReceivers());
        return all;
    }

    /**
     * @return the Transmitter of the first Synthesizer in the list of
     * Synthesizers. Not useful in the context of multiple Synthesizers.
     * @throws MidiUnavailableException If the MultiSynthesizer is not open
     * or if something goes wrong in getting the Trasnmitter.
     */
    @Deprecated
    @Override
    public Transmitter getTransmitter() throws MidiUnavailableException {
        if (!initialized)
            throw new MidiUnavailableException();
        return theSynths.get(0).getTransmitter();
    }

    /**
     * @return All of the Transmitters of all of the Synthesizers in the list
     * of Synthesizers.
     */
    @Override
    public List<Transmitter> getTransmitters() {
        ArrayList<Transmitter> all = new ArrayList<Transmitter>();
        for (Synthesizer s : theSynths)
            all.addAll(s.getTransmitters());
        return all;
    }

    /**
     * @return The maximum polyphony of the set of Synthesizers,
     * determined by the Synthesizer with the minimum polyphony.
     */
    @Override
    public int getMaxPolyphony() {
        ArrayList<Integer> nums = new ArrayList<Integer>();
        for (Synthesizer s : theSynths)
            nums.add(s.getMaxPolyphony());
        Collections.sort(nums);
        return nums.get(nums.size() - 1);
    }

    /**
     * @return The latency of the set of Synthesizers,
     * determined by the Synthesizer with the highest latency.
     */
    @Override
    public long getLatency() {
        ArrayList<Long> nums = new ArrayList<Long>();
        for (Synthesizer s : theSynths)
            nums.add(s.getLatency());
        Collections.sort(nums);
        return nums.get(0);
    }

    /**
     * @return An array of MidiChannel objects. Elements 0-15 are from the first
     * synthesizer, elements 16-31 are from the second synthesizer, and so on.
     */
    @Override
    public MidiChannel[] getChannels() {
        ArrayList<MidiChannel> all = new ArrayList<MidiChannel>();
        if (theSynths.size() == 1)
            return theSynths.get(0).getChannels();
        else {
            for (Synthesizer s : theSynths) {
                MidiChannel[] temp = s.getChannels();
                for (MidiChannel m : temp)
                    all.add(m);
            }
        }
        MidiChannel[] ret = new MidiChannel[all.size()];
        for (int i = 0; i < all.size(); i++)
            ret[i] = all.get(i);
        return ret;
    }


    /**
     * @return the VoiceStatus of all of the Synthesizer objects.
     */
    @Override
    public VoiceStatus[] getVoiceStatus() {
        VoiceStatus[] all;
        if (theSynths.size() == 1)
            all = theSynths.get(0).getVoiceStatus();
        else {
            all = new VoiceStatus[0];
            for (Synthesizer s : theSynths) {
                VoiceStatus[] temp = s.getVoiceStatus();
                VoiceStatus[] concat =
                        new VoiceStatus[all.length + temp.length];
                for (int i = 0; i < concat.length; i++) {
                    if (i < all.length)
                        concat[i] = all[i];
                    else
                        concat[i] = temp[i-all.length];
                }
                all = concat;
            }
        }
        return all;
    }

    /**
     * @return <b>True</b> if the given Soundbank is supported by ALL of
     * the Synthesizers in the list of Synthesizers, <b>False</b> otherwise.
     */
    @Override
    public boolean isSoundbankSupported(Soundbank soundbank) {
        for (Synthesizer s : theSynths)
            if (!s.isSoundbankSupported(soundbank))
                return false;
        return true;
    }

    /**
     * Loads instruments into all of the Synthesizers in the list.
     * @return <b>True</b> if the Instrument is successfully loaded into
     * ALL of the Synthesizers in the list of Synthesizers, <b>False</b>
     * otherwise.
     */
    @Override
    public boolean loadInstrument(Instrument instrument) {
        boolean isLoadedCorrectly = true;;
        for (Synthesizer s : theSynths)
            if (!s.loadInstrument(instrument))
                isLoadedCorrectly = false;;
                return isLoadedCorrectly;
    }

    /**
     * Unloads the specified instrument from ALL of the Synthesizers in the
     * list of Synthesizers.
     * @param instrument The Instrument to unload.
     */
    @Override
    public void unloadInstrument(Instrument instrument) {
        for (Synthesizer s : theSynths)
            s.unloadInstrument(instrument);
    }

    /**
     * @return <b>True</b> if the Instrument remapping was successful in ALL
     * of the Synthesizer objects in the Synthesizer list, <b>False</b>
     * otherwise.
     */
    @Override
    public boolean remapInstrument(Instrument from, Instrument to) {
        boolean didItWork = true;
        for (Synthesizer s : theSynths)
            if (!s.remapInstrument(from, to))
                didItWork = false;
        return didItWork;
    }

    /**
     * @return The default soundbank of the first Synthesizer element in
     * the list of Synthesizers.
     */
    @Override
    public Soundbank getDefaultSoundbank() {
        return theSynths.get(0).getDefaultSoundbank();
    }

    /**
     * Theoretically, the available instruments in all of the
     * Synthesizers should be equivalent, but that may not be the case.
     * Will implement an intersection algorithm sometime, but for now,
     * we will just keep it simple.
     * @return The available instruments of the first Synthesizer object
     * in the list of Synthesizers.
     */
    @Override
    public Instrument[] getAvailableInstruments() {
        return theSynths.get(0).getAvailableInstruments();
    }

    /**
     * At some point, an intersection algorithm will determine the
     * common loaded instruments in all of the Synthesizers in the list.
     * For now, it will be kept simple.
     * @return The loaded instruments of the first Synthesizer object in
     * the list of Synthesizers.
     */
    @Override
    public Instrument[] getLoadedInstruments() {
        return theSynths.get(0).getLoadedInstruments();
    }

    /**
     * Loads all of the instruments of a Soundbank into ALL of the Synthesizers
     * in the list of Synthesizers.
     * @return <b>True</b> if the load was successful, <b>False</b> otherwise.
     */
    @Override
    public boolean loadAllInstruments(Soundbank soundbank) {
        boolean success = true;
        for (Synthesizer s : theSynths)
            if (!s.loadAllInstruments(soundbank))
                success = false;
        return success;
    }

    /**
     * Unloads all of the instruments of a Soundbank from ALL of the
     * Synthesizers in the list of Synthesizers.
     */
    @Override
    public void unloadAllInstruments(Soundbank soundbank) {
        for (Synthesizer s : theSynths)
            s.unloadAllInstruments(soundbank);
    }

    /**
     * Loads the specified instruments from a soundbank into ALL of the
     * Synthesizers in the list of Synthesizers.
     * @return <b>True</b> if it succeeded, <b>False</b> otherwise.
     */
    @Override
    public boolean loadInstruments(Soundbank soundbank, Patch[] patchList) {
        boolean success = true;
        for (Synthesizer s : theSynths)
            if (!s.loadInstruments(soundbank, patchList))
                success = false;
        return success;
    }

    /**
     * Unloads the specified instruments from ALL of the Synthesizers in the
     * list of Synthesizers.
     */
    @Override
    public void unloadInstruments(Soundbank soundbank, Patch[] patchList) {
        for (Synthesizer s : theSynths)
            s.unloadInstruments(soundbank, patchList);
    }

    /**
     * Adds SoftSynthesizer objects to the MultiSynthesizer such that
     * it has enough Synthesizers to accommodate the number of channels
     * that one wishes to have.
     * @param i The number of channels that one wishes to have.
     * @throws MidiUnavailableException If the MultiSynthesizer isn't
     * initialized.
     */
    public void ensureCapacity(int i) throws MidiUnavailableException {
        if (!initialized)
            throw new MidiUnavailableException();
        int repeat = (int) Math.floor((double)i / 16);
        for (int j = 0; j < repeat; j++) {
            Synthesizer s = new SoftSynthesizer();
            s.open();
            for (Instrument inst : s.getLoadedInstruments())
                s.unloadInstrument(inst);
            for (Instrument inst : this.getLoadedInstruments())
                s.loadInstrument(inst);
            theSynths.add(s);
        }
    }
}
<file_sep>package smp.models.staff;

import java.io.Serializable;
import java.text.ParseException;

import smp.components.Values;
import smp.components.InstrumentIndex;

/**
 * A note on the Staff, to be added to the noteMatrix of the Staff.
 *
 * @author RehdBlob
 * @since 2012.08.31
 */
public class StaffNote implements Serializable {

    /**
     * Generated serial ID.
     */
    private static final long serialVersionUID = 6827248837281952104L;

    /**
     * This is the location on the matrix where the note exists. (y-axis). One
     * can recover the note that you are supposed to actually play by adding a
     * constant offset to this position value.
     */
    private int position;

    /** The offset that this note will have. */
    private int accidental = 0;

    /** This is the volume of the note. */
    private int volume = Values.DEFAULT_VELOCITY;

    /**
     * Whether this is a mute note or not. 0 indicates no. 1 indicates a note
     * mute type. 2 indicates an instrument mute type.
     */
    private int muteNote;

    /**
     * The Instrument that the note on the staff is to use.
     */
    private InstrumentIndex theInstrument;

    /**
     * Default constructor that makes the note by default at half volume.
     *
     * @param theInd
     *            The instrument that this StaffNote will play.
     * @param position
     *            The physical location of this note on the staff.
     * @param acc
     *            The sharp / flat / whatever that we are offsetting this note
     *            by.
     */
    public StaffNote(InstrumentIndex theInd, int pos, int acc) {
        this(theInd, pos, acc, Values.HALF_VELOCITY);
    }

    /**
     * @param theInd
     *            The instrument that this StaffNote will play.
     * @param position
     *            The physical location of this note on the staff.
     * @param acc
     *            The sharp / flat / whatever that we are offsetting this note
     *            by.
     * @param vol
     *            The volume that we want this note to play at.
     */
    public StaffNote(InstrumentIndex theInd, int pos, int acc, int vol) {
        theInstrument = theInd;
        accidental = acc;
        position = pos;
        volume = vol;
    }

    /**
     * Construct a <code>StaffNote</code> given its printed
     * <code>toString()</code>
     *
     * @param spl
     *            The String to attempt to convert to a <code>StaffNote</code>
     * @throws ParseException
     *             In case we are trying to parse an invalid string.
     */
    public StaffNote(String spl) throws ParseException {
        String[] sp = spl.split(" ");
        if (sp.length != 2) {
            throw new ParseException("Invalid note", 0);
        }
        theInstrument = InstrumentIndex.valueOf(sp[0]);
        for (int i = 0; i < Values.staffNotes.length; i++) {
            if (sp[1].contains(Values.staffNotes[i].name())) {
                position = i;
            }
        }
        // Single-note volumes not implemented yet.
        volume = Values.HALF_VELOCITY;
        switch (sp[1].length()) {
        case 2:
            accidental = 0;
            muteNote = 0;
            break;
        case 3:
            accidental = decodeAccidental(sp[1].charAt(2));
            muteNote = 0;
            break;
        case 4:
            accidental = 0;
            muteNote = Integer.parseInt("" + sp[1].charAt(sp[1].length() - 1));
            break;
        case 5:
            accidental = decodeAccidental(sp[1].charAt(2));
            muteNote = Integer.parseInt("" + sp[1].charAt(sp[1].length() - 1));
            break;
        default:
            accidental = 0;
            muteNote = 0;
            break;
        }
    }

    /**
     * Given character <code>c</code>, decode it as a doublesharp, sharp, flat,
     * or doubleflat.
     *
     * @param c
     *            The character to decode.
     * @return The accidental to set.
     */
    private int decodeAccidental(char c) {
        switch (c) {
        case 'X':
            return 2;
        case '#':
            return 1;
        case 'b':
            return -1;
        case 'B':
            return -2;
        default:
            return 0;
        }
    }

    /**
     * Sets the accidental of this note to whatever <code>a</code> is.
     *
     * @param a
     *            The accidental that we're trying to set this note to.
     */
    public void setAccidental(int a) {
        accidental = a;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof StaffNote)) {
            return false;
        } else {
            StaffNote other = (StaffNote) o;
            return other.position == position
                    && other.theInstrument == theInstrument
                    && other.accidental == accidental
                    && other.muteNote == muteNote;
        }
    }

    /** @return The offset from the actual note that we have here. */
    public int getAccidental() {
        return accidental;
    }

    /** @return The numerical position that this note is located at. */
    public int getPosition() {
        return position;
    }

    /** @return The numerical volume of this note. */
    public int volume() {
        return volume;
    }

    /**
     * Sets the volume to some number that we give to this method.
     *
     * @param v
     *            The volume we want to set.
     */
    public void setVolume(int v) {
        if (v >= Values.MIN_VELOCITY && v <= Values.MAX_VELOCITY)
            volume = v;
    }

    /** @return The instrument that this StaffNote is. */
    public InstrumentIndex getInstrument() {
        return theInstrument;
    }

    /**
     * @return The mute note type that this note is.
     */
    public int muteNoteVal() {
        return muteNote;
    }

    /**
     * Sets the StaffNote to a specific type of mute note. 1 indicates a mute
     * note that mutes a single note. 2 indicates a mute note that mutes an
     * entire instrument.
     *
     * @param m
     *            What type of mute note this is.
     */
    public void setMuteNote(int m) {
        muteNote = m;
    }

    @Override
    public String toString() {
        String noteName = Values.staffNotes[position].name();
        String noteAcc = "";
        switch (accidental) {
        case 2:
            noteAcc = "X";
            break;
        case 1:
            noteAcc = "#";
            break;
        case -1:
            noteAcc = "b";
            break;
        case -2:
            noteAcc = "B";
            break;
        default:
            break;

        }
        if (muteNote == 2) {
            return theInstrument.toString() + " " + noteName + noteAcc + "m2";
        } else if (muteNote == 1) {
            return theInstrument.toString() + " " + noteName + noteAcc + "m1";
        } else {
            return theInstrument.toString() + " " + noteName + noteAcc;
        }
    }

}
<file_sep>package smp.presenters.staff;

import java.util.ArrayList;

import javafx.beans.property.DoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import smp.ImageIndex;
import smp.ImageLoader;
import smp.TestMain;
import smp.components.Values;
import smp.models.stateMachine.StateMachine;

public class StaffMeasureLinesPresenter {

	// TODO: auto-add these model comments
	// ====Models====
	private DoubleProperty measureLineNumber;

	private HBox staffMeasureLines;

	private ArrayList<ImageView> measureLines;

	// TODO: set
	private ImageLoader il = (ImageLoader) TestMain.imgLoader;

	public StaffMeasureLinesPresenter(HBox staffMeasureLines) {
		this.staffMeasureLines = staffMeasureLines;

		this.measureLineNumber = StateMachine.getMeasureLineNum();
		initializeStaffMeasureLines(this.staffMeasureLines);
		setupViewUpdater();
	}
	
    /**
     * These are the lines that divide up the staff.
     *
     * @param staffMLines
     *            The measure lines that divide the staff.
     */
	private void initializeStaffMeasureLines(HBox mLines) {
        measureLines = new ArrayList<ImageView>();
        for (Node n : mLines.getChildren())
            measureLines.add((ImageView) n);
        for (int i = 0; i < measureLines.size(); i++) {
            if (i % Values.TIMESIG_BEATS == 0)
                measureLines.get(i).setImage(
                        il.getSpriteFX(ImageIndex.STAFF_MLINE));
            else
                measureLines.get(i).setImage(
                        il.getSpriteFX(ImageIndex.STAFF_LINE));
        }
	}

	private void setupViewUpdater() {
		this.measureLineNumber.addListener(new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				int currLine = newValue.intValue();
		        for (int i = 0; i < measureLines.size(); i++) {
		        	ImageView currImage = measureLines.get(i);
		            if ((currLine + i) % Values.TIMESIG_BEATS == 0) {
		            	currImage.setImage(il.getSpriteFX(ImageIndex.STAFF_MLINE));
		            } else {
		                currImage.setImage(il.getSpriteFX(ImageIndex.STAFF_LINE));
		            }
		        }
			}
		});
	}
}
<file_sep>package smp.models.staff;

import java.io.Serializable;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import smp.components.Values;

/**
 * A line of notes on the staff. This can include
 * notes, bookmarks, etc.
 * @author RehdBlob
 * @since 2012.09.19
 */
public class StaffNoteLine implements Serializable {

    /**
     * Generated serial ID
     */
    private static final long serialVersionUID = 3876410979457142750L;

    /**
     * This is the list of note volumes (we will use this later as an extension
     * to the usual volume-bar-sets-the-volume-of-the-whole-line thing.
     */
    private ObservableList<IntegerProperty> volumes;

    /** This is the volume of the entire <code>StaffNoteLine</code> */
    private IntegerProperty volume = new SimpleIntegerProperty(Values.DEFAULT_VELOCITY);

    /** This ArrayList holds staff notes inside it. */
    private ObservableList<StaffNote> notes = FXCollections.observableArrayList();  
    
    /** @since v1.1.3*/
    private ReadOnlyIntegerProperty notesSize = new SimpleListProperty<>(notes).sizeProperty();

    /** This ArrayList holds staff events in it. */
    private ObservableList<StaffEvent> marks = FXCollections.observableArrayList();

    /**
     * Creates a new staff note line with the specified
     * line number.
     */
    public StaffNoteLine() {
    	
    }

    /**
     * Adds a note to the staff note line.
     * @param n The note to add to this StaffNoteLine.
     */
    public void add(StaffNote n) {
        notes.add(n);
    }

    /**
     * Adds an event to this staff note line.
     * @param e The event that we are trying to add.
     */
    public void addEvent(StaffEvent e) {
        marks.add(e);
    }

    /**
     * Deletes a note from the staff.
     * @param n The note to delete.
     * @return True if we successfully removed the note.
     */
    public boolean remove(StaffNote n) {
        return notes.remove(n);
    }

    /**
     * Deletes a note from the staff.
     * @param index The index which we are deleting from.
     * @return The deleted element.
     */
    public StaffNote remove(int index) {
        return notes.remove(index);
    }


    /**
     * Deletes an event from this staff note line.
     * @param e The event that we are trying to remove.
     * @return True if we successfully removed the event.
     */
    public boolean removeEvent(StaffEvent e) {
        return marks.remove(e);
    }

    /** @return Whether this StaffNoteLine contains the staff note already. */
    public boolean contains(StaffNote theNote) {
        return notes.contains(theNote);
    }

    /** @return The number of notes that are in this StaffNoteLine. */
    public int size() {
        return notes.size();
    }

    /** @return Whether the StaffNoteLine has any notes or not. */
    public boolean isEmpty() {
        return notes.isEmpty();
    }

    /**
     * @return The list of notes that this <code>StaffNoteLine</code> contains.
     */
    public ObservableList<StaffNote> getNotes() {
        return notes;
    }

    /** @return The list of volumes of the different notes. */
    public ObservableList<IntegerProperty> getVolumes() {
        return volumes;
    }

    /** @return The volume of this <code>StaffNoteLine</code>. */
    public IntegerProperty getVolume() {
        return volume;
    }

    /**
     * @param y The volume that we want to set this note line to.
     */
    public void setVolume(double y) {
        if (volume.get() >= Values.MIN_VELOCITY &&
                volume.get() <= Values.MAX_VELOCITY)
            volume.set((int) y);
    }

    /**
     * @param vol A percentage (between 0 and 1) that we want
     * to scale this volume by.
     */
    public void setVolumePercent(double vol) {
        if (vol >= 0 && vol <= 1)
            volume.set((int) (vol * Values.MAX_VELOCITY));
    }

    /**
     * @return The percent volume of this StaffNoteLine.
     */
    public double getVolumePercent() {
        return volume.doubleValue() / Values.MAX_VELOCITY;
    }

    public ReadOnlyIntegerProperty getNotesSize() {
    	return notesSize;
    }
    
    @Override
    public String toString() {
        return notes.toString();
    }


}
<file_sep>package smp.presenters;

import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.image.ImageView;
import smp.models.stateMachine.ProgramState;
import smp.models.stateMachine.StateMachine;

public class ClipboardLabelPresenter {

	//TODO: auto-add these model comments
	//====Models====
	private ObjectProperty<ProgramState> programState;
	
	private ImageView clipboardLabel;

	public ClipboardLabelPresenter(ImageView clipboardLabel) {
		this.clipboardLabel = clipboardLabel;
		this.programState = StateMachine.getState();
		setupViewUpdater();
	}

    private void setupViewUpdater() {
    	this.programState.addListener(new ChangeListener<Object>() {

			@Override
			public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
				if (newValue.equals(ProgramState.EDITING))
					clipboardLabel.setVisible(true);
				else if (newValue.equals(ProgramState.ARR_EDITING))
					clipboardLabel.setVisible(false);
			}
		});
	}
}
<file_sep>package smp.components.staff.sequences;

import java.io.Serializable;
import java.util.ArrayList;

import smp.components.Values;

/**
 * A line of notes on the staff. This can include
 * notes, bookmarks, etc.
 * @author RehdBlob
 * @since 2012.09.19
 */
public class StaffNoteLine implements Serializable {

    /**
     * Generated serial ID
     */
    private static final long serialVersionUID = 3876410979457142750L;

    /**
     * This is the list of note volumes (we will use this later as an extension
     * to the usual volume-bar-sets-the-volume-of-the-whole-line thing.
     */
    private ArrayList<Integer> volumes;

    /** This is the volume of the entire <code>StaffNoteLine</code> */
    private int volume;

    /** This ArrayList holds staff notes inside it. */
    private ArrayList<StaffNote> notes;

    /** This ArrayList holds staff events in it. */
    private ArrayList<StaffEvent> marks;

    /**
     * Creates a new staff note line with the specified
     * line number.
     */
    public StaffNoteLine() {
        notes = new ArrayList<StaffNote>();
        marks = new ArrayList<StaffEvent>();
    }

    /**
     * Adds a note to the staff note line.
     * @param n The note to add to this StaffNoteLine.
     */
    public void add(StaffNote n) {
        notes.add(n);
    }

    /**
     * Adds an event to this staff note line.
     * @param e The event that we are trying to add.
     */
    public void addEvent(StaffEvent e) {
        marks.add(e);
    }

    /**
     * Deletes a note from the staff.
     * @param n The note to delete.
     * @return True if we successfully removed the note.
     */
    public boolean remove(StaffNote n) {
        return notes.remove(n);
    }

    /**
     * Deletes a note from the staff.
     * @param index The index which we are deleting from.
     * @return The deleted element.
     */
    public StaffNote remove(int index) {
        return notes.remove(index);
    }


    /**
     * Deletes an event from this staff note line.
     * @param e The event that we are trying to remove.
     * @return True if we successfully removed the event.
     */
    public boolean removeEvent(StaffEvent e) {
        return marks.remove(e);
    }

    /** @return Whether this StaffNoteLine contains the staff note already. */
    public boolean contains(StaffNote theNote) {
        return notes.contains(theNote);
    }

    /** @return The number of notes that are in this StaffNoteLine. */
    public int size() {
        return notes.size();
    }

    /** @return Whether the StaffNoteLine has any notes or not. */
    public boolean isEmpty() {
        return notes.isEmpty();
    }

    /**
     * @return The list of notes that this <code>StaffNoteLine</code> contains.
     */
    public ArrayList<StaffNote> getNotes() {
        return notes;
    }

    /** @return The list of volumes of the different notes. */
    public ArrayList<Integer> getVolumes() {
        return volumes;
    }

    /** @return The volume of this <code>StaffNoteLine</code>. */
    public int getVolume() {
        return volume;
    }

    /**
     * @param y The volume that we want to set this note line to.
     */
    public void setVolume(double y) {
        if (volume >= Values.MIN_VELOCITY &&
                volume <= Values.MAX_VELOCITY)
            volume = (int) y;
    }

    /**
     * @param vol A percentage (between 0 and 1) that we want
     * to scale this volume by.
     */
    public void setVolumePercent(double vol) {
        if (vol >= 0 && vol <= 1)
            volume = (int) (vol * Values.MAX_VELOCITY);
    }

    /**
     * @return The percent volume of this StaffNoteLine.
     */
    public double getVolumePercent() {
        return ((double) volume) / Values.MAX_VELOCITY;
    }

    @Override
    public String toString() {
        return notes.toString();
    }


}
<file_sep>package smp.presenters.api.reattachers;

import java.util.ArrayList;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import smp.components.Values;
import smp.models.staff.StaffNoteLine;
import smp.models.staff.StaffSequence;

/**
 * This will reattach listeners to the StaffSequence's member properties when a
 * new sequence gets set.
 * 
 * @author J
 *
 */
public class SequenceReattacher {

	// ====(not a Model)====
	private ObjectProperty<StaffSequence> theSequence;

	public ChangeListener<Number> tempoListener;

	public ChangeListener<String> soundsetListener;

	public ListChangeListener<StaffNoteLine> theLinesListener;

	public ChangeListener<Number> theLinesSizeListener;

	public ArrayList<ChangeListener<Boolean>> noteExtensionsListeners = new ArrayList<ChangeListener<Boolean>>();

	public ChangeListener<Object> timeSignatureListener;

	/**
	 * The listener for when a new sequence is set. For instance, a new sequence
	 * is set: the new sequence's tempo should be listened for and the tempo
	 * view updated.
	 */
	public ChangeListener<StaffSequence> onReattachListener;

	public SequenceReattacher(ObjectProperty<StaffSequence> theSequence) {
		this.theSequence = theSequence;
		for (int i = 0; i < Values.NUMINSTRUMENTS; i++)
			noteExtensionsListeners.add(null);
		setupReattacher();
	}

	private void setupReattacher() {
		this.theSequence.addListener(new ChangeListener<Object>() {

			@Override
			public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
				StaffSequence oldSequence = (StaffSequence) oldValue;
				BooleanProperty[] noteExtensions = oldSequence.getNoteExtensions();
				for (int i = 0; i < noteExtensions.length; i++)
					if (noteExtensionsListeners.get(i) != null)
						noteExtensions[i].removeListener(noteExtensionsListeners.get(i));
				if (soundsetListener != null)
					oldSequence.getSoundset().removeListener(soundsetListener);
				if (tempoListener != null)
					oldSequence.getTempo().removeListener(tempoListener);
				if (theLinesListener != null)
					oldSequence.getTheLines().removeListener(theLinesListener);
				if (theLinesSizeListener != null)
					oldSequence.getTheLinesSize().removeListener(theLinesSizeListener);
				if (timeSignatureListener != null)
					oldSequence.getTimeSignature().removeListener(timeSignatureListener);

				StaffSequence newSequence = (StaffSequence) newValue;
				noteExtensions = newSequence.getNoteExtensions();
				for (int i = 0; i < noteExtensions.length; i++)
					if (noteExtensionsListeners.get(i) != null)
						noteExtensions[i].addListener(noteExtensionsListeners.get(i));
				if (soundsetListener != null)
					newSequence.getSoundset().addListener(soundsetListener);
				if (tempoListener != null)
					newSequence.getTempo().addListener(tempoListener);
				if (theLinesListener != null)
					newSequence.getTheLines().addListener(theLinesListener);
				if (theLinesSizeListener != null)
					newSequence.getTheLinesSize().addListener(theLinesSizeListener);
				if (timeSignatureListener != null)
					newSequence.getTimeSignature().addListener(timeSignatureListener);
			}
		});
	}

	public void setNewTempoListener(ChangeListener<Number> tempoListener) {
		if (this.tempoListener != null)
			this.theSequence.get().getTempo().removeListener(this.tempoListener);
		this.tempoListener = tempoListener;
		this.theSequence.get().getTempo().addListener(this.tempoListener);
	}

	public void setNewSoundsetListener(ChangeListener<String> soundsetListener) {
		if (this.soundsetListener != null)
			this.theSequence.get().getSoundset().removeListener(this.soundsetListener);
		this.soundsetListener = soundsetListener;
		this.theSequence.get().getSoundset().addListener(this.soundsetListener);
	}

	public void setNewTheLinesListener(ListChangeListener<StaffNoteLine> theLinesListener) {
		if (this.theLinesListener != null)
			this.theSequence.get().getTheLines().removeListener(this.theLinesListener);
		this.theLinesListener = theLinesListener;
		this.theSequence.get().getTheLines().addListener(this.theLinesListener);
	}

	public void setNewTheLinesSizeListener(ChangeListener<Number> theLinesSizeListener) {
		if (this.theLinesSizeListener != null)
			this.theSequence.get().getTheLinesSize().removeListener(this.theLinesSizeListener);
		this.theLinesSizeListener = theLinesSizeListener;
		this.theSequence.get().getTheLinesSize().addListener(this.theLinesSizeListener);
	}

	public void setNewNoteExtensionListener(int index, ChangeListener<Boolean> noteExtensionsListener) {
		if (this.noteExtensionsListeners.get(index) != null)
			this.theSequence.get().getNoteExtensions()[index].removeListener(this.noteExtensionsListeners.get(index));
		this.noteExtensionsListeners.set(index, noteExtensionsListener);
		this.theSequence.get().getNoteExtensions()[index].addListener(this.noteExtensionsListeners.get(index));
	}

	public void setNewTimeSignatureListener(ChangeListener<Object> timeSignatureListener) {
		if (this.timeSignatureListener != null)
			this.theSequence.get().getTimeSignature().removeListener(this.timeSignatureListener);
		this.timeSignatureListener = timeSignatureListener;
		this.theSequence.get().getTimeSignature().addListener(this.timeSignatureListener);
	}

	public void setOnReattachListener(ChangeListener<StaffSequence> onReattachListener) {
		this.onReattachListener = onReattachListener;
		this.theSequence.addListener(onReattachListener);
	}
}
<file_sep>package smp;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;

import javax.sound.midi.Instrument;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Soundbank;
import javax.sound.midi.Synthesizer;

import smp.components.Values;
import smp.components.InstrumentIndex;
import smp.components.staff.sequences.Note;
import smp.components.staff.sounds.SMPSynthesizer;
import smp.stateMachine.Settings;
import smp.stateMachine.StateMachine;

/**
 * Loads the soundfonts that will be used to play sounds.
 * Also holds a Synthesizer and Soundbank that will be used
 * to play more sounds.
 * @author RehdBlob
 * @author j574y923
 * @since 2012.08.14
 */
public class SoundfontLoader implements Loader {

    /**
     * A number between 0 and 1 that indicates the
     * completion of the loading Thread's tasks.
     */
    private double loadStatus = 0.0;

    /**
     * The sound synthesizer used to hold as many instruments as needed.
     */
    private static SMPSynthesizer theSynthesizer;

    /**
     * The MIDI channels associated with the MultiSynthsizer.
     */
    private static MidiChannel [] chan;

    /**
     * The soundbank that will hold the sounds that we're trying to play.
     */
    private static Soundbank bank;

	/**
	 * The cache that will contain all soundbanks. Each soundbank is indexed by
	 * its filename.
	 */
	private static Map<String, Soundbank> bankCache = new HashMap<>();

	/**
	 * Initializes a MultiSynthesizer with the soundfont.
	 */
    @Override
    public void run() {
        try {
    		File f = new File("./" + Values.DEFAULT_SOUNDFONT);
            bank = MidiSystem.getSoundbank(f);
            theSynthesizer = new SMPSynthesizer();
            theSynthesizer.open();
            setLoadStatus(0.1);
            /* if (advanced mode on)
             *     theSynthesizer.ensureCapacity(50);
             * else
             */

            theSynthesizer.ensureCapacity(45);
            for (Instrument i : theSynthesizer.getLoadedInstruments()) {
                theSynthesizer.unloadInstrument(i);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            setLoadStatus(0.2);
            theSynthesizer.loadAllInstruments(bank);
            setLoadStatus(0.3);

            if (Settings.debug > 0){
                System.out.println("Loaded Instruments: ");
                for (Instrument j : theSynthesizer.getLoadedInstruments())
                    System.out.println(j.getName());
            }

            int ordinal = 0;
            chan = theSynthesizer.getChannels();
            for (InstrumentIndex i : InstrumentIndex.values()) {
                setLoadStatus(0.3 + 0.7
                        * ordinal / InstrumentIndex.values().length);
                chan[ordinal].programChange(ordinal);
                chan[ordinal].controlChange(Values.REVERB, 0);
                ordinal++;
                System.out.println("Initialized Instrument: "
                        + i.toString());
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (Settings.debug > 0)
                System.out.println(
                        "Synth Latency: " + theSynthesizer.getLatency());
            setLoadStatus(1);
        } catch (MidiUnavailableException e) {
            // Can't recover.
            e.printStackTrace();
            System.exit(0);
        } catch (InvalidMidiDataException e) {
            // Can't recover.
            e.printStackTrace();
            System.exit(0);
        } catch (IOException e) {
            // Can't recover.
            e.printStackTrace();
            System.exit(0);
        }
	}

	/**
	 * Creates soundfonts folder in AppData if it doesn't already exist. Next,
	 * copies over the default soundfont soundset3.sf2 if it isn't already in
	 * the folder.
	 * 
	 * @since v1.1.2
	 */
	public void ensureSoundfontsFolderExists() {
		// windows only for now, TODO: linux and mac
		// 1. Let's make sure the folder exists
		File soundfontsFolder = new File(Values.SOUNDFONTS_FOLDER);
		if(!soundfontsFolder.exists()) {
			if(!soundfontsFolder.mkdirs())
				System.out.println("Error: failed to create " + Values.SOUNDFONTS_FOLDER);
		}
		
		// 2. Let's copy soundset3.sf2 if it's not already in there
		File soundfontsFolderSoundset = new File(Values.SOUNDFONTS_FOLDER + Values.DEFAULT_SOUNDFONT);
		if (!soundfontsFolderSoundset.exists()) {
			try {
				Files.copy(new File("./" + Values.DEFAULT_SOUNDFONT).toPath(), soundfontsFolderSoundset.toPath());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
    }
    
	/**
	 * @return The list of filenames *.sf2 in the soundfonts folder.
	 * @since v1.1.2
	 */
	public String[] getSoundfontsList() {
		File soundfontsFolder = new File(Values.SOUNDFONTS_FOLDER);
		if(!soundfontsFolder.exists()) {
			System.out.println("Error: no such directory " + Values.SOUNDFONTS_FOLDER);
			return null;
		}
		
		return soundfontsFolder.list(new FilenameFilter() {
		    @Override
		    public boolean accept(File dir, String name) {
		        return name.toLowerCase().endsWith(".sf2");
		    }
		});
    }
    
	/**
	 * Takes in the absolute path of a soundfont file and constructs a new
	 * soundbank with all the soundfont's instruments loaded in. The
	 * MultiSynthesizer will then use the new soundbank.
	 * 
	 * @param path
	 *            the soundfont file
	 * @throws IOException
	 * @throws InvalidMidiDataException
	 * @throws MidiUnavailableException
	 * @since v1.1.2
	 */
	public void loadSoundfont(String path) throws InvalidMidiDataException, IOException, MidiUnavailableException {
		File f = new File(path);
		if(f.getName().isEmpty())
			return;
		if (!f.getName().equals(StateMachine.getCurrentSoundset())) {
			bank = MidiSystem.getSoundbank(f);
			theSynthesizer.loadAllInstruments(bank);
			StateMachine.setCurrentSoundset(f.getName());
		}
	}
	
	/**
	 * Loads the passed-in filename from AppData.
	 * 
	 * @param soundset
	 *            The soundfont name
	 * @throws InvalidMidiDataException
	 * @throws IOException
	 * @throws MidiUnavailableException
	 * @since v1.1.2
	 */
	public void loadFromAppData(String soundset) throws InvalidMidiDataException, IOException, MidiUnavailableException {
		//TODO: check linux or mac, choose platform-specific folder
		if(soundset.isEmpty())
			return;
		loadSoundfont(Values.SOUNDFONTS_FOLDER + soundset);
	}

	/**
	 * Copies the soundfont file to AppData.
	 * 
	 * @param path
	 *            Path to the soundfont
	 * @return if soundfont exists in AppData now
	 * @since v1.1.2
	 */
	public boolean addSoundfont(String path) {
		return addSoundfont(new File(path));
	}
	
	/**
	 * Copies the soundfont file to AppData.
	 * 
	 * @param sf
	 *            The soundfont file.
	 * @return if soundfont exists in AppData now
	 * @since v1.1.2
	 */
	public boolean addSoundfont(File sf) {
		String sfName = sf.getName();
		if(sfName.isEmpty())
			return false;
		File destSf = new File(Values.SOUNDFONTS_FOLDER + sfName);
		if(!destSf.exists()) {
			try {
				Files.copy(sf.toPath(), destSf.toPath());
			} catch (IOException e) {
				e.printStackTrace();
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Stores the current soundbank in cache for quick loading. 
	 * 
	 * @since v1.1.2
	 */
	public void storeInCache() {
		String currentSoundset = StateMachine.getCurrentSoundset();
		if(!bankCache.containsKey(currentSoundset))
			bankCache.put(currentSoundset, bank);
	}
	
	/**
	 * Loads the soundset from AppData and stores the soundbank in cache for
	 * quick loading. This will not change the program's current soundbank.
	 * 
	 * @param soundset
	 *            The soundfont name
	 * @throws IOException 
	 * @throws InvalidMidiDataException 
	 * @since v1.1.2
	 */
	public void loadToCache(String soundset) throws InvalidMidiDataException, IOException {
		//TODO: check linux or mac, choose platform-specific folder
		if(soundset.isEmpty())
			return;
		File f = new File(Values.SOUNDFONTS_FOLDER + soundset);
		if(!bankCache.containsKey(soundset)) {
			Soundbank sb = MidiSystem.getSoundbank(f);
			bankCache.put(soundset, sb);
		}
	}

	/**
	 * Loads the Soundbank with the soundset name from bankCache and sets it as
	 * the program's MultiSynthesizer's current soundfont. This is the fast way
	 * to set the program's soundfont.
	 * 
	 * @param soundset
	 *            The soundset name (e.g. soundset3.sf2)
	 * @return if program's current soundset successfully set to soundset
	 * @since v1.1.2
	 */
	public boolean loadFromCache(String soundset) {
		if(StateMachine.getCurrentSoundset().equals(soundset))
			return true;
		if(bankCache.containsKey(soundset)) {
			bank = bankCache.get(soundset);
			theSynthesizer.loadAllInstruments(bank);
			StateMachine.setCurrentSoundset(soundset);
			return true;
		}
		return false;
	}

	/**
	 * Clears the bankCache.
	 * 
	 * @since v1.1.2
	 */
	public void clearCache() {
		bankCache.clear();
	}

	/**
	 * @return The Soundbank cache that holds a map of soundbanks.
	 * @since v1.1.2
	 */
	public static Map<String, Soundbank> getBankCache() {
        return bankCache;
    }

	/**
	 * @return The current MultiSynthesizer that holds a list of Synthesizers.
	 */
	public static Synthesizer getSynth() {
		return theSynthesizer;
    }

    /**
     * @return An Array of references for MidiChannel objects needed to
     * play sounds.
     */
    public static MidiChannel[] getChannels() {
        return chan;
    }

    /**
     * Closes the synthesizers.
     */
    public void close() {
        theSynthesizer.close();
    }

    /**
     * @return A double value between 0 and 1, representing the
     * load status of this class.
     */
    @Override
    public double getLoadStatus() {
        return loadStatus;
    }

    /**
     * Set the load status of the SoundfontLoader.
     * @param d A double value between 0 and 1 that represents the
     * load state of this class.
     */
    @Override
    public void setLoadStatus(double d) {
        if (d >= 0 && d <= 1)
            loadStatus = d;
    }

    /**
     * Plays a certain sound given a Note and some instrument.
     * @param n The Note to play
     * @param i The Instrument to play it with.
     */
    public static void playSound(Note n, InstrumentIndex i) {
        playSound(n.getKeyNum(), i, 0);
    }

    /**
     * Plays a certain sound given a Note and some instrument, along with the
     * accidental we are supposed to play it with.
     * @param i The note index we are supposed to play this note at.
     * @param theInd The InstrumentIndex.
     * @param acc The accidental that we are given.
     */
    public static void playSound(int i, InstrumentIndex theInd, int acc) {
        playSound(i, theInd, acc, Values.MAX_VELOCITY);
    }

    /**
     * Plays a certain sound given a Note and some instrument, along with the
     * accidental we are supposed to play it with and the volume with which we are
     * trying to play at.
     * @param i The note index we are supposed to play this note at.
     * @param theInd The InstrumentIndex.
     * @param acc The accidental that we are given.
     * @param vel The velocity of the note that we are given.
     */
    public static void playSound(int i, InstrumentIndex theInd, int acc, int vel) {
        int ind = theInd.getChannel() - 1;
        chan[ind].noteOn(i + acc, vel);
    }

    /**
     * Stops a certain sound given a Note and some instrument, along with the
     * accidental we are supposed to play it with and the volume with which we are
     * trying to play at.
     * @param i The note index we are supposed to play this note at.
     * @param theInd The InstrumentIndex.
     * @param acc The accidental that we are given.
     */
    public static void stopSound(int i, InstrumentIndex theInd, int acc) {
        int ind = theInd.getChannel() - 1;
        chan[ind].noteOff(i + acc);
    }

    /**
     * Stops a certain sound given a Note and some instrument, along with the
     * accidental we are supposed to play it with and the volume with which we are
     * trying to play at.
     * @param i The note index we are supposed to play this note at.
     * @param theInd The InstrumentIndex.
     * @param acc The accidental that we are given.
     * @param vel The note-off velocity.
     */
    public static void stopSound(int i, InstrumentIndex theInd, int acc, int vel) {
        int ind = theInd.getChannel() - 1;
        chan[ind].noteOff(i + acc, vel);
    }

}
<file_sep>package smp;

import java.io.File;
import java.util.ArrayList;
import java.util.EnumSet;

import com.sun.javafx.application.LauncherImpl;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.application.Preloader.ProgressNotification;
import javafx.application.Preloader.StateChangeNotification;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos;
import javafx.scene.ImageCursor;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.WindowEvent;
import smp.components.Values;
import smp.components.InstrumentIndex;
import smp.components.staff.StaffInstrumentEventHandler;
import smp.fx.SMPFXController;
import smp.fx.SplashScreen;
import smp.stateMachine.ProgramState;
import smp.stateMachine.Settings;
import smp.stateMachine.StateMachine;

/**
 * Super Mario Paint <br>
 * Based on the old SNES game from 1992, Mario Paint <br>
 * Inspired by:<br>
 * MarioSequencer (2002) <br>
 * TrioSequencer <br>
 * <NAME>'s Mario Paint Composer 1.0 / 2.0 (2007-2008) <br>
 * FordPrefect's Advanced Mario Sequencer (2009) <br>
 * The GUI is primarily written with JavaFX <br>
 *
 * Dev team: 
 * RehdBlob (2012 - current)
 * j574y923 (2017 - current)
 * CyanSMP64 (2019 - current)
 * seymour (2020 - current)
 *
 * @author RehdBlob
 * @author j574y923
 * @author CyanSMP64
 * @author seymour
 * 
 * @since 2012.08.16
 * @version 1.4.2
 */
public class SuperMarioPaint extends Application {

    /**
     * Location of the Main Window fxml file.
     */
    private String mainFxml = "./MainWindow.fxml";

    /**
     * Location of the Advanced Mode (super secret!!) fxml file.
     */
    @SuppressWarnings("unused")
    private String advFxml = "./AdvWindow.fxml";

    /**
     * The number of threads that are running to load things for Super Mario
     * Paint.
     */
    private static final int NUM_THREADS = 2;

    /**
     * Loads all the sprites that will be used in Super Mario Paint.
     */
    private Loader imgLoader = new ImageLoader();

    /**
     * Loads the soundfonts that will be used in Super Mario Paint.
     */
    private Loader sfLoader = new SoundfontLoader();

    /** Image Loader thread. */
    private Thread imgLd;

    /** Soundfont loader thread. */
    private Thread sfLd;

    /** This is the main application stage. */
    private Stage primaryStage;

    /** This is the primary Scene on the main application stage. */
    private Scene primaryScene;

    /** This is the loaded FXML file. */
    private Parent root;

    /**
     * The controller class for the FXML.
     */
    private SMPFXController controller = new SMPFXController();

    /** Whether we are done loading the application or not. */
    private BooleanProperty ready = new SimpleBooleanProperty(false);

    /**
     * This should hopefully get something up on the screen quickly. This is
     * taken from http://docs.oracle.com/javafx/2/deployment/preloaders.htm
     */
    private void longStart() {

        // long init in background
        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                sfLd.start();
                imgLd.start();
                do {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    double imgStatus = imgLoader.getLoadStatus();
                    double sfStatus = sfLoader.getLoadStatus();
                    double ld = (imgStatus + sfStatus) * 100 / NUM_THREADS
                            * 0.5;
                    notifyPreloader(new ProgressNotification(ld));
                } while (imgLd.isAlive() || sfLd.isAlive());
                FXMLLoader loader = new FXMLLoader();
                loader.setController(controller);
                loader.setLocation(new File(mainFxml).toURI().toURL());
                root = (Parent) loader.load();
                notifyPreloader(new ProgressNotification(0.75));
                ready.setValue(Boolean.TRUE);
                return null;
            }
        };
        new Thread(task).start();
    }

    /** Explicitly create constructor without arguments. */
    public SuperMarioPaint() {

    }

    /**
     * Starts three <code>Thread</code>s. One of them is currently a dummy
     * splash screen, the second an <code>ImageLoader</code>, and the third one
     * a <code>SoundfontLoader</code>.
     *
     * @see ImageLoader
     * @see SoundfontLoader
     * @see SplashScreen
     */
    @Override
    public void init() {
        imgLd = new Thread(imgLoader);
        sfLd = new Thread(sfLoader);
        controller.setImageLoader((ImageLoader) imgLoader);
        controller.setSoundfontLoader((SoundfontLoader) sfLoader);
    }
    
    /**
     * Starts the application and loads the FXML file that contains a lot of the
     * class hierarchy.
     *
     * @param primaryStage
     *            The primary stage that will be showing the main window of
     *            Super Mario Paint.
     */
    @Override
    public void start(Stage ps) {
        primaryStage = ps;

        longStart();
        

        ready.addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> ov,
                    Boolean t, Boolean t1) {
                if (Boolean.TRUE.equals(t1)) {
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                primaryStage.setTitle("Super Mario Paint " + Settings.version);
                                setupCloseBehaviour(primaryStage);
                                primaryStage.setResizable(false);
                                primaryStage.setHeight(Values.DEFAULT_HEIGHT);
                                primaryStage.setWidth(Values.DEFAULT_WIDTH);
                                primaryScene = new Scene(root, 1024, 768);
                                primaryStage.setScene(primaryScene);
                                makeKeyboardListeners(primaryScene);
                                notifyPreloader(new ProgressNotification(1));
                                notifyPreloader(new StateChangeNotification(
                                        StateChangeNotification.Type.BEFORE_START));
                                
                                // @since 2020.4.28 - seymour
                                // Changes the cursor image
                                setCursor(0);
                                
                                // Changes the app icon (gives the option to use a given icon OR a random icon from all instruments)
                                if (new File("./sprites/ICON.png").exists())
                                	setIcon("./sprites/ICON.png");
                                else {
	                                int randNum = (int) Math.floor(Math.random() * Values.NUMINSTRUMENTS);
	                                ArrayList<InstrumentIndex> instList = new ArrayList<InstrumentIndex>(
	                                		EnumSet.allOf(InstrumentIndex.class));
	                                String instName = instList.get(randNum).name();
	                                setIcon("./sprites/" + instName + "_SM.png");
                                }
                                
                                primaryStage.show();
                            } catch (Exception e) {
                                e.printStackTrace();
                                System.exit(1);
                            }
                        }
                    });
                }
            }
        });
        ;
    }

    /**
     * Protip for JavaFX users: Make sure you define your application close
     * behavior.
     */
    @Override
    public void stop() {
//    	Platform.exit();
        System.exit(0);
    }

    /**
     * Got this off of https://community.oracle.com/thread/2247058?tstart=0 This
     * appears quite useful as a 'really exit?' type thing. This dialog
     * currently needs some work, so we're not going to include it in the alpha
     * release.
     *
     * @param primaryStage
     *            The main stage of interest.
     */
    private void setupCloseBehaviour(final Stage primaryStage) {
        primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {

            @Override
            public void handle(WindowEvent event) {
                if (StateMachine.isSongModified()
                        || StateMachine.isArrModified()) {
                    final Stage dialog = new Stage();
                    dialog.setHeight(100);
                    dialog.setWidth(300);
                    dialog.setResizable(false);
                    dialog.initStyle(StageStyle.UTILITY);
                    Label label = new Label();
                    label.setMaxWidth(300);
                    label.setWrapText(true);
                    if (StateMachine.isSongModified()
                            && StateMachine.isArrModified()) {
                        label.setText("The song and arrangement have\n"
                                + "both not been saved! Really exit?");
                    } else if (StateMachine.isSongModified()) {
                        label.setText("The song has not been saved! "
                                + "Really exit?");
                    } else if (StateMachine.isArrModified()) {
                        label.setText("The arrangement has not been saved! "
                                + "Really exit?");
                    }
                    Button okButton = new Button("Yes");
                    okButton.setOnAction(new EventHandler<ActionEvent>() {

                        @Override
                        public void handle(ActionEvent event) {
                            dialog.close();
                            stop();

                        }
                    });
                    Button cancelButton = new Button("No");
                    cancelButton.setOnAction(new EventHandler<ActionEvent>() {

                        @Override
                        public void handle(ActionEvent event) {
                            dialog.close();
                        }
                    });
                    FlowPane pane = new FlowPane(10, 10);
                    pane.setAlignment(Pos.CENTER);
                    pane.getChildren().addAll(okButton, cancelButton);
                    VBox vBox = new VBox(10);
                    vBox.setAlignment(Pos.CENTER);
                    vBox.getChildren().addAll(label, pane);
                    Scene scene1 = new Scene(vBox);
                    dialog.setScene(scene1);
                    dialog.show();
                } else {
                    stop();
                }
                event.consume();
            }
        });
    }
    
    /**
     * Creates the keyboard listeners that we will be using for various other
     * portions of the program. Ctrl, alt, and shift are of interest here, but
     * the arrow keys will also be considered.
     *
     * @param primaryScene
     *            The main window.
     */
    private void makeKeyboardListeners(Scene primaryScene) {
    	
    	primaryScene.addEventHandler(MouseEvent.ANY, controller.getStaffInstrumentEventHandler());
    	
    	ArrayList<MouseButton> mouseButtons = new ArrayList<MouseButton>();
    	
    	// Just a temporary thing to change mouse until i (or someone else) can find out where to put it =P -- seymour
    	primaryScene.addEventHandler(MouseEvent.MOUSE_PRESSED,
    			new EventHandler<MouseEvent>() {
    				
    				@Override
    				public void handle(MouseEvent m) {
    					if (!mouseButtons.contains(m.getButton()))
    						mouseButtons.add(m.getButton());
    					
    					if (mouseButtons.contains(MouseButton.MIDDLE) || (StateMachine.isClipboardPressed() && m.getButton() != MouseButton.SECONDARY))
		    				setCursor(2);
		    			else if (mouseButtons.contains(MouseButton.PRIMARY))
		    				setCursor(1);
		    			else if (mouseButtons.contains(MouseButton.SECONDARY) && !StateMachine.isClipboardPressed())
		    				setCursor(3);
    					
    					m.consume();
    				}
    	});
    	primaryScene.addEventHandler(MouseEvent.MOUSE_RELEASED,
    			new EventHandler<MouseEvent>() {
    		
		    		@Override
					public void handle(MouseEvent m) {
		    			// Added to remove the default cursor appearing while other mouse buttons are held
		    			mouseButtons.remove(m.getButton());
		    			
		    			if (mouseButtons.contains(MouseButton.MIDDLE) || (StateMachine.isClipboardPressed() && m.getButton() != MouseButton.SECONDARY))
		    				setCursor(2);
		    			else if (mouseButtons.contains(MouseButton.PRIMARY))
		    				setCursor(1);
		    			else if (mouseButtons.contains(MouseButton.SECONDARY) && !StateMachine.isClipboardPressed())
		    				setCursor(3);
		    			
		    			if (mouseButtons.isEmpty())
							setCursor(0);
						
						m.consume();
					}
    	});
    	
    	// TODO: move to its own keyhandler
        primaryScene.addEventHandler(KeyEvent.KEY_PRESSED,
                new EventHandler<KeyEvent>() {

                    @Override
                    public void handle(KeyEvent ke) {
                    	
                    	switch(ke.getCode()) {
                    	case PAGE_UP:
                    		controller.getStaff().shift(-Values.NOTELINES_IN_THE_WINDOW);
                    		break;
                    	case PAGE_DOWN:
                    		controller.getStaff().shift(Values.NOTELINES_IN_THE_WINDOW);
                    		break;
                    	case HOME:
                    		if(ke.isControlDown())
                    			controller.getStaff().setLocation(0);
                    		break;
                    	case END:
                    		if(ke.isControlDown())
                    			controller.getStaff().setLocation((int)controller.getScrollbar().getMax());
                    		break;
                    	// @since v1.4, adds A and D as controls. move to correct spot later if needed
                    	case A:
                    		if(!ke.isControlDown() && !ke.isShiftDown())
                    			controller.getStaff().moveLeft();
                    	// @since v1.1.2, requested by seymour
                    	case LEFT:
                    		if (controller.getNameTextField().focusedProperty().get()) // Don't trigger while typing name
                				break;
                    		if(ke.isControlDown() && ke.isShiftDown())
                    			controller.getStaff().shift(-4);
                    		if((ke.isControlDown() && ke.getCode() != KeyCode.A) || ke.isShiftDown())
                    			controller.getStaff().shift(-4);
                    		break;
                    	case D:
                    		if(!ke.isControlDown() && !ke.isShiftDown())
                    			controller.getStaff().moveRight();
                    	case RIGHT:
                    		if (controller.getNameTextField().focusedProperty().get()) // Don't trigger while typing name
                    			break;
                    		if(ke.isControlDown() && ke.isShiftDown())
                    			controller.getStaff().shift(4);
                    		if(ke.isControlDown() || ke.isShiftDown())
                    			controller.getStaff().shift(4);
                    		break;
                    		
                    	// @since 1.4, adds conventional spacebar functionality 
                    	// TODO: Make this better please =)
                    	case SPACE:
                    		if (controller.getNameTextField().focusedProperty().get()) // Don't trigger while typing name
                    			break;
                    		
                    		if (ke.isControlDown() || ke.isShiftDown())
                    			controller.getStaff().setLocation(0);
                    		
                			if (StateMachine.getState() == ProgramState.SONG_PLAYING) {
                	            StateMachine.setState(ProgramState.EDITING);
                        		controller.getStaff().stopSong();
                	        } else if (StateMachine.getState() == ProgramState.ARR_PLAYING) {
                	            StateMachine.setState(ProgramState.ARR_EDITING);
                	            controller.getStaff().stopSong();
                	        } else if (StateMachine.getState() == ProgramState.EDITING) {
                	            StateMachine.setState(ProgramState.SONG_PLAYING);
                	            controller.getStaff().startSong();
                	        } else if (StateMachine.getState() == ProgramState.ARR_EDITING) {
                	            StateMachine.setState(ProgramState.ARR_PLAYING);
                	            controller.getStaff().startArrangement();
                	        }
                    		break;
                    	default:
                    	}
                    	
                        StateMachine.getButtonsPressed().add(ke.getCode());
                        StaffInstrumentEventHandler.updateAccidental();
                        ke.consume();
                    }
                });

        primaryScene.addEventHandler(KeyEvent.KEY_RELEASED,
                new EventHandler<KeyEvent>() {

                    @Override
                    public void handle(KeyEvent ke) {
                        StateMachine.getButtonsPressed().remove(ke.getCode());
                        StaffInstrumentEventHandler.updateAccidental();
                        ke.consume();
                    }
                });

        //put this into StateMachine?
		primaryScene.addEventHandler(ScrollEvent.ANY, new EventHandler<ScrollEvent>() {

			@Override
			public void handle(ScrollEvent se) {
				if (se.getDeltaY() < 0) {
//					if (se.isControlDown())
//						controller.getStaff().setLocation((int) controller.getScrollbar().getValue() + 4);
//					else
						controller.getStaff().moveRight();
				} else if (se.getDeltaY() > 0) {
//					if (se.isControlDown())
//						controller.getStaff().setLocation((int) controller.getScrollbar().getValue() - 4);
//					else
						controller.getStaff().moveLeft();
				}
				se.consume();
			}
		});
        
        primaryStage.focusedProperty().addListener(
                new ChangeListener<Boolean>() {
                    @Override
                    public void changed(ObservableValue<? extends Boolean> ov,
                            Boolean t, Boolean t1) {
                        StateMachine.clearKeyPresses();
                    }
                });

    }

    /**
     * Launches the application.
     *
     * @param args
     *            Sets debug options on or off.
     */
    public static void main(String[] args) {
        if (args.length == 0) {
            try {
                LauncherImpl.launchApplication(SuperMarioPaint.class,
                        SplashScreen.class, args);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if (args.length > 0
                && (args[0].equals("--debug") || args[0].equals("--d"))) {
            if (args[1] != null) {
                try {
                    Settings.setDebug(Integer.parseInt(args[1]));
                } catch (NumberFormatException e) {
                    Settings.setDebug(1);
                }
            } else {
                Settings.setDebug(1);
            }
            try {
                LauncherImpl.launchApplication(SuperMarioPaint.class,
                        SplashScreen.class, args);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

	}
    
    /**
     * Changes the cursor image.
     * TYPES:
     * 0 - Default
     * 1 - Grab
     * 2 - Open (splayed)
     * 3 - Eraser
     * 
     * @param isPressed
     * 			Which mouse button is being pressed (if any).
     */
    public void setCursor(int type) {
        Platform.runLater(new Runnable() {

            @Override
            public void run() {
                ImageCursor im = ((ImageLoader) imgLoader).getCursor(type);
                if (im != null) {
                    primaryScene.setCursor(im);
                }
            }
            
        });
    }
    
    /**
     * Changes the icon to the given image location.
     * 
     * @param fileLocation
     * 			The location of the new icon image.
     */
    public void setIcon(String fileLocation) {
    	if (new File(fileLocation).exists())
    		primaryStage.getIcons().add(new Image("file:" + fileLocation));
    }

	/**
	 * Gets the soundfont Loader. This function will probably only be temporary
	 * if we plan to move the loader to another class.
	 * 
	 * @return the soundfont Loader
	 */
	public Loader getSoundfontLoader() {
    	return sfLoader;
    }
}
<file_sep>package smp.presenters.options;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.CheckBox;
import smp.models.stateMachine.Variables;

public class BindSoundfontPresenter {

	//TODO: auto-add these model comments
	//====Models====
	private BooleanProperty optionsBindSoundfont;
	private StringProperty optionsBindedSoundfont;
	
	private CheckBox bindSoundfont;
	
	public BindSoundfontPresenter(CheckBox bindSoundfont) {
		this.bindSoundfont = bindSoundfont;
		this.optionsBindSoundfont = Variables.optionsBindSoundfont;
		this.optionsBindedSoundfont = Variables.optionsBindedSoundfont;
		
		this.bindSoundfont.selectedProperty().bindBidirectional(optionsBindSoundfont);
		if (optionsBindedSoundfont.get().equals(Variables.optionsCurrentSoundfont.get()))
			this.bindSoundfont.setSelected(true);
		this.bindSoundfont.selectedProperty().addListener(new ChangeListener<Boolean>() {
			public void changed(ObservableValue<? extends Boolean> ov, Boolean oldVal, Boolean checked) {
				if(checked) {
					optionsBindedSoundfont.set(Variables.optionsCurrentSoundfont.get());
				} else {
					optionsBindedSoundfont.set("");
				}
			}
		});
	}
}
<file_sep>package smp.components.buttons;

import javafx.event.EventHandler;
import javafx.scene.control.Slider;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import smp.ImageLoader;
import smp.components.Values;
import smp.components.general.ImagePushButton;
import smp.components.staff.sequences.StaffArrangement;
import smp.components.staff.sequences.StaffSequence;
import smp.fx.Dialog;
import smp.fx.SMPFXController;
import smp.stateMachine.ProgramState;
import smp.stateMachine.StateMachine;

/**
 * This is the button that creates a new song.
 *
 * @author RehdBlob
 * @since 2013.12.18
 *
 */
public class NewButton extends ImagePushButton {

    /**
     * Default constructor.
     *
     * @param i
     *            This is the <code>ImageView</code> object that will house the
     *            Load button.
     * @param ct
     *            The FXML controller object.
     * @param im
     *            The Image loader object.
     */
    public NewButton(ImageView i, SMPFXController ct, ImageLoader im) {
        super(i, ct, im);
        
        // @since v1.4 to accomodate for those with a smaller screen that may not be able to access it.
  		ct.getBasePane().addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {

  			@Override
  			public void handle(KeyEvent event) {
				if (controller.getNameTextField().focusedProperty().get()) return; // Disable while textfield is focused
  				if(event.isControlDown() && event.getCode() == KeyCode.N)
  					reactPressed(null);
  			}
  		});
    }

    @Override
    protected void reactPressed(MouseEvent event) {
        ProgramState curr = StateMachine.getState();
        if (curr == ProgramState.EDITING)
            newSong();
        else if (curr == ProgramState.ARR_EDITING)
            newArrangement();
    }

    @Override
    protected void reactReleased(MouseEvent event) {
        // do nothing.
    }

    /**
     * Creates a new song and clears the staff of all notes. Make sure you save
     * your song first! The action is ignored if the song is playing.
     */
    private void newSong() {
        boolean cont = true;
        if (StateMachine.isSongModified())
            cont = Dialog
                    .showYesNoDialog("The current song has been modified!\n"
                            + "Create a new song anyway?");

        if (cont) {
            theStaff.setSequence(new StaffSequence());
            theStaff.setSequenceFile(null);
            Slider sc = theStaff.getControlPanel().getScrollbar();
            sc.setValue(0);
            sc.setMax(Values.DEFAULT_LINES_PER_SONG - 10);
            ArrowButton.setEndOfFile(false);
            theStaff.getNoteMatrix().redraw();
            controller.getNameTextField().clear();
            StateMachine.setSongModified(false);
        }
    }

    /**
     * Creates a new arrangement and clears the staff of all notes. Make sure
     * you save your arrangement first! The action is ignored if an arrangement
     * is playing.
     */
    private void newArrangement() {
        boolean cont = true;
        if (StateMachine.isArrModified()) {
            cont = Dialog
                    .showYesNoDialog("The current arrangement has been\n"
                            + "modified! Create a new arrangement\nanyway?");
        }
        if (cont) {
            theStaff.setArrangement(new StaffArrangement());
            theStaff.setArrangementFile(null);
            controller.getNameTextField().clear();
            theStaff.getArrangementList().getItems().clear();
            StateMachine.setArrModified(false);
        }
    }

}
<file_sep>package smp.components.staff;

import java.util.ArrayList;

import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.scene.Node;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import smp.ImageIndex;
import smp.ImageLoader;
import smp.components.Values;
import smp.components.staff.sequences.StaffNoteLine;
import smp.fx.SMPFXController;

/**
 * Wrapper class for all of the images that appear on the Staff of Super Mario
 * Paint. Contains the sprite holders for the images that represent notes, the
 * ImageView holders for each of the measure lines, the lines that allow one to
 * display notes higher than a G or lower than a D on the staff, and the measure
 * numbers.
 *
 * @author RehdBlob
 * @since 2012.09.17
 */
public class StaffImages {

    /**
     * The ArrayList that holds the ImageView objects that the measure lines
     * object holds.
     */
    private ArrayList<ImageView> measureLines;

    /**
     * The ArrayList that holds the Text objects that will hold the measure line
     * numbers.
     */
    private ArrayList<Text> measureNums;

    /** These are the bars that highlight notes. */
    private ArrayList<ImageView> staffPlayBars;

    /** This is the FXML controller class. */
    private SMPFXController controller;

    /** This is the ImageLoader class. */
    private ImageLoader il;

    /** The ledger lines. */
    private HBox[] staffLLines;

    /** The ledger lines at the high C of the staff. */
    private ArrayList<Node> highC;

    /** The ledger lines at the high A of the staff. */
    private ArrayList<Node> highA;

    /** The ledger lines at the low C of the staff. */
    private ArrayList<Node> lowC;

    /** The ledger lines at the low A of the staff. */
    private ArrayList<Node> lowA;

    /**
     * This is the list of lists of the ledger lines, so that we can quickly
     * hide everything using a loop, if necessary.
     */
    private ArrayList<ArrayList<Node>> theLLines = new ArrayList<ArrayList<Node>>();

    /**
     * The parent staff object.
     */
    private Staff theStaff;

    /**
     * Constructor that also sets up the staff ledger lines.
     */
    public StaffImages(ImageLoader i) {
        il = i;
    }

    /**
     * Instantiates this wrapper class with the correct HBox objects such that
     * it can begin keeping track of whatever's happening on the staff, at least
     * on the measure lines side.
     */
    public void initialize() {

        initializeStaffMeasureLines(controller.getStaffMeasureLines());
        initializeStaffPlayBars(controller.getStaffPlayBars());
        initializeStaffMeasureNums(controller.getStaffMeasureNums());
        initializeStaffLedgerLines();
        initializeStaffInstruments(controller.getStaffInstruments(),
                controller.getStaffAccidentals());
        initializeVolumeBars(controller.getVolumeBars());
        initializeVolumeBarLinks();
    }
    /**
     * Sets up the links between the volume bars display and StaffNoteLines.
     */
    private void initializeVolumeBarLinks() {
        for (int i = 0; i < Values.NOTELINES_IN_THE_WINDOW; i++) {
            StaffVolumeEventHandler sveh = theStaff.getNoteMatrix()
                    .getVolHandler(i);
            StaffNoteLine stl = theStaff.getSequence().getLine(i);
            sveh.setStaffNoteLine(stl);
        }
    }

    /**
     * Initializes the volume bars in the program.
     *
     * @param volumeBars
     *            This is the HBox that holds all of the volume bar
     *            <code>StackPane</code> objects.
     */
    private void initializeVolumeBars(HBox volumeBars) {
        ArrayList<StackPane> vol = new ArrayList<StackPane>();
        for (Node v : volumeBars.getChildren()) {
            StackPane volBar = (StackPane) v;
            vol.add(volBar);
            StaffVolumeEventHandler sveh = new StaffVolumeEventHandler(volBar,
                    il, controller.getModifySongManager());
            volBar.addEventHandler(Event.ANY, sveh);
            theStaff.getNoteMatrix().addVolHandler(sveh);
        }
        theStaff.getNoteMatrix().setVolumeBars(vol);

    }

    /**
     * These are the numbers above each successive measure.
     */
    private void initializeStaffMeasureNums(HBox mNums) {
        ArrayList<HBox> measureNumBoxes = new ArrayList<HBox>();
        measureNums = new ArrayList<Text>();
        for (Node num : mNums.getChildren())
            measureNumBoxes.add((HBox) num);
        int counter = 1;
        for (int i = 0; i < measureNumBoxes.size(); i++) {
            HBox theBox = measureNumBoxes.get(i);
            Text t = new Text();
            theBox.getChildren().add(t);
            measureNums.add(t);
            if (i % Values.TIMESIG_BEATS == 0) {
                t.setText(String.valueOf(counter));
                counter++;
            } else
                continue;
        }
    }

    /**
     * Sets up the various note lines of the staff. These are the notes that can
     * appear on the staff. This method also sets up sharps, flats, etc.
     *
     * @param accidentals
     *            The HBox that holds the framework for the sharps / flats.
     * @param instruments
     *            The HBox that holds the framework for the instruments.
     */
    private void initializeStaffInstruments(HBox instruments, HBox accidentals) {
        NoteMatrix staffMatrix = theStaff.getNoteMatrix();

        ArrayList<VBox> accidentalLines = new ArrayList<VBox>();
        for (Node n : accidentals.getChildren())
            accidentalLines.add((VBox) n);

        ArrayList<VBox> noteLines = new ArrayList<VBox>();
        for (Node n : instruments.getChildren())
            noteLines.add((VBox) n);

        for (int line = 0; line < noteLines.size(); line++) {
            VBox verticalHolder = noteLines.get(line);
            VBox accVerticalHolder = accidentalLines.get(line);

            ObservableList<Node> lineOfNotes = verticalHolder.getChildren();
            ObservableList<Node> lineOfAcc = accVerticalHolder.getChildren();

            ArrayList<StackPane> notes = new ArrayList<StackPane>();
            ArrayList<StackPane> accs = new ArrayList<StackPane>();

            for (int pos = 1; pos <= Values.NOTES_IN_A_LINE; pos++) {
                StackPane note = (StackPane) lineOfNotes.get(pos - 1);
                StackPane acc = (StackPane) lineOfAcc.get(pos - 1);
                notes.add(note);
                accs.add(acc);
            }

            staffMatrix.addLine(notes);
            staffMatrix.addAccLine(accs);
        }
    }

    /**
     * These are the lines that divide up the staff.
     *
     * @param staffMLines
     *            The measure lines that divide the staff.
     */
    private void initializeStaffMeasureLines(HBox mLines) {
        measureLines = new ArrayList<ImageView>();
        for (Node n : mLines.getChildren())
            measureLines.add((ImageView) n);
        for (int i = 0; i < measureLines.size(); i++) {
            if (i % Values.TIMESIG_BEATS == 0)
                measureLines.get(i).setImage(
                        il.getSpriteFX(ImageIndex.STAFF_MLINE));
            else
                measureLines.get(i).setImage(
                        il.getSpriteFX(ImageIndex.STAFF_LINE));
        }
    }

    /**
     * Redraws the staff measure lines and numbers.
     *
     * @param currLine
     *            The current line that we are on.
     */
    public void updateStaffMeasureLines(int currLine) {
        int counter = 0;
        for (int i = 0; i < measureLines.size(); i++) {
            ImageView currImage = measureLines.get(i);
            Text currText = measureNums.get(i);
            if ((currLine + i) % Values.TIMESIG_BEATS == 0) {
                currImage.setImage(il.getSpriteFX(ImageIndex.STAFF_MLINE));
                currText.setText(String.valueOf((int) (Math.ceil(currLine
                        / (double) Values.TIMESIG_BEATS) + 1 + counter)));
                counter++;
            } else {
                currImage.setImage(il.getSpriteFX(ImageIndex.STAFF_LINE));
                currText.setText("");
            }
        }
    }

    /**
     * Sets up the note highlighting functionality.
     *
     * @param staffPlayBars
     *            The bars that move to highlight different notes.
     */
    private void initializeStaffPlayBars(HBox playBars) {
        staffPlayBars = new ArrayList<ImageView>();
        for (Node n : playBars.getChildren()) {
            ImageView i = (ImageView) n;
            i.setImage(il.getSpriteFX(ImageIndex.PLAY_BAR1));
            i.setVisible(false);
            staffPlayBars.add(i);
        }
    }

    /**
     * Sets up the staff expansion lines, which are to hold notes that are
     * higher than or lower than the regular lines of the staff.
     *
     * @param staffLLines
     *            An array of ledger lines. This method expects that there will
     *            be four of these, two of which indicate the lines above the
     *            staff and the other of which indicating the lines below the
     *            staff.
     */
    private void initializeStaffLedgerLines() {
        highC = new ArrayList<Node>();
        highA = new ArrayList<Node>();
        lowC = new ArrayList<Node>();
        lowA = new ArrayList<Node>();
        highC.addAll(staffLLines[0].getChildren());
        highA.addAll(staffLLines[1].getChildren());
        lowC.addAll(staffLLines[2].getChildren());
        lowA.addAll(staffLLines[3].getChildren());
        theLLines.add(highC);
        theLLines.add(highA);
        theLLines.add(lowC);
        theLLines.add(lowA);
        hideAllLedgerLines();
    }

    /**
     * Hides all of the ledger lines.
     */
    public void hideAllLedgerLines() {

        for (ArrayList<Node> n : theLLines)
            for (Node nd : n)
                nd.setVisible(false);

    }

    /**
     * Sets the parent staff object to the specified object.
     *
     * @param s
     *            The pointer to the parent staff object.
     */
    public void setStaff(Staff s) {
        theStaff = s;
    }

    /**
     * @return The list of <code>ImageView</code> objects that holds the bars
     *         that will highlight the notes that we are playing on the staff.
     */
    public ArrayList<ImageView> getPlayBars() {
        return staffPlayBars;
    }

    /**
     * Sets the controller class.
     *
     * @param ct
     *            The FXML controller class.
     */
    public void setController(SMPFXController ct) {
        controller = ct;
    }

    /**
     * @param l
     *            The array of <code>HBox</code>es that contains the rectangles
     *            for the ledger lines.
     */
    public void setLedgerLines(HBox[] l) {
        staffLLines = l;
    }

    /**
     * @return The array of <code>HBox</code>es that contains the rectangles for
     *         the ledger lines.
     */
    public HBox[] getLedgerLines() {
        return staffLLines;
    }

    /** @return The ledger lines at position high C. */
    public ArrayList<Node> highC() {
        return highC;
    }

    /** @return The ledger lines at position high A. */
    public ArrayList<Node> highA() {
        return highA;
    }

    /** @return The ledger lines at position low C. */
    public ArrayList<Node> lowC() {
        return lowC;
    }

    /** @return The ledger lines at position low C. */
    public ArrayList<Node> lowA() {
        return lowA;
    }

}
<file_sep>package smp.components.staff.sequences;

/**
 * A Bookmark that is placed at some location on the staff, like in
 * Advanced Mario Sequencer.
 * @author RehdBlob
 * @since 2012.09.17
 */
public class Bookmark extends AbstractStaffEvent {

    /**
     * Generated serial ID.
     */
    private static final long serialVersionUID = -5083645250328720866L;

    /** Currently does nothing. */
    public Bookmark(int num) {
        super(num);
    }

}
<file_sep>package smp.models.staff;

/**
 * Keeps track of the different types of staff events.
 * @author RehdBlob
 * @since 2013.03.29
 */
public enum StaffEventType {

    /** A note. */
    NOTE,

    /** This changes the tempo of the song. */
    SPEEDMARK,

    /**
     * This is a bookmark that keeps the
     * place of whatever it is the user wants.
     */
    BOOKMARK;

}
<file_sep>package smp.commandmanager;

import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;

import smp.components.Values;

/**
 * A command manager responsible for recording commands, undoing, and redoing
 * commands.
 * 
 * Several commands can be executed then recorded into a list and, when undoing
 * or redoing, will undo or redo all those commands together.
 * 
 * @author J
 *
 */
public class CommandManager {

	protected Deque<List<CommandInterface>> undoStack;
	protected Deque<List<CommandInterface>> redoStack;

	private List<CommandInterface> nextCommands;
	
	public CommandManager() {
		undoStack = new LinkedBlockingDeque<>(Values.MAX_UNDO_REDO_SIZE);
		redoStack = new LinkedBlockingDeque<>(Values.MAX_UNDO_REDO_SIZE);
	}

	public void undo() {
		//make sure any current action is recorded before undoing
		record();
		
		if (undoStack.isEmpty())
			return;

		List<CommandInterface> commands = undoStack.pop();
		//undo needs reverse order on the commands
		for(int i = commands.size() - 1; i >= 0; i--) {
			CommandInterface command = commands.get(i);
			command.undo();
		}

		redoStack.push(commands);
		System.out.println("...undo..." + undoStack.size() + "/" + (undoStack.size() + redoStack.size()));
	}

	public void redo() {
		if (redoStack.isEmpty())
			return;

		List<CommandInterface> commands = redoStack.pop();
		for(CommandInterface command : commands) {
			command.redo();
		}

		undoStack.push(commands);
		System.out.println("...redo..." + undoStack.size() + "/" + (undoStack.size() + redoStack.size()));
	}

	/**
	 * This does not literally execute the command. It will put the command into
	 * a list then you can call record() to put the command list onto the
	 * undoStack.
	 * 
	 * @param command
	 */
	public void execute(CommandInterface command) {

		if (nextCommands == null)
			nextCommands = new ArrayList<>();
		
		nextCommands.add(command);
	}
	
	/**
	 * record the commands given to execute()
	 */
	public void record() {		
		if (nextCommands == null)
			return;
		if (undoStack.size() >= Values.MAX_UNDO_REDO_SIZE)
			undoStack.removeLast();
		
		undoStack.push(nextCommands);
		redoStack.clear();
		
		nextCommands = null;
	}
	
	public void reset() {
		undoStack.clear();
		redoStack.clear();
	}
}
<file_sep>package smp.presenters.buttons;

import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Tooltip;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import smp.ImageIndex;
import smp.models.stateMachine.ProgramState;
import smp.models.stateMachine.StateMachine;
import smp.presenters.api.button.ImageToggleButton;

/**
 * Wrapper class for an ImageView that holds the stop button image. Pressing the
 * button changes the image and also changes the state of the program.
 *
 * @author RehdBlob
 * @author j574y923
 * 
 * @since 2018.01.14
 */
public class ClipboardButtonPresenter extends ImageToggleButton {

	//TODO: auto-add these model comments
	//====Models====
    private ObjectProperty<ProgramState> programState;

	/**
     * Instantiates the clipboard selection button.
     *
     * @param clipboardButton
     *            The ImageView that will be manipulated by this class.
     * @param ct
     *            The FXML controller object.
     * @param im
     *            The Image loader object.
     */
	public ClipboardButtonPresenter(ImageView clipboardButton) {
		super(clipboardButton);
		getImages(ImageIndex.CLIPBOARD_PRESSED, ImageIndex.CLIPBOARD_RELEASED);
		releaseImage();
		Tooltip.install(clipboardButton, new Tooltip("Click (or Shift+R) to toggle region selection\n"
				+ "Hover over instrument & press F to filter instrument\n"
				+ "Ctrl+A to select all\n"
				+ "Ctrl+C to copy notes\n"
				+ "Ctrl+V to paste notes\n"
				+ "Ctrl+X to cut notes\n"
				+ "Delete to delete notes\n"
				+ "Alt+N to toggle notes selection\n"
				+ "Alt+V to toggle volumes selection"));
		
		// TODO: create getClipboardButton() somewhere so adding a hotkey can be done elsewhere
//		ct.getBasePane().addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
//
//			@Override
//			public void handle(KeyEvent event) {
//				if(event.isShiftDown() && event.getCode() == KeyCode.R)
//					reactPressed(null);
//			}
//		});
		this.programState = StateMachine.getState();
		setupViewUpdater();
	}

    private void setupViewUpdater() {
    	this.programState.addListener(new ChangeListener<Object>() {

			@Override
			public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
				if (newValue.equals(ProgramState.EDITING))
					theImage.setVisible(true);
				else if (newValue.equals(ProgramState.ARR_EDITING))
					theImage.setVisible(false);
			}
		});
	}

	@Override
    public void reactPressed(MouseEvent e) {
        if (isPressed) {
            isPressed = false;
            releaseImage();
    		StateMachine.setClipboardPressed(false);
        	//TODO: staff, volume presenter
    		//TODO: make clipboard api more intuitive to mvp
    		this.controller.getStaffInstruments().setMouseTransparent(false);
    		this.controller.getVolumeBars().setMouseTransparent(false);
    		
    		this.controller.getBasePane().getScene().addEventHandler(MouseEvent.ANY,
    				 					controller.getStaffInstrumentEventHandler());
        } else {
            isPressed = true;
            pressImage();
    		StateMachine.setClipboardPressed(true);
    		this.controller.getStaffInstruments().setMouseTransparent(true);
    		this.controller.getVolumeBars().setMouseTransparent(true);
    		
    		this.controller.getBasePane().getScene().removeEventHandler(MouseEvent.ANY,
    				 					controller.getStaffInstrumentEventHandler());
        }
    }
}
<file_sep>package smp;

import java.io.File;
import java.util.ArrayList;
import java.util.Hashtable;

import smp.stateMachine.Settings;
import javafx.scene.ImageCursor;
import javafx.scene.image.Image;

/**
 * A class that loads all the necessary images for the program to function when
 * the program first starts.
 * 
 * @author RehdBlob
 * @since 2012.08.14
 */
public class ImageLoader implements Loader {

    /**
     * Contains references to all the loaded sprites in JavaFX Image form.
     */
    private Hashtable<ImageIndex, Image> spritesFX;

    
    /**
     * Contains references to all of the loaded sprites for the cursors in this 
     * program.
     */
    private ArrayList<ImageCursor> cursors;
    
    /**
     * The amount of loading that the imageLoader has done, anywhere between 0 to 1.
     */
    private static double loadStatus = 0.0;

    /**
     * The extension of the image files that we are to be loading. An advantage of
     * .png files is that they can have transparent pixels.
     */
    private String extension = ".png";

    /**
     * The path where the sprites are located.
     */
    private String path = "./sprites/";

    /**
     * Initializes the sprites hashtables. Will eventually figure out which Image
     * class is better: java.awt.Image, or javafx.scene.image.Image.
     */
    public ImageLoader() {
        cursors = new ArrayList<ImageCursor>();
        spritesFX = new Hashtable<ImageIndex, javafx.scene.image.Image>();
    }

    /**
     * Loads all of the image files that will be used in drawing
     * the main window and all of the buttons of Super Mario Paint.
     * A splash screen runs while this is happening.
     */
    @Override
    public void run() {
        File f;
        ImageIndex [] ind = ImageIndex.values();
        /** Change this if for some reason we want more cursors. */
        int NUM_CURSORS = 4;
        for (int i = 0; i < NUM_CURSORS; i++) {
            String s = "./sprites/CURSOR_" + i + ".png";
            File f2 = new File(s);
            if (f2.exists()) {
                cursors.add(new ImageCursor(
                        new Image(f2.toURI().toString()), 0, 0));
                if (Settings.debug > 0)
                    System.out.println(
                            "Loaded Cursor: " + s);
            }
        }
        for (ImageIndex i : ind) {
            setLoadStatus((i.ordinal() + 1.0) / ind.length);
            f = new File(path + i.toString() + extension);
            try {
                Image temp2 =
                        new Image(f.toURI().toString());
                spritesFX.put(i, temp2);
                if (Settings.debug > 0)
                    System.out.println(
                            "Loaded Image: " + i.toString() + extension);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                } catch (Exception e) {
                    e.printStackTrace();
            }
        }
        setLoadStatus(1);

    }

    /**
     * Gets an image from whatever <code>ImageIndex</code> that was passed to it.
     * 
     * @param index An ImageIndex, presumably the one that one wants to retreive an
     *              image with.
     * @return An <code>Image</code> that was loaded by the ImageLoader in the
     *         beginning, linked by the ImageIndex in a Hashtable.
     * @throws NullPointerException
     */
    public Image getSpriteFX(ImageIndex index) throws NullPointerException {
        return spritesFX.get(index);
    }

    /**
     * Gets a cursor from
     * @param type 
     * @return
     */
    public ImageCursor getCursor(int type) {
        if (type >= cursors.size() || type < 0)
            return null;
        return cursors.get(type);
    }
    
    /**
     * @return The current load status, which is anywhere between 0 and 1.
     */
    @Override
    public double getLoadStatus() {
        return loadStatus;
    }

    /**
     * Sets the load status of the Thread version of ImageLoader.
     * 
     * @param d A <b>double</b> that is anywhere between 0 and 1.
     */
    @Override
    public void setLoadStatus(double d) {
        if (d >= 0 && d <= 1)
            loadStatus = d;
    }

}
<file_sep>package smp.components.buttons;

import java.util.Timer;
import java.util.TimerTask;

import javafx.application.Platform;
import javafx.beans.property.StringProperty;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import smp.ImageLoader;
import smp.components.Values;
import smp.components.general.ImagePushButton;
import smp.fx.SMPFXController;
import smp.stateMachine.ProgramState;
import smp.stateMachine.StateMachine;

/**
 * This is a class that takes care of the adjustment of tempo in Super Mario
 * Paint.
 *
 * @author RehdBlob
 * @since 2013.09.28
 */
public class TempoAdjustButton extends ImagePushButton {

    /** This is the current tempo. */
    private StringProperty currTempo;

    /** Tells us whether this is a plus or minus button. */
    private boolean isPositive;

    /** This is a timer object for click-and-hold. */
    private Timer t;

    /**
     * Default constructor
     *
     * @param i
     *            The <code>ImageView</code> object that we want this adjustment
     *            button to be linked to.
     * @param ct
     *            The FXML controller object.
     * @param im
     *            The Image loader object.
     */
    public TempoAdjustButton(ImageView i, SMPFXController ct, ImageLoader im) {
        super(i, ct, im);
        t = new Timer();

    }

    /**
     * @param b
     *            Is this a positive button?
     */
    public void setPositive(boolean b) {
        isPositive = b;
    }

    /**
     * @return Whether this is a positive button.
     */
    public boolean isPositive() {
        return isPositive;
    }

    /**
     * Sets the String property to display the tempo.
     *
     * @param s
     *            This is the StringProperty that displays the tempo.
     */
    public void setStringProperty(StringProperty s) {
        currTempo = s;
    }

    @Override
    protected void reactPressed(MouseEvent event) {
        ProgramState curr = StateMachine.getState();
        if (curr == ProgramState.EDITING) {
            setPressed();
            addTempo(1);
            TimerTask tt = new clickHold();
            t.schedule(tt, Values.HOLDTIME, Values.REPEATTIME);
        }
    }

    @Override
    protected void reactReleased(MouseEvent event) {
        t.cancel();
        t = new Timer();
        resetPressed();
    }

    /**
     * Makes it such that the application thread changes the tempo of the song.
     *
     * @param add
     *            The amount of tempo that you want to add. Usually an integer.
     */
    private void addTempo(final double add) {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                double ch = 0;
                if (isPositive)
                    ch = add;
                else
                    ch = -add;
                double tempo = StateMachine.getTempo() + ch;
                StateMachine.setTempo(tempo);
                currTempo.setValue(String.valueOf(tempo));
            }
        });
    }

    /**
     * This is a timer task that increments the tempo of the song.
     *
     * @author RehdBlob
     * @since 2013.11.10
     */
    class clickHold extends TimerTask {

        @Override
        public void run() {
            addTempo(1);
        }

    }

}
<file_sep>package smp.components.topPanel;

import smp.ImageLoader;
import smp.components.buttons.ModeButton;
import smp.components.staff.Staff;
import smp.fx.SMPFXController;

/**
 * Panel buttons that are on the top panel.
 * @author RehdBlob
 * @since 2014.05.21
 */
public class PanelButtons {

    /** The Staff that this is linked to. */
    private Staff theStaff;

    /** Mode button. */
    private ModeButton mButton;

    /** The FXML controller class. */
    private SMPFXController controller;

    /** The Image Loader class. */
    private ImageLoader il;

    /** The button line, where one selects instruments. */
    private ButtonLine bl;

    /** Default constructor. */
    public PanelButtons(Staff s, SMPFXController ct, ImageLoader im, ButtonLine b) {
        il = im;
        theStaff = s;
        setController(ct);
        mButton = new ModeButton(controller.getModeButton(),
                controller.getModeText(), controller, il);
        mButton.setStaff(theStaff);
        bl = b;
    }

    /**
     * Sets the controller class.
     * @param ct The FXML controller class.
     */
    public void setController(SMPFXController ct) {
        controller = ct;
    }

    /**
     * @param b The Button Line that we want to associate with the top panel.
     */
    public void setButtonLine(ButtonLine b) {
        bl = b;
    }

    /**
     * @return The ButtonLine that is associated with the top panel.
     */
    public ButtonLine getButtonLine() {
        return bl;
    }

}
<file_sep>package smp.components.buttons;

import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import smp.ImageIndex;
import smp.ImageLoader;
import smp.components.general.ImageToggleButton;
import smp.fx.SMPFXController;
import smp.stateMachine.ProgramState;
import smp.stateMachine.StateMachine;

/**
 * This is the button, that when clicked, toggles the loop function of this
 * program.
 *
 * @author RehdBlob
 * @since 2013.08.29
 */
public class LoopButton extends ImageToggleButton {

    /**
     * This creates a new LoopButton object.
     *
     * @param i
     *            This <code>ImageView</code> object that you are trying to link
     *            this button with.
     * @param ct
     *            The FXML controller object.
     * @param im
     *            The Image loader object.
     */
    public LoopButton(ImageView i, SMPFXController ct, ImageLoader im) {
        super(i, ct, im);
        getImages(ImageIndex.LOOP_PRESSED, ImageIndex.LOOP_RELEASED);
        releaseImage();
        isPressed = false;
    }

    /** Releases the loop button. */
    public void release() {
        if (isPressed)
            reactPressed(null);
    }

    @Override
    protected void reactPressed(MouseEvent event) {
        ProgramState curr = StateMachine.getState();
        if (curr != ProgramState.ARR_EDITING && curr != ProgramState.ARR_PLAYING) {
            if (isPressed) {
                isPressed = false;
                releaseImage();
                StateMachine.resetLoopPressed();
            } else {
                isPressed = true;
                pressImage();
                StateMachine.setLoopPressed();
            }
        }
    }

}
<file_sep>package smp.presenters;

import java.io.File;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.scene.control.ListView;
import smp.models.staff.StaffArrangement;
import smp.models.staff.StaffSequence;
import smp.models.stateMachine.ProgramState;
import smp.models.stateMachine.StateMachine;
import smp.models.stateMachine.Variables;
import smp.presenters.api.load.Utilities;
import smp.presenters.api.reattachers.ArrangementReattacher;

public class ArrangementListPresenter {
	
	//TODO: auto-add these model comments
	//====Models====
	private ObjectProperty<StaffArrangement> theArrangement;
	private ObjectProperty<ProgramState> programState;
	private IntegerProperty arrangementListSelectedIndex;
	private ObjectProperty<StaffSequence> theSequence;
	
	private ListView<String> arrangementList;
	
	private ArrangementReattacher arrangementReattacher;

	ListProperty<String> listProperty = new SimpleListProperty<>();

	public ArrangementListPresenter(ListView<String> arrangementList) {
		this.arrangementList = arrangementList;
		
		this.theArrangement = Variables.theArrangement;
		this.theSequence = Variables.theSequence;
		this.arrangementListSelectedIndex = Variables.arrangementListSelectedIndex;
		this.programState = StateMachine.getState();
		this.arrangementReattacher = new ArrangementReattacher(this.theArrangement);
		initializeArrangementList();
		setupViewUpdater();
	}

	/**
	 * Adds in the listener behaviour for the arrangement list.
	 */
	private void initializeArrangementList() {
		arrangementList.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
			@Override
			public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
				if (programState.get() == ProgramState.ARR_PLAYING)
					return;
				int x = arrangementList.getSelectionModel().getSelectedIndex();
				if (x != -1) {
					ObservableList<StaffSequence> s = theArrangement.get().getTheSequences();
					ObservableList<File> f = theArrangement.get().getTheSequenceFiles();
					theSequence.set(Utilities.loadSequenceFromArrangement(f.get(x)));
					s.set(x, theSequence.get());
				}
			}
		});
	}
	
	private void setupViewUpdater() {
		this.arrangementList.setEditable(true);
		this.arrangementList.setStyle("-fx-font: 8pt \"Arial\";");
		this.arrangementList.setVisible(false);

		this.arrangementReattacher.setOnReattachListener(new ChangeListener<StaffArrangement>() {

			@Override
			public void changed(ObservableValue<? extends StaffArrangement> observable, StaffArrangement oldValue,
					StaffArrangement newValue) {
				listProperty.set(theArrangement.get().getTheSequenceNames());
				arrangementList.itemsProperty().unbind();
				arrangementList.itemsProperty().bind(listProperty);
				
				if(theArrangement.get().getTheSequenceNames().size() > 0)
					arrangementList.getSelectionModel().select(0);
			}
		});
		
		arrangementList.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				arrangementListSelectedIndex.set(newValue.intValue());
			}
		});
		this.arrangementListSelectedIndex.addListener(new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				arrangementList.getSelectionModel().select(newValue.intValue());
			}
		});

		this.programState.addListener(new ChangeListener<Object>() {

			@Override
			public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
				if (newValue.equals(ProgramState.EDITING))
					arrangementList.setVisible(false);
				else if (newValue.equals(ProgramState.ARR_EDITING))
					arrangementList.setVisible(true);
			}
		});
	}
}
<file_sep>package smp.commandmanager;

public interface CommandInterface {
	
	public void redo();
	
	public void undo();
}<file_sep>package smp.presenters.staff.VolumeBarsPresenter;

import java.util.ArrayList;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.scene.Node;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import smp.ImageIndex;
import smp.ImageLoader;
import smp.TestMain;
import smp.models.staff.StaffNoteLine;
import smp.models.stateMachine.Variables;
import smp.models.stateMachine.Variables.WindowLines;
import smp.presenters.api.reattachers.NoteLineReattacher;
import smp.presenters.staff.VolumeBarsPresenter.StaffVolumeEventHandler;

public class VolumeBarsPresenter {

	// TODO: auto-add these model comments
	// ====Models====
	private WindowLines windowLines;

	private ArrayList<NoteLineReattacher> noteLineReattachers;

	private HBox volumeBars;
	
    /** The ImageView objects for each volume bar. */
    //j574y923..model-view
    private ArrayList<ImageView> theVolBarIVs = new ArrayList<>();
    
    /** The ImageLoader class. */
    //TODO:
    //j574y923..model-view
    private ImageLoader il = (ImageLoader) TestMain.imgLoader;
	
	/**
	 * This is the list of volume bar handlers on the staff.
	 * 
	 * @see smp.components.staff.NoteMatrix.volumeBarHandlers
	 */
	private ArrayList<StaffVolumeEventHandler> volumeBarHandlers;

	/**
	 * This is the list of volume bars on the staff.
	 * 
	 * @see smp.components.staff.NoteMatrix.volumeBars
	 */
	private ArrayList<StackPane> volumeBarsSP;

	public VolumeBarsPresenter(HBox volumeBars) {
		this.volumeBars = volumeBars;
        volumeBarHandlers = new ArrayList<StaffVolumeEventHandler>();

		this.windowLines = Variables.windowLines;
		this.noteLineReattachers = new ArrayList<NoteLineReattacher>();
		initializeVolumeBars(this.volumeBars);
		initializeVolumeBarIVs(this.volumeBars);
		setupViewUpdater();
	}

	private void initializeVolumeBarIVs(HBox volumeBars) {
		for (Node v : volumeBars.getChildren()) {
			StackPane st = (StackPane) v;
	        ImageView theVolBar = (ImageView) st.getChildren().get(0);
	        theVolBar.setImage(il.getSpriteFX(ImageIndex.VOL_BAR));
	        theVolBar.setVisible(false);
			theVolBarIVs.add(theVolBar);
		}
	}

	/**
	 * Initializes the volume bars in the program.
	 *
	 * @param volumeBars
	 *            This is the HBox that holds all of the volume bar
	 *            <code>StackPane</code> objects.
	 */
	private void initializeVolumeBars(HBox volumeBars) {
		ArrayList<StackPane> vol = new ArrayList<StackPane>();
		ObservableList<Node> volumeBarsChildren = volumeBars.getChildren();
		for (int i = 0; i < volumeBarsChildren.size(); i++) {
			Node v = volumeBarsChildren.get(i);
			StackPane volBar = (StackPane) v;
			vol.add(volBar);
			StaffVolumeEventHandler sveh = new StaffVolumeEventHandler(volBar, windowLines.get(i));
			volBar.addEventHandler(Event.ANY, sveh);
			volumeBarHandlers.add(sveh);
		}
		volumeBarsSP = vol;
	}

	private void setupViewUpdater() {
		for (int i = 0; i < windowLines.size(); i++) {
			final int index = i;
			NoteLineReattacher nlr = new NoteLineReattacher(windowLines.get(i));			
			nlr.setNewNotesSizeListener(new ChangeListener<Number>() {

				@Override
				public void changed(ObservableValue<? extends Number> observable, Number oldSize, Number newSize) {
					// either old size or new size is empty
					// we are observing whether its empty
					// we only want to update visibility depending on whether its empty
					if((oldSize.intValue() & newSize.intValue()) == 0)
						updateVolume(index);
				}
			});
			nlr.setNewVolumeListener(new ChangeListener<Number>() {

				@Override
				public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
					updateVolume(index);
				}
			});
			nlr.setOnReattachListener(new ChangeListener<Object>() {

				@Override
				public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
					updateVolume(index);
				}
			});
			noteLineReattachers.add(nlr);
		}
	}
	
    /**
     * Displays the volume of this note line.
     * @param y The volume that we want to show.
     */
    public void setVolumeDisplay(int line, double y) {
    	ImageView theVolBar = theVolBarIVs.get(line);
        if (y <= 0) {
            theVolBar.setVisible(false);
            return;
        }
        theVolBar.setVisible(true);
        theVolBar.setFitHeight(y);
    }

	/**
	 * Updates the volume display on this volume displayer.
	 */
	public void updateVolume(int line) {
		StaffNoteLine theLine = this.windowLines.get(line).get();
		StackPane stp = volumeBarsSP.get(line);
    	ImageView theVolBarIV = theVolBarIVs.get(line);
		setVolumeDisplay(line, theLine.getVolumePercent() * stp.getHeight());
		if (theLine.getVolume().get() == 0 || theLine.isEmpty()) {
			theVolBarIV.setVisible(false);
		}
    }
}
<file_sep>package smp.commandmanager.commands;

import smp.commandmanager.CommandInterface;
import smp.components.staff.sequences.StaffNoteLine;

public class RemoveVolumeCommand implements CommandInterface {

	StaffNoteLine theLine;
	int theOldVolume;
	
	public RemoveVolumeCommand(StaffNoteLine line, int oldVolume) {
		theLine = line;
		theOldVolume = oldVolume;
	}
	
	@Override
	public void redo() {
		//do nothing, AddVolumeCommand will handle this
	}

	@Override
	public void undo() {
		theLine.setVolume(theOldVolume);
	}

}
<file_sep>package smp.commandmanager.commands;

import smp.commandmanager.CommandInterface;
import smp.components.staff.sequences.StaffNote;
import smp.components.staff.sequences.StaffNoteLine;

public class AddNoteCommand implements CommandInterface {

	private StaffNoteLine theLine;
	private StaffNote theNote;

	public AddNoteCommand(StaffNoteLine line, StaffNote note) {
		theLine = line;
		theNote = note;
	}
	
	@Override
	public void redo() {
		theLine.add(theNote);
	}

	@Override
	public void undo() {
		theLine.remove(theNote);
	}

}
<file_sep>package smp.stateMachine;

import java.io.File;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javafx.scene.input.KeyCode;
import smp.components.Values;

/**
 * This is the state machine that keeps track of what state the main window is
 * in. This class keeps track of a bunch of variables that the program generally
 * uses.
 *
 * @author RehdBlob
 * @since 2012.08.07
 */
public class StateMachine {

    /** This tells us whether we have modified the song or not. */
    private static boolean modifiedSong = false;

    /** This tells us whether we have modified the arrangement or not. */
    private static boolean modifiedArr = false;

    /** This keeps track of whether we have pressed the loop button or not. */
    private static boolean loopPressed = false;

	/** This keeps track of whether we have pressed the mute button or not. */
	private static boolean mutePressed = false;

    /**
     * This keeps track of whether we have pressed the low A mute button or not.
     */
    private static boolean muteAPressed = false;

	/**
	 * This keeps track of whether we have pressed the clipboard button or not.
	 */
	private static boolean clipboardPressed = false;

    /** The list of values denoting which notes should be extended. */
    private static boolean[] noteExtensions = new boolean[Values.NUMINSTRUMENTS];
    
    /**
     * The file directory that we are currently located in. We'll start in the
     * user directory.
     */
    private static File currentDirectory = new File(System.getProperty("user.dir"));

    /** Set of currently-pressed buttons. */
    private static Set<KeyCode> buttonsPressed =
            Collections.synchronizedSet(new HashSet<KeyCode>());

    /**
     * The default state that the program is in is the EDITING state, in which
     * notes are being placed on the staff.
     */
    private static ProgramState currentState = ProgramState.EDITING;

    /**
     * The default time signature that we start out with is 4/4 time.
     */
    private static TimeSignature currentTimeSignature = TimeSignature.FOUR_FOUR;

    /**
     * The current measure line number that the program is on. Typically a
     * number between 0 and 383. This is zero by default.
     */
    private static int currentLine = 0;

    /**
     * This is the current tempo that the program is running at.
     */
    private static double tempo = Values.DEFAULT_TEMPO;

	/**
	 * The current soundset name. This should change when a new soundfont is
	 * loaded.
	 */
	private static String currentSoundset = Values.DEFAULT_SOUNDFONT;

    /**
     * Do not make an instance of this class! The implementation is such that
     * several classes may check the overall state of the program, so there
     * should only ever be just the class and its static variables and methods
     * around.
     *
     * @deprecated
     */
    private StateMachine() {
    }

    /**
     * Get the current <code>State</code> of the <code>StateMachine</code>
     *
     * @return The current <code>State</code>.
     */
    public static ProgramState getState() {
        return currentState;
    }

    /**
     * @param s
     *            Set the <code>StateMachine</code> to a certain State.
     */
    public static void setState(ProgramState s) {
        currentState = s;
    }

    /**
     * Sets the state back to "Editing" by default.
     */
    public static void resetState() {
        currentState = ProgramState.EDITING;
    }

    /**
     * @return The current time signature that we are running at.
     */
    public static TimeSignature getTimeSignature() {
        return currentTimeSignature;
    }

    /**
     * Sets the time signature to whatever that we give this method.
     *
     * @param t
     *            The new time signature.
     */
    public static void setTimeSignature(TimeSignature t) {
        currentTimeSignature = t;
    }

    /** Sets the time signature back to "4/4" by default. */
    public static void resetTimeSignature() {
        currentTimeSignature = TimeSignature.FOUR_FOUR;
    }

    /**
     * @return The tempo that this program is running at.
     */
    public static double getTempo() {
        return tempo;
    }

    /**
     * Sets the tempo to what we give it here.
     *
     * @param num
     *            The tempo we want to set the program to run at.
     * @return The current tempo.
     */
    public static void setTempo(double num) {
        tempo = num;
    }

    /**
     * Gets the current line number that we're on. Typically a value between 0
     * and 383 for most files unless you've done fun stuff and removed the
     * 96-measure limit.
     *
     * @return The current line number (left justify)
     */
    public static int getMeasureLineNum() {
        return currentLine;
    }

    /**
     * Sets the current line number to whatever is given to this method.
     *
     * @param num
     *            The number that we're trying to set our current line number
     *            to.
     */
    public static void setMeasureLineNum(int num) {
        currentLine = num;
    }

    /** Sets that the song is now loop-enabled. */
    public static void setLoopPressed() {
        loopPressed = true;
    }

    /** Sets that the song is now *not* loop-enabled. */
    public static void resetLoopPressed() {
        loopPressed = false;
    }

    /** @return Whether the loop button is pressed or not. */
    public static boolean isLoopPressed() {
        return loopPressed;
    }

    /** Sets the fact that mute notes are now enabled. */
    public static void setMutePressed() {
        mutePressed = true;
    }

    /** Turns off the fact that we have pressed the mute button. */
    public static void resetMutePressed() {
        mutePressed = false;
    }

    /** @return Whether the mute button is pressed or not. */
    public static boolean isMutePressed() {
        return mutePressed;
    }

    /**
     * Sets the modified flag to true or false.
     *
     * @param b
     *            Whether we have modified a song or not.
     */
    public static void setSongModified(boolean b) {
        modifiedSong = b;
    }

    /**
     * @return Whether we have modified the current song or not.
     */
    public static boolean isSongModified() {
        return modifiedSong;
    }

    /**
     * @param b
     *            Whether we have modified an arrangement or not.
     */
    public static void setArrModified(boolean b) {
        modifiedArr = b;
    }

    /**
     * @return Whether we have modified the current arrangement or not.
     */
    public static boolean isArrModified() {
        return modifiedArr;
    }

    /**
     * @param b
     *            Whether we have pressed the low A mute button.
     */
    public static void setMuteAPressed(boolean b) {
        muteAPressed = b;
    }

    /**
     * @return Whether our mute-all button is pressed or not.
     */
    public static boolean isMuteAPressed() {
        return muteAPressed;
    }
    
    /**
     * @since 08.2017
     */
    public static void setClipboardPressed() {
    	clipboardPressed = true;
    }
    public static void resetClipboardPressed() {
    	clipboardPressed = false;
    }
    public static boolean isClipboardPressed() {
    	return clipboardPressed;
    }
    

    /**
     * @param set The note extensions that we want to set.
     */
    public static void setNoteExtensions(boolean[] set) {
        noteExtensions = set;
    }

    /**
     * @return A list of notes that we want to act like the coin.
     */
    public static boolean[] getNoteExtensions() {
        return noteExtensions;
    }

    /**
     * @return Set of currently-pressed buttons.
     */
    public static Set<KeyCode> getButtonsPressed() {
        return buttonsPressed;
    }

    /**
     * Clears the set of key presses in this program.
     */
    public static void clearKeyPresses() {
        buttonsPressed.clear();

    }

    /** @return Last directory we accessed. */
    public static File getCurrentDirectory() {
        return currentDirectory;
    }

    /** @param cDir Set current directory to this. */
    public static void setCurrentDirectory(File cDir) {
        StateMachine.currentDirectory = cDir;
    }

	/**
	 * @return The current soundset name.
	 * @since v1.1.2
	 */
	public static String getCurrentSoundset() {
		return currentSoundset;
	}

	/**
	 * @param soundset
	 *            Set current soundset to this.
	 * @since v1.1.2
	 */
	public static void setCurrentSoundset(String soundset) {
		StateMachine.currentSoundset = soundset;
    }

}
<file_sep>package smp.commandmanager.commands;

import java.util.ArrayList;

import smp.commandmanager.CommandInterface;
import smp.components.Values;
import smp.components.staff.Staff;
import smp.components.staff.sequences.StaffNoteLine;

public class MultiplyTempoCommand implements CommandInterface {

	Staff theStaff;
	int theMultiplyAmount;
	
	public MultiplyTempoCommand(Staff staff, int multiplyAmount) {
		theStaff = staff;
		theMultiplyAmount = multiplyAmount;
	}
	
	@Override
	public void redo() {
		ArrayList<StaffNoteLine> s = theStaff.getSequence().getTheLines();
        ArrayList<StaffNoteLine> n = new ArrayList<StaffNoteLine>();
        for (int i = 0; i < s.size(); i++) {
            n.add(s.get(i));
            for (int j = 0; j < theMultiplyAmount - 1; j++)
                n.add(new StaffNoteLine());
        }
        s.clear();
        s.addAll(n);
//        StateMachine.setTempo(theStaff.getSequence().getTempo() * num);
//        theStaff.updateCurrTempo();
        theStaff.getControlPanel().getScrollbar()
                .setMax(s.size() - Values.NOTELINES_IN_THE_WINDOW);
	}

	@Override
	public void undo() {
		ArrayList<StaffNoteLine> s = theStaff.getSequence().getTheLines();
        ArrayList<StaffNoteLine> n = new ArrayList<StaffNoteLine>();
		for (int i = 0; i < s.size(); i+= theMultiplyAmount) {
			n.add(s.get(i));
		}
        s.clear();
        s.addAll(n);
//      StateMachine.setTempo(theStaff.getSequence().getTempo() * num);
//      theStaff.updateCurrTempo();
	    theStaff.getControlPanel().getScrollbar()
	            .setMax(s.size() - Values.NOTELINES_IN_THE_WINDOW);
	}

}
<file_sep>package smp.presenters.staff;

import java.util.ArrayList;

import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.scene.Node;
import javafx.scene.layout.HBox;
import smp.components.Values;
import smp.models.staff.StaffNote;
import smp.models.staff.StaffNoteLine;
import smp.models.stateMachine.Variables;
import smp.models.stateMachine.Variables.WindowLines;
import smp.presenters.api.reattachers.NoteLineReattacher;

public class StaffExtLinesLowAPresenter {

	// TODO: auto-add these model comments
	// ====Models====
	private WindowLines windowLines;

	private ArrayList<NoteLineReattacher> noteLineReattachers;

	private HBox staffExtLinesLowA;

	private ArrayList<Node> lowA;

	public StaffExtLinesLowAPresenter(HBox staffExtLinesLowA) {
		this.staffExtLinesLowA = staffExtLinesLowA;

		this.windowLines = Variables.windowLines;
		this.noteLineReattachers = new ArrayList<NoteLineReattacher>();
		initializeStaffLedgerLines();
		setupViewUpdater();
	}

	private void setupViewUpdater() {
		for (int i = 0; i < windowLines.size(); i++) {
			final int index = i;
			final ObjectProperty<StaffNoteLine> windowLine = windowLines.get(i);
			NoteLineReattacher nlr = new NoteLineReattacher(windowLines.get(i));
			nlr.setNewNotesListener(new ListChangeListener<StaffNote>() {

				@Override
				public void onChanged(Change<? extends StaffNote> c) {
					populateStaffLedgerLines(windowLine.get(), index);
				}
			});
			nlr.setOnReattachListener(new ChangeListener<Object>() {

				@Override
				public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
					populateStaffLedgerLines((StaffNoteLine) newValue, index);
				}
			});
			noteLineReattachers.add(nlr);
		}
	}

	/**
	 * Re-draws the staff ledger lines based on the notes present in a
	 * StaffNoteLine at a certain index. Position 14 & 16 are the positions of
	 * the high A and high C lines, and positions 0 and 2 are the positions of
	 * the low A and low C lines.
	 *
	 * @param stl
	 *            The <code>StaffNoteLine</code> that we want to check.
	 * @param index
	 *            The index that we are updating.
	 *
	 */
	private void populateStaffLedgerLines(StaffNoteLine stl, int index) {
		int low = Values.NOTES_IN_A_LINE;
		for (StaffNote n : stl.getNotes()) {
			int nt = n.getPosition();
			if (nt <= low)
				low = nt;
		}
		lowA.get(index).setVisible(low <= Values.lowA);
	}

	/**
	 * Sets up the staff expansion lines, which are to hold notes that are
	 * higher than or lower than the regular lines of the staff.
	 *
	 * @param staffLLines
	 *            An array of ledger lines. This method expects that there will
	 *            be four of these, two of which indicate the lines above the
	 *            staff and the other of which indicating the lines below the
	 *            staff.
	 */
	private void initializeStaffLedgerLines() {
		lowA = new ArrayList<Node>();
		lowA.addAll(this.staffExtLinesLowA.getChildren());
		hideAllLedgerLines();
	}

	/**
	 * Hides all of the ledger lines.
	 */
	public void hideAllLedgerLines() {

		for (Node nd : lowA)
			nd.setVisible(false);
	}
}
<file_sep>package smp;

import java.util.Set;

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.ScrollEvent;
import javafx.stage.Stage;
import smp.components.Values;
import smp.models.staff.StaffSequence;
import smp.models.stateMachine.StateMachine;
import smp.models.stateMachine.Variables;

public class KeyboardListeners {

	// TODO: auto-add these model comments
	// ====Models====
	private DoubleProperty measureLineNum;
	private ObjectProperty<StaffSequence> theSequence;
	private Set<KeyCode> buttonsPressed;
	
	private Stage primaryStage;
	private Scene primaryScene;

	/**
	 * Creates the keyboard listeners that we will be using for various other
	 * portions of the program. Ctrl, alt, and shift are of interest here, but
	 * the arrow keys will also be considered.
	 *
	 * @param primaryStage
	 *            The main window.
	 */
	public KeyboardListeners(Stage primaryStage) {

		this.primaryStage = primaryStage;
		this.primaryScene = primaryStage.getScene();
		
		this.measureLineNum = StateMachine.getMeasureLineNum();
		this.theSequence = Variables.theSequence;
		this.buttonsPressed = StateMachine.getButtonsPressed();
		setupKeyboardListeners();
	}

	private void setupKeyboardListeners() {
		
		primaryScene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {

			@Override
			public void handle(KeyEvent ke) {

				switch (ke.getCode()) {
				case PAGE_UP:
					measureLineNum.set(measureLineNum.get() - Values.NOTELINES_IN_THE_WINDOW);
					break;
				case PAGE_DOWN:
					measureLineNum.set(measureLineNum.get() + Values.NOTELINES_IN_THE_WINDOW);
					break;
				case HOME:
					if (ke.isControlDown())
						measureLineNum.set(0);
					break;
				case END:
					if (ke.isControlDown())
						measureLineNum.set(theSequence.get().getTheLinesSize().get());
					break;
				// @since v1.1.2, requested by <NAME>
				case LEFT:
					if (ke.isControlDown())
						measureLineNum.set(measureLineNum.get() - 4);
					break;
				case RIGHT:
					if (ke.isControlDown())
						measureLineNum.set(measureLineNum.get() + 4);
					break;
				default:
				}

				buttonsPressed.add(ke.getCode());
//				StaffInstrumentEventHandler.updateAccidental();
				ke.consume();
			}
		});

		primaryScene.addEventHandler(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() {

			@Override
			public void handle(KeyEvent ke) {
				buttonsPressed.remove(ke.getCode());
//				StaffInstrumentEventHandler.updateAccidental();
				ke.consume();
			}
		});

		primaryScene.addEventHandler(ScrollEvent.ANY, new EventHandler<ScrollEvent>() {

			@Override
			public void handle(ScrollEvent se) {
				if (se.getDeltaY() < 0) {
					measureLineNum.set(measureLineNum.get() + 1);
				} else if (se.getDeltaY() > 0) {
					measureLineNum.set(measureLineNum.get() - 1);
				}
				se.consume();
			}
		});

		primaryStage.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
				buttonsPressed.clear();
			}
		});
	}
}
<file_sep>package smp.views;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import smp.presenters.ArrangementListPresenter;
import smp.presenters.InstLinePresenter;
import smp.presenters.ModeTextPresenter;
import smp.presenters.ScrollbarPresenter;
import smp.presenters.SelectedInstPresenter;
import smp.presenters.SongNamePresenter;
import smp.presenters.TempoBoxPresenter;
import smp.presenters.TempoIndicatorPresenter;
import smp.presenters.buttons.AddButtonPresenter;
import smp.presenters.buttons.DeleteButtonPresenter;
import smp.presenters.buttons.DownButtonPresenter;
import smp.presenters.buttons.LeftArrowPresenter;
import smp.presenters.buttons.LeftFastArrowPresenter;
import smp.presenters.buttons.LoadButtonPresenter;
import smp.presenters.buttons.ModeButtonPresenter;
import smp.presenters.buttons.NewButtonPresenter;
import smp.presenters.buttons.OptionsButtonPresenter;
import smp.presenters.buttons.RightArrowPresenter;
import smp.presenters.buttons.RightFastArrowPresenter;
import smp.presenters.buttons.SaveButtonPresenter;
import smp.presenters.buttons.TempoMinusPresenter;
import smp.presenters.buttons.TempoPlusPresenter;
import smp.presenters.buttons.UpButtonPresenter;
import smp.presenters.staff.StaffAccidentalsPresenter;
import smp.presenters.staff.StaffExtLinesHighAPresenter;
import smp.presenters.staff.StaffExtLinesHighCPresenter;
import smp.presenters.staff.StaffExtLinesLowAPresenter;
import smp.presenters.staff.StaffExtLinesLowCPresenter;
import smp.presenters.staff.StaffMeasureLinesPresenter;
import smp.presenters.staff.StaffMeasureNumbersPresenter;
import smp.presenters.staff.StaffInstrumentsPresenter.StaffInstrumentsPresenter;
import smp.presenters.staff.VolumeBarsPresenter.VolumeBarsPresenter;

public class MainWindowController {
	
    /**
     * Location of the Options Window fxml file. 
     * TODO: move fxml string to somewhere in models layer
     */
    private String fxml = "./MainWindow.fxml";
    
    @FXML
    private HBox staffExtLinesHighC;

    @FXML
    private ImageView play;

    @FXML
    private HBox staffExtLinesHighA;

    @FXML
    private ImageView deleteButton;

    @FXML
    private HBox controls;

    @FXML
    private ImageView loadButton;

    @FXML
    private Text tempoIndicator;

    @FXML
    private ImageView rightFastArrow;

    @FXML
    private HBox staffExtLinesLowC;

    @FXML
    private StackPane tempoBox;

    @FXML
    private HBox staffExtLinesLowA;

    @FXML
    private HBox staffInstruments;

    @FXML
    private HBox scrollbarHolder;

    @FXML
    private ImageView tempoPlus;

    @FXML
    private ImageView loop;

    @FXML
    private HBox volumeBars;

    @FXML
    private ImageView optionsButton;

    @FXML
    private ListView<String> arrangementList;

    @FXML
    private ImageView mute;

    @FXML
    private ImageView addButton;

    @FXML
    private ImageView tempoMinus;

    @FXML
    private ImageView leftFastArrow;

    @FXML
    private ImageView stop;

    @FXML
    private ImageView modeButton;

    @FXML
    private Color x1;

    @FXML
    private Font x2;

    @FXML
    private AnchorPane basePane;

    @FXML
    private ImageView selectedInstHolder;

    @FXML
    private StackPane staffPane;

    @FXML
    private TextField songName;

    @FXML
    private Slider scrollbar;

    @FXML
    private HBox staffMeasureLines;

    @FXML
    private HBox staffPlayBars;

    @FXML
    private HBox topBoxRight;

    @FXML
    private ImageView upButton;

    @FXML
    private ImageView clipboardButton;

    @FXML
    private ImageView downButton;

    @FXML
    private Pane topPane;

    @FXML
    private HBox instLine;

    @FXML
    private HBox staffMeasureNumbers;

    @FXML
    private Button secretButton;

    @FXML
    private ImageView saveButton;

    @FXML
    private ImageView clipboardLabel;

    @FXML
    private HBox staffAccidentals;

    @FXML
    private ImageView selectedInst;

    @FXML
    private ImageView leftArrow;

    @FXML
    private ImageView rightArrow;

    @FXML
    private BorderPane mainLayout;

    @FXML
    private ImageView newButton;

    @FXML
    private ImageView muteA;

    @FXML
    private Text modeText;    
    
    /**
     * Initializes the Controller class for the options window
     */
    public void initialize() {
    	new ArrangementListPresenter(arrangementList);
    	new InstLinePresenter(instLine);
    	new ModeTextPresenter(modeText);
    	new SelectedInstPresenter(selectedInst);
    	new ScrollbarPresenter(scrollbar);
    	new SongNamePresenter(songName);
    	new TempoBoxPresenter(tempoBox);
    	new TempoIndicatorPresenter(tempoIndicator);
    	
    	new AddButtonPresenter(addButton);
    	new DeleteButtonPresenter(deleteButton);
    	new DownButtonPresenter(downButton);
    	new LeftArrowPresenter(leftArrow);
    	new LeftFastArrowPresenter(leftFastArrow);
    	new LoadButtonPresenter(loadButton);
    	new ModeButtonPresenter(modeButton);
    	new NewButtonPresenter(newButton);
    	new OptionsButtonPresenter(optionsButton);
    	new RightArrowPresenter(rightArrow);
    	new RightFastArrowPresenter(rightFastArrow);
    	new SaveButtonPresenter(saveButton);
    	new TempoMinusPresenter(tempoMinus);
    	new TempoPlusPresenter(tempoPlus);
    	new UpButtonPresenter(upButton);
    	
    	new StaffAccidentalsPresenter(staffAccidentals);
    	new StaffExtLinesHighAPresenter(staffExtLinesHighA);
    	new StaffExtLinesHighCPresenter(staffExtLinesHighC);
    	new StaffExtLinesLowAPresenter(staffExtLinesLowA);
    	new StaffExtLinesLowCPresenter(staffExtLinesLowC);
    	new StaffInstrumentsPresenter(staffInstruments);
    	new StaffMeasureLinesPresenter(staffMeasureLines);
    	new StaffMeasureNumbersPresenter(staffMeasureNumbers);
    	new VolumeBarsPresenter(volumeBars);
    }
    
    /**
     * @return Location of the Options Window fxml file.
     */
    public String getFXML() {
    	return fxml;
    }
}
<file_sep>package smp.presenters.api.clipboard;

import java.util.HashMap;
import java.util.HashSet;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.effect.Blend;
import javafx.scene.effect.BlendMode;
import javafx.scene.effect.ColorInput;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import smp.ImageLoader;
import smp.components.Values;
import smp.components.staff.Staff;
import smp.components.staff.sequences.StaffNoteLine;
import smp.fx.SMPFXController;

public class StaffClipboard {

	public static Color HIGHLIGHT_FILL = new Color(0.5, 0.5, 0.5, 0.5);
	
	private Staff theStaff;
	private SMPFXController controller;
	private ImageLoader il;
	private Pane rubberBandLayer;
	private StaffRubberBand rubberBand;
	private StaffRubberBandEventHandler rbeh;

	private StaffClipboardFilter instFilter;
	
	/** The functions class for copy, cut, paste, etc. */
	private StaffClipboardAPI theAPI;
	
	/** The list that keeps track of all the selections' bounds made by the user */
	private HashMap<Integer, StaffNoteLine> selection;
	
	/** The list that will keep track of copied notes (and volumes) */
	private HashMap<Integer, StaffNoteLine> copiedData;

	/** Volumes aren't node references so we keep track of the volumes' lines */
	private HashSet<Integer> highlightedVolumes;
	
	/**
	 * The listener that will update which volume bars are highlighted every
	 * scrollbar change
	 */
	private ChangeListener<Number> highlightedVolumesRedrawer;

	public StaffClipboard(StaffRubberBand rb, Staff st, SMPFXController ct, ImageLoader im) {

		rubberBand = rb;
		il = im;
		theStaff = st;
		controller = ct;

		selection = new HashMap<>();
		copiedData = new HashMap<>();
		
		//TODO: merge staffclipboard and staffclipboardapi together
		theAPI = new StaffClipboardAPI(this, theStaff, im, ct.getModifySongManager());

		redrawUI(ct);
		
		rubberBandLayer = controller.getBasePane();
		rbeh = new StaffRubberBandEventHandler(rubberBand, controller, rubberBandLayer, this);
        initializeRBEH(rbeh, controller);
		rubberBandLayer.addEventHandler(MouseEvent.ANY, rbeh);
		
		initializeHighlightedVolumes(ct);
		
		instFilter = new StaffClipboardFilter(ct.getInstLine(), il);
	}

	private void initializeHighlightedVolumes(SMPFXController ct) {
		
		final ObservableList<Node> volumeBars = ct.getVolumeBars().getChildren();
		ImageView theVolBar = (ImageView) ((StackPane)volumeBars.get(0)).getChildren().get(0);
		final Blend highlightBlendVolume = new Blend(
	            BlendMode.SRC_OVER,
	            null,
	            new ColorInput(
	            		-theVolBar.getFitWidth(),
	                    0,
	                    theVolBar.getFitWidth() * 3,
	                    theVolBar.getFitHeight(),
	                    StaffClipboard.HIGHLIGHT_FILL
	            ));

		highlightedVolumes = new HashSet<>();
		highlightedVolumesRedrawer = new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				for(int i = 0; i < Values.NOTELINES_IN_THE_WINDOW; i++) {

					// we want the actual volumebar image to highlight
					ImageView theVolBar = (ImageView) ((StackPane) volumeBars.get(i)).getChildren().get(0);

					if (highlightedVolumes.contains(i + newValue.intValue()))
						theVolBar.setEffect(highlightBlendVolume);
					else
						theVolBar.setEffect(null);
				}
			}
		};
		
		ct.getScrollbar().valueProperty().addListener(highlightedVolumesRedrawer);
	}
	
	/**
	 * trigger layout pass in basePane and all its children. this will
	 * initialize the bounds for all UI components.
	 * 
	 * components cannot be modified until this function initializes them.
	 * 
	 * @param ct
	 */
	private void redrawUI(SMPFXController ct) {
		AnchorPane basePane = ct.getBasePane();
		basePane.applyCss();//requestLayout??
		basePane.layout();
	}
	
	/**
	 * redrawUI before calling this 
	 * 
	 * @param rbeh
	 * @param ct
	 */
	private void initializeRBEH(StaffRubberBandEventHandler rbeh, SMPFXController ct) {
		
		//initialize lineMinBound
		HBox staffInstruments = ct.getStaffInstruments();
		ObservableList<Node> instrumentLines = staffInstruments.getChildren();
		VBox firstLine = (VBox) instrumentLines.get(0);
		Bounds firstLineBounds = firstLine.localToScene(firstLine.getBoundsInLocal()); 
		rbeh.initializeLineMinBound(firstLineBounds.getMinX());

		//initialize lineMaxBound
		VBox lastLine = (VBox) instrumentLines.get(instrumentLines.size() - 1);
		Bounds lastLineBounds = lastLine.localToScene(lastLine.getBoundsInLocal()); 
		rbeh.initializeLineMaxBound(lastLineBounds.getMaxX());
		
		//initialize lineSpacing
		rbeh.initializeLineSpacing(firstLineBounds.getWidth());
		
		//initialize positionMinBound
		ObservableList<Node> positions = firstLine.getChildren();
		StackPane firstPosition = (StackPane) positions.get(0);
		Bounds firstPositionBounds = firstPosition.localToScene(firstPosition.getBoundsInLocal()); 
		rbeh.initializePositionMinBound(firstPositionBounds.getMinY());		
		
		//initialize positionMaxBound
		StackPane lastPosition = (StackPane) positions.get(positions.size() - 1);
		Bounds lastPositionBounds = lastPosition.localToScene(lastPosition.getBoundsInLocal());
		rbeh.initializePositionMaxBound(lastPositionBounds.getMaxY());
	
		//initialize positionSpacing
		StackPane secondPosition = (StackPane) positions.get(1);
		Bounds secondPositionBounds = secondPosition.localToScene(secondPosition.getBoundsInLocal());
		rbeh.initializePositionSpacing((secondPositionBounds.getMinY() - firstPositionBounds.getMinY()) * 2);
		
		//initialize volume YMax coordinate
		HBox volumeBars = ct.getVolumeBars();
		Bounds volumeBarsBounds = volumeBars.localToScene(volumeBars.getBoundsInLocal());
		rbeh.initializeVolumeYMaxCoord(volumeBarsBounds.getMaxY());
		
		//set margins
		StackPane staffPane = ct.getStaffPane();
		Bounds staffPaneBounds = staffPane.localToScene(staffPane.getBoundsInLocal());
		double marginDeltaX = firstLineBounds.getMinX() - staffPaneBounds.getMinX();
		double marginDeltaY = firstPositionBounds.getMinY() - staffPaneBounds.getMinY();
		rbeh.setMarginVertical(marginDeltaY);
		rbeh.setMarginHorizontal(marginDeltaX);
		
		//set scrollbar resizing
		rbeh.setScrollBarResizable(controller.getScrollbar());
	}
	
	public HashMap<Integer, StaffNoteLine> getSelection() {
		return selection;
	}
	
	public HashMap<Integer, StaffNoteLine> getCopiedData() {
		return copiedData;
	}
	
	public HashSet<Integer> getHighlightedVolumes() {
		return highlightedVolumes;
	}
	
	public ChangeListener<Number> getHighlightedVolumesRedrawer() {
		return highlightedVolumesRedrawer;
	}
	
	public StaffClipboardFilter getInstrumentFilter() {
		return instFilter;
	}
	
	// temp? merge the two classes together?
	public StaffClipboardAPI getAPI() {
		return theAPI;
	}
	
	/**
	 * @return the node the rubberband is added to or removed from
	 * @since v1.1.2
	 */
	public Pane getRubberBandLayer() {
		return rubberBandLayer;
	}
	
	/**
	 * @return the rubberband
	 * @since v1.1.2
	 */
	public StaffRubberBand getRubberBand() {
		return rubberBand;
	}
}
<file_sep>package smp.components.staff.sequences.ams;

import java.io.File;
import java.text.ParseException;
import java.util.ArrayList;

import smp.components.staff.sequences.Bookmark;
import smp.components.staff.sequences.Speedmark;
import smp.components.staff.sequences.StaffSequence;
import smp.components.staff.sounds.SMPSoundfont;

/**
 * A class for decoding Advanced Mario Sequencer songs.
 * @author RehdBlob
 * @since 2012.09.10
 */
public class AMSDecoder {

    public static StaffSequence decode(File file) {
        // TODO: Fix this.
        return null;
    }

    /**
     * Decodes an Advanced Mario Sequencer song into an SMP-readable format.
     * @param in The input String that contains (supposedly) Advanced Mario
     * Sequencer data.
     * @throws ParseException If someone tries to feed this method an invalid
     * text file.
     */
    public static StaffSequence decode(String in) throws ParseException {
        if (!isValid(in)) {
            throw new ParseException("Invalid File", 0);
        }
        String timeSig = null, tempo = null;
        SMPSoundfont soundfont = null;
        ArrayList<Speedmark> speedmarks = null;
        ArrayList<Bookmark> bookmarks = null;
        ArrayList<String> data = new ArrayList<String>();
        return populateSequence(timeSig, soundfont, speedmarks,
                bookmarks, data, tempo);
    }

    /**
     * Determines whether an input file is a valid Advanced Mario Sequencer
     * file.
     * @param in The String that is supposedly a representation of the Advanced
     * Mario Sequencer file.
     * @return <b>True</b> if the file happens to be valid. <b>False</b>
     * otherwise.
     */
    private static boolean isValid(String in) {
        // TODO Auto-generated method stub
        return false;
    }

    /**
     * Creates a new Super Mario Paint song
     * @param timeSig The time signature of the song.
     * @param soundfont The soundfont of the song
     * @param speedmarks The speedmarks present in the Advanced Mario Sequencer
     * song.
     * @param bookmarks The bookmarks present in the Advanced Mario Sequencer
     * song.
     * @param tempo The tempo that this song is to run at.
     * @param data The song data that has been parsed.
     * @return A new Super Mario Paint sequence that is to be loaded
     * immediately upon creation.
     */
    private static StaffSequence populateSequence(String timeSig,
            SMPSoundfont soundfont, ArrayList<Speedmark> speedmarks,
            ArrayList<Bookmark> bookmarks, ArrayList<String> data,
            String tempo) {
        return null;
    }

}
<file_sep>package smp.advanced;

/**
 * Advanced version of Super Mario Paint, a
 * hidden mode.
 * Currently a skeleton.
 * @author RehdBlob
 * @since 2012.10.29
 */
public class AdvancedVersion {

}
<file_sep>package smp.components.staff;

import java.util.ArrayList;
import java.util.Set;

import smp.ImageIndex;
import smp.ImageLoader;
import smp.SoundfontLoader;
import smp.commandmanager.ModifySongManager;
import smp.commandmanager.commands.AddNoteCommand;
import smp.commandmanager.commands.AddVolumeCommand;
import smp.commandmanager.commands.RemoveNoteCommand;
import smp.commandmanager.commands.RemoveVolumeCommand;
import smp.components.Values;
import smp.components.InstrumentIndex;
import smp.components.staff.sequences.StaffAccidental;
import smp.components.staff.sequences.StaffNote;
import smp.components.staff.sequences.StaffNoteLine;
import smp.components.topPanel.ButtonLine;
import smp.stateMachine.Settings;
import smp.stateMachine.StateMachine;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;

/**
 * THIS IS A MODIFIED VERSION OF REHDBLOB's STAFF EVENT HANDLER. IT IS MADE IN
 * RESPONSE TO THE MULTITHREADING ISSUE WITH ARRAYLISTS AND STACKPANES. THERE
 * WILL ONLY BE ONE OF THIS EVENT HANDLER AND IT WILL BE ADDED TO THE ENTIRE
 * SCENE. WITH THIS HANDLER, ALL STACKPANES ARE DISABLED AND EVENTS WILL BE
 * REGISTERED TO THE STACKPANES BY CALCULATING WHICH PANE TO FETCH VIA MOUSE
 * POSITION.
 * 
 * A Staff event handler. The StaffImages implementation was getting bulky and
 * there are still many many features to be implemented here. This handler
 * primarily handles mouse events.
 *
 * @author RehdBlob
 * @author j574y923
 * @since 2013.07.27 (2017.06.22)
 */
public class StaffInstrumentEventHandler implements EventHandler<Event> {

    /** The line number of this note, on the screen. */
    private int line;

    /** The position of this note. */
    private int position;

    /** Whether the mouse is in the frame or not. */
    private static boolean focus = false;

    /**
     * This is the list of image notes that we have. These should all be
     * ImageView-type objects.
     */
    private static ObservableList<Node> theImages;

    /** The StackPane that will display sharps, flats, etc. */
    private static ObservableList<Node> accList;

    /**
     * This is the <code>ImageView</code> object responsible for displaying the
     * silhouette of the note that we are about to place on the staff.
     */
    private static ImageView silhouette = new ImageView();

    /** The pointer to the staff object that this handler is linked to. */
    private Staff theStaff;

    /**
     * This is the <code>ImageView</code> object responsible for displaying the
     * silhouette of the sharp / flat of the note that we are about to place on
     * the staff.
     */
    private static ImageView accSilhouette;

    /** The topmost image of the instrument. */
    private StaffNote theStaffNote;

    /**
     * This is the image that holds the different types of sharps/flats etc.
     */
    private StaffAccidental accidental;
    
    /** This is the ImageLoader class. */
    private static ImageLoader il;

    /** This is the amount that we want to sharp / flat / etc. a note. */
    private static int acc = 0;
    
    private ModifySongManager commandManager;
    
    /**
     * Constructor for this StaffEventHandler. This creates a handler that takes
     * a StackPane and a position on the staff.
     *
     * @param s
     *            The pointer to the Staff object that this event handler is
     *            linked to.
     * @param ct 
     * @param i
     * 			  The program's image loader.
     * @param cm
     * 			  The undo/redo manager.
     */
    public StaffInstrumentEventHandler(Staff s, ImageLoader i, ModifySongManager cm) {
    	
        il = i;
        theStaff = s;
        accSilhouette = new ImageView();
        
        accSilhouette.setFitWidth(32);
        accSilhouette.setFitHeight(32);
        silhouette.setFitWidth(32);
        silhouette.setFitHeight(36);
        
        commandManager = cm;
        
        if ((Settings.debug & 0b10) == 0b10) {
//            System.out.println("Line: " + l);
//            System.out.println("Position: " + pos);
        }
    }

	/**
	 * Disables all the stack panes. 
	 */
	private void disableAllStackPanes() {
		for (int index = 0; index < Values.NOTELINES_IN_THE_WINDOW; index++) {
			for (int i = 0; i < Values.NOTES_IN_A_LINE; i++) {
				StackPane[] noteAndAcc = theStaff.getNoteMatrix().getNote(index, i);
				noteAndAcc[0].setDisable(true);
			}
		}
	}

	@Override
    public void handle(Event event) {
    	
		boolean newNote = false;
    	if(event instanceof MouseEvent){
    		int lineTmp = getLine(((MouseEvent)event).getX());
    		int positionTmp = getPosition(((MouseEvent)event).getY());
    		
    		//invalid
    		if(!validNote(lineTmp, positionTmp))
    			return;
    		
    		//new note
    		newNote = updateNote(lineTmp, positionTmp);
    	}
    	
        InstrumentIndex theInd = ButtonLine.getSelectedInstrument();
        // Drag-add notes, hold e to drag-remove notes
		if (event instanceof MouseEvent && ((MouseEvent) event).isPrimaryButtonDown() && newNote) {
			leftMousePressed(theInd);
			event.consume();
			StateMachine.setSongModified(true);
		}
		// Drag-remove notes
		else if (event instanceof MouseEvent && ((MouseEvent) event).isSecondaryButtonDown() && newNote) {
			rightMousePressed(theInd);
			event.consume();
			StateMachine.setSongModified(true);
		}
		else if (event.getEventType() == MouseEvent.MOUSE_PRESSED) {
			MouseButton b = ((MouseEvent) event).getButton();
            if (b == MouseButton.PRIMARY)
                leftMousePressed(theInd);
            else if (b == MouseButton.SECONDARY)
                rightMousePressed(theInd);
            event.consume();
            StateMachine.setSongModified(true);
        } else if (event.getEventType() == MouseEvent.MOUSE_MOVED) {
            focus = true;
            mouseEntered(theInd);
            event.consume();
        } else if (event.getEventType() == MouseEvent.MOUSE_EXITED) {
            focus = false;
            mouseExited(theInd);
            event.consume();
        } 
        else if (event.getEventType() == MouseEvent.MOUSE_RELEASED) {
        	mouseReleased();
        }
    }

	private void mouseReleased() {
		commandManager.record();
	}

	/**
	 * Takes in a line and position and check if they are valid. If the note is
	 * not valid then it will call mouseExited().
	 * 
	 * @param lineTmp
	 * @param positionTmp
	 * @return if the given line and position are at a valid note.
	 */
	private boolean validNote(int lineTmp, int positionTmp) {
		// MOUSE_EXITED
		if (lineTmp < 0 || positionTmp < 0) {
			InstrumentIndex theInd = ButtonLine.getSelectedInstrument();
			mouseExited(theInd);
			return false;
		}
		return true;
	}
	
	/**
	 * Updates member variables to new note values. Detects if stackpanes are
	 * disabled and if they aren't this will disable them.
	 * 
	 * @param lineTmp
	 *            the note's line
	 * @param positionTmp
	 *            the note's position
	 * @return success if we updated note to new line or position
	 */
	private boolean updateNote(int lineTmp, int positionTmp) {
		if(line != lineTmp || position != positionTmp){
			
			line = lineTmp;
			position = positionTmp;
			StackPane[] noteAndAcc = theStaff.getNoteMatrix().getNote(line, position);
			
			if(!noteAndAcc[0].isDisabled())
				disableAllStackPanes();
			
			theImages = noteAndAcc[0].getChildren();
			accList = noteAndAcc[1].getChildren();
			
			return true;
		}
		return false;
	}
	
    /**
     * The method that is called when the left mouse button is pressed. This is
     * generally the signal to add an instrument to that line.
     *
     * @param theInd
     *            The InstrumentIndex corresponding to what instrument is
     *            currently selected.
     */
    private void leftMousePressed(InstrumentIndex theInd) {
        if (StateMachine.getButtonsPressed().contains(KeyCode.E)) {
            removeNote();
        } else {
            placeNote(theInd);
        }
    }

    /**
     * Places a note where the mouse currently is.
     *
     * @param theInd
     *            The <code>InstrumentIndex</code> that we are going to use to
     *            place this note.
     */
    private void placeNote(InstrumentIndex theInd) {
        boolean mute = StateMachine.isMutePressed();
        boolean muteA = StateMachine.isMuteAPressed();

        if (!mute && !muteA)
            playSound(theInd, position, acc);

        theStaffNote = new StaffNote(theInd, position, acc);

        theStaffNote.setMuteNote(muteA ? 2 : mute ? 1 : 0);

        if (!mute && !muteA) {
            theStaffNote.setImage(il.getSpriteFX(theInd.imageIndex()));
        } else if (mute) {
            theStaffNote.setImage(il.getSpriteFX(theInd.imageIndex().alt()));
        } else if (muteA) {
            theStaffNote.setImage(il.getSpriteFX(theInd.imageIndex()
                    .silhouette()));
        }

        accidental = new StaffAccidental(theStaffNote);
        accidental.setImage(il.getSpriteFX(Staff.switchAcc(acc)));
        
        accidental.setFitWidth(32);
        accidental.setFitHeight(32);

        theImages.remove(silhouette);
        accList.remove(accSilhouette);

        if (!theImages.contains(theStaffNote))
            theImages.add(theStaffNote);

        if (!accList.contains(accidental))
            accList.add(accidental);

        StaffNoteLine temp = theStaff.getSequence().getLine(
                line + StateMachine.getMeasureLineNum());

        if (temp.isEmpty()) {
            temp.setVolumePercent(((double) Values.DEFAULT_VELOCITY)
                    / Values.MAX_VELOCITY);
            commandManager.execute(new AddVolumeCommand(temp, Values.DEFAULT_VELOCITY));
        }

        if (!temp.contains(theStaffNote)) {
            temp.add(theStaffNote);
            commandManager.execute(new AddNoteCommand(temp, theStaffNote));
        }
        StaffVolumeEventHandler sveh = theStaff.getNoteMatrix().getVolHandler(
                line);
        sveh.updateVolume();
        theStaff.redraw();
    }

    /**
     * The method that is called when the right mouse button is pressed. This is
     * generally the signal to remove the instrument from that line.
     *
     * @param theInd
     *            The InstrumentIndex corresponding to what instrument is
     *            currently selected. (currently not actually used, but can be
     *            extended later to selectively remove instruments.
     */
    private void rightMousePressed(InstrumentIndex theInd) {

        removeNote();

    }

    /**
     * This removes a note.
     */
    private void removeNote() {
        theImages.remove(silhouette);
        accList.remove(accSilhouette);

        if (!theImages.isEmpty())
            theImages.remove(theImages.size() - 1);
        if (!accList.isEmpty())
            accList.remove(0);

        StaffNoteLine temp = theStaff.getSequence().getLine(
                line + StateMachine.getMeasureLineNum());

        if (!temp.isEmpty()) {
            ArrayList<StaffNote> nt = temp.getNotes();
            for (int i = nt.size() - 1; i >= 0; i--) {
                StaffNote s = nt.get(i);
                if (s.getPosition() == position) {
                    StaffNote removedNote = nt.remove(i);
                    commandManager.execute(new RemoveNoteCommand(temp, removedNote));
                    break;
                }
            }
        }

        if (temp.isEmpty()) {
            StaffVolumeEventHandler sveh = theStaff.getNoteMatrix()
                    .getVolHandler(line);
            sveh.setVolumeVisible(false);
            commandManager.execute(new RemoveVolumeCommand(temp, temp.getVolume()));
        }
        theStaff.redraw();
    }

    /**
     * The method that is called when the mouse enters the object.
     *
     * @param theInd
     *            The InstrumentIndex corresponding to what instrument is
     *            currently selected.
     */
    private void mouseEntered(InstrumentIndex theInd) {
//        StateMachine.setFocusPane(this);
//        theStaff.getNoteMatrix().setFocusPane(this);
        updateAccidental();
        silhouette.setImage(il.getSpriteFX(theInd.imageIndex().silhouette()));
        if (!theImages.contains(silhouette))
            theImages.add(silhouette);
        accSilhouette.setImage(il
                .getSpriteFX(Staff.switchAcc(acc).silhouette()));
        if (!accList.contains(accSilhouette))
            accList.add(accSilhouette);
        silhouette.setVisible(true);
        accSilhouette.setVisible(true);
    }

    /**
     * The method that is called when the mouse exits the object.
     *
     * @param children
     *            List of Nodes that we have here, hopefully full of
     *            ImageView-type objects.
     * @param theInd
     *            The InstrumentIndex corresponding to what instrument is
     *            currently selected.
     */
    private void mouseExited(InstrumentIndex theInd) {

    	if(silhouette.getImage() != null)
    		theImages.remove(silhouette);
    	if(accSilhouette.getImage() != null)
    		accList.remove(accSilhouette);

    }

    /**
     * Updates how much we want to sharp / flat a note.
     */
    public static void updateAccidental() {
        if (!focus)
            return;
        Set<KeyCode> bp = StateMachine.getButtonsPressed();
        boolean ctrl = bp.contains(KeyCode.CONTROL);
        boolean shift = bp.contains(KeyCode.SHIFT);
        boolean alt = bp.contains(KeyCode.ALT) || bp.contains(KeyCode.ALT_GRAPH);

        if (alt && ctrl)
            acc = -2;
        else if (ctrl && shift)
            acc = 2;
        else if (shift)
            acc = 1;
        else if (alt || ctrl)
            acc = -1;
        else
            acc = 0;

        switch (acc) {
        case 2:
            accSilhouette.setImage(il.getSpriteFX(ImageIndex.DOUBLESHARP_SIL));
            break;
        case 1:
            accSilhouette.setImage(il.getSpriteFX(ImageIndex.SHARP_SIL));
            break;
        case -1:
            accSilhouette.setImage(il.getSpriteFX(ImageIndex.FLAT_SIL));
            break;
        case -2:
            accSilhouette.setImage(il.getSpriteFX(ImageIndex.DOUBLEFLAT_SIL));
            break;
        default:
            accSilhouette.setVisible(false);
            break;
        }

        if (acc != 0)
            accSilhouette.setVisible(true);

        if (acc != 0 && !accList.contains(accSilhouette))
            accList.add(accSilhouette);
        if (acc != 0 && !theImages.contains(silhouette)) {
            theImages.add(silhouette);
            silhouette.setImage(il.getSpriteFX(ButtonLine
                    .getSelectedInstrument().imageIndex().silhouette()));
            silhouette.setVisible(true);
        }
        //Cannot use this in a static context... will fix this later
//        if ((Settings.debug & 0b01) == 0b01) {
//            System.out.println(this);
//        }

    }

    /**
     * Called whenever we request a redraw of the staff.
     */
    public void redraw() {
        if (!focus)
            return;
        InstrumentIndex ind = ButtonLine.getSelectedInstrument();
        mouseExited(ind);
        mouseEntered(ind);
    }

    /**
     * Plays a sound given an index and a position.
     *
     * @param theInd
     *            The index at which this instrument is located at.
     * @param pos
     *            The position at which this note is located at.
     * @param acc
     *            The sharp / flat that we want to play this note at.
     */
    private static void playSound(InstrumentIndex theInd, int pos, int acc) {
        SoundfontLoader.playSound(Values.staffNotes[pos].getKeyNum(), theInd,
                acc);
    }

    /**
     * Sets the amount that we want to sharp / flat a note.
     *
     * @param accidental
     *            Any integer between -2 and 2.
     */
    public void setAcc(int accidental) {
        acc = accidental;
    }

    /**
     * @return The amount that a note is to be offset from its usual position.
     */
    public int getAcc() {
        return acc;
    }

    /**
     * @return The line that this handler is located on.
     */
    public int getLine() {
        return line;
    }

    /**
     * @return Whether the mouse is currently in the frame.
     */
    public boolean hasMouse() {
        return focus;
    }

    @Override
    public String toString() {
        String out = "Line: " + (StateMachine.getMeasureLineNum() + line)
                + "\nPosition: " + position + "\nAccidental: " + acc;
        return out;
    }
    
    /**
     * 
     * @param x mouse pos
     * @return line in the current window based on x coord
     */
    private static int getLine(double x){

    	if(x < 135 || x > 999)
    		return -1;
    	return (((int)x - 165) / 64);
    }
    
    /**
     * 
     * @param y mouse pos
     * @return note position based on y coord
     */
    private static int getPosition(double y){
    	if(y < 66 || y >= 526)
    		return -1;
    	return Values.NOTES_IN_A_LINE - (((int)y - 66) / 16) - 1;
    }
    

}
<file_sep>package smp.extras;

/**
 * Easter egg...
 * @author RehdBlob
 * @since 2013.04.02
 */
public class RPG {

    /** Displays the main menu of the RPG. */
    public void mainMenu() {

    }

}
<file_sep>package smp.components.general;

import smp.ImageIndex;
import smp.ImageLoader;
import smp.components.staff.Staff;
import smp.fx.SMPFXController;
import javafx.event.EventHandler;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.DragEvent;
import javafx.scene.input.MouseEvent;

/**
 * An image button, to be used as a background-type object.
 * To be extended by more concrete classes that use
 * images as backgrounds. Updated 2012-August-30 to
 * reflect change to JavaFX2.2.
 * @author RehdBlob
 * @since 2012.08.14
 */
public abstract class AbstractImageButton {

    /** This is the staff. */
    protected Staff theStaff;

    /**
     * Indicates whether the button is currently pressed.
     */
    protected boolean isPressed;

    /**
     * The image that displays when the button is not pressed.
     */
    protected Image notPressed;

    /**
     * The image that displays when the button is pressed.
     */
    protected Image pressed;

    /**
     * This is the object that this class will be manipulating
     * to act like a button.
     */
    protected ImageView theImage;

    /** This is the FXML controller class. */
    protected SMPFXController controller;
    
    /** This is the image loader class. */
    protected ImageLoader il;
    
    /**
     * @param i The ImageView passed to the Button
     * wrapper.
     * @param ct 
     */
    public AbstractImageButton(ImageView i, SMPFXController ct, ImageLoader im) {
        il = im;
        setController(ct);
        theImage = i;
        initializeHandler();
    }

    /**
     * Initializes the button click handler. Every time
     * the button is pressed, the react() method in this
     * class is called.
     */
    private void initializeHandler() {
        theImage.setOnMouseClicked(
                new EventHandler<MouseEvent>() {
                    @Override
                    public void handle(MouseEvent event) {
                        reactClicked(event);
                        event.consume();
                    }
                });
        theImage.setOnMousePressed(
                new EventHandler<MouseEvent>() {
                    @Override
                    public void handle(MouseEvent event) {
                        reactPressed(event);
                        event.consume();
                    }
                });
        theImage.setOnMouseReleased(
                new EventHandler<MouseEvent>() {
                    @Override
                    public void handle(MouseEvent event) {
                        reactReleased(event);
                        event.consume();
                    }
                });
        theImage.setOnDragDetected(
                new EventHandler<MouseEvent>() {
                    @Override
                    public void handle(MouseEvent event) {
                        reactDragDetected(event);
                        event.consume();
                    }
                });
        theImage.setOnDragDone(
                new EventHandler<DragEvent>() {
                    @Override
                    public void handle(DragEvent event) {
                        reactDragDone(event);
                        event.consume();
                    }
                });
    }

    /**
     * "Presses" the button by setting the image to <code>pressed</code>.
     */
    protected void pressImage() {
        theImage.setImage(pressed);
    }

    /**
     * "Releases" the button by setting the image to <code>notPressed</code>.
     */
    protected void releaseImage() {
        theImage.setImage(notPressed);
    }

    /**
     * Sets the <code>isPressed</code> parameter to <b>true</b>
     * but does <b>not</b> define what happens afterwards.
     */
    protected void setPressed() {
        isPressed = true;
    }

    /**
     * Sets the <code>isPressed</code> parameter to <b>false</b>
     * but does <b>not</b> define what happens afterwards.
     */
    protected void resetPressed() {
        isPressed = false;
    }

    /**
     * @return Whether this button is actually pressed or not.
     */
    public boolean isPressed() {
        return isPressed;
    }

    /**
     * This method is always called when a
     * MouseClicked event occurs. Defaults to no action.
     * @param event The event that occurred.
     */
    protected void reactClicked(MouseEvent event) {

    }

    /**
     * This method is always called when a
     * MousePressed event occurs. Defaults to no action.
     * @param event The event that occurred.
     */
    protected void reactPressed(MouseEvent event){

    }

    /**
     * This method is always called when a
     * MouseReleased event occurs. Defaults to no action.
     * @param event The event that occurred.
     */
    protected void reactReleased(MouseEvent event) {

    }


    /**
     * This method is always called when a Drag event is detected.
     * Usually defaults to no action.
     * @param event The event that occurred.
     */
    protected void reactDragDetected(MouseEvent event) {

    }

    /**
     * This method is always called when a Drag event is completed.
     * @param event The event that occurred.
     */
    protected void reactDragDone(DragEvent event) {

    }


    /**
     * Gets the images for the <code>pressed</code> and <code>notPressed</code>
     * versions of the button.
     */
    protected void getImages(ImageIndex pr, ImageIndex notPr) {
        pressed = il.getSpriteFX(pr);
        notPressed = il.getSpriteFX(notPr);
    }

    /** @return The <code>ImageView</code> object that this button holds. */
    public ImageView getImage() {
        return theImage;
    }

    /**
     * Sets the staff that this button is connected to.
     * @param s The staff we want to set.
     */
    public void setStaff(Staff s) {
        theStaff = s;
    }

    /** @return The staff that this button is connected to. */
    public Staff getStaff() {
        return theStaff;
    }

    /**
     * Sets the controller class.
     * @param ct The FXML controller class.
     */
    public void setController(SMPFXController ct) {
        controller = ct;
    }
    
}
<file_sep>package smp.components.staff.sequences.mpc;

import java.text.ParseException;
import java.util.ArrayList;

/**
 * A class that denotes a line of notes on the MPC staff. Pulled from
 * MPCTxtTools 1.07a.
 * @author RehdBlob
 * @since MPCTxtTools 1.07
 * @since 2013.04.08
 */
public class NoteLine {

    /** THe number of notes in an MPC song line. There are actually 6 even though
     * MPC itself can only play 5 at a time.
     */
    private static final int NOTESIZE = 6;

    /** List of notes in this line. */
    private ArrayList<String> notes;

    /** A character (a-q) denoting what volume that this note is played at. */
    private char vol;

    /** Makes a new note line without anything in it. */
    public NoteLine() {
        notes = new ArrayList<String>();
        vol = 'q';
    }

    /**
     * Makes a new note line from a String that we assume to be a note line.
     * @param parse This is the String that we are attempting to parse.
     */
    public NoteLine(String parse) throws ParseException {
        if (parse.length() < NOTESIZE + 1) {
            notes = new ArrayList<String>();
            for (int i = 0; i < NOTESIZE; i++)
                notes.add("");
            vol = 'q';
            return;
        }
        notes = TextUtil.dice(parse);
        vol = parse.charAt(parse.length() - 2);
        if (!(vol >= 'a' && vol <= 'q'))
            vol = 'q';
        if (notes.size() != NOTESIZE)
            throw new ParseException("Incorrect number of notes in a line!", 0);
    }

    /**
     * @return The volume of the notes in this note line.
     */
    public char vol() {
        return vol;
    }

    /**
     * @return The list of notes that this note line has.
     */
    public ArrayList<String> notes() {
        return notes;
    }

    /**
     * Gets a specific note at location x from this note line.
     * @param x The index at which we want to grab a note from.
     * @return The note at that index.
     */
    public String notes(int x) {
        if (x < 0 || x > notes.size() - 1)
            return null;
        return notes.get(x);
    }

    @Override
    public String toString() {
        StringBuilder s = new StringBuilder();
        int empty = 0;
        for (String s1 : notes)
            if (s1.isEmpty())
                empty++;
        if (!(empty >= NOTESIZE && vol == 'q')) {
            for (String s1 : notes) {
                s.append(s1);
                s.append("+");
            }
            s.append(vol);
        }
        s.append(":");
        return s.toString();
    }
}
<file_sep>package smp.presenters.staff;

import java.util.ArrayList;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import smp.ImageIndex;
import smp.ImageLoader;
import smp.TestMain;
import smp.components.Values;
import smp.models.staff.StaffNote;
import smp.models.staff.StaffNoteLine;
import smp.models.stateMachine.Variables;
import smp.models.stateMachine.Variables.WindowLines;
import smp.presenters.api.reattachers.NoteLineReattacher;

public class StaffAccidentalsPresenter {

	// TODO: auto-add these model comments
	// ====Models====
	private WindowLines windowLines;
	private IntegerProperty selectedAccidental;
	private BooleanProperty accSilhouetteVisible;
	private IntegerProperty selectedLine;
	private IntegerProperty selectedPosition;

	private ArrayList<NoteLineReattacher> noteLineReattachers;

	/** This is an HBox of VBoxes. Each VBox is a vertical list of StackPanes. */
	private HBox staffAccidentals;

	/** This is the matrix of flats / sharps / etc. */
	private ArrayList<ArrayList<StackPane>> accMatrix;
	
    /** Pointer to the image loader object. */
    private transient ImageLoader il = (ImageLoader) TestMain.imgLoader;
    
    private ImageView accSilhouette = new ImageView();

	public StaffAccidentalsPresenter(HBox staffAccidentals) {
		this.staffAccidentals = staffAccidentals;
        accMatrix = new ArrayList<ArrayList<StackPane>>();
        accSilhouette.setImage(il.getSpriteFX(ImageIndex.BLANK));

		this.windowLines = Variables.windowLines;
		this.selectedAccidental = Variables.selectedAccidental;
		this.accSilhouetteVisible = Variables.accSilhouetteVisible;
		this.selectedLine  = Variables.selectedLine;
		this.selectedPosition = Variables.selectedPosition;
		this.noteLineReattachers = new ArrayList<NoteLineReattacher>();
		initializeStaffInstruments(this.staffAccidentals);
		setupViewUpdater();
	}

	/**
	 * Sets up the various note lines of the staff. These are the notes that can
	 * appear on the staff. This method also sets up sharps, flats, etc.
	 *
	 * @param accidentals
	 *            The HBox that holds the framework for the sharps / flats.
	 */
	private void initializeStaffInstruments(HBox accidentals) {
		ArrayList<VBox> accidentalLines = new ArrayList<VBox>();
		for (Node n : accidentals.getChildren())
			accidentalLines.add((VBox) n);

		for (int line = 0; line < accidentalLines.size(); line++) {
			VBox accVerticalHolder = accidentalLines.get(line);

			ObservableList<Node> lineOfAcc = accVerticalHolder.getChildren();

			ArrayList<StackPane> accs = new ArrayList<StackPane>();

			for (int pos = 1; pos <= Values.NOTES_IN_A_LINE; pos++) {
				StackPane acc = (StackPane) lineOfAcc.get(pos - 1);
				accs.add(acc);
			}

			accMatrix.add(accs);
		}
	}

	private void setupViewUpdater() {
		for (int i = 0; i < windowLines.size(); i++) {
			final int index = i;
			final ObjectProperty<StaffNoteLine> windowLine = windowLines.get(i);
			NoteLineReattacher nlr = new NoteLineReattacher(windowLine);
			nlr.setNewNotesListener(new ListChangeListener<StaffNote>() {

				@Override
				public void onChanged(Change<? extends StaffNote> c) {
					clearNoteDisplay(index);
					populateNoteDisplay(windowLine.get(), index);
				}
			});
			nlr.setOnReattachListener(new ChangeListener<Object>() {

				@Override
				public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
					clearNoteDisplay(index);
					populateNoteDisplay((StaffNoteLine) newValue, index);
				}
			});
			noteLineReattachers.add(nlr);
		}

		//INSTRUMENTEVENTHANDLER
		this.selectedAccidental.addListener(new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				accSilhouette.setImage(il.getSpriteFX(switchAcc(newValue.intValue()).silhouette()));
			}
		});
		this.accSilhouette.visibleProperty().bindBidirectional(accSilhouetteVisible);
		this.selectedLine.addListener(new ChangeListener<Number>(){

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				moveAcc();
			}
		});
		this.selectedPosition.addListener(new ChangeListener<Number>(){

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				moveAcc();
			}
		});
	}
	
	private void moveAcc() {
		if(selectedLine.get() < 0 || selectedPosition.get() < 0)
			return;
		StackPane stp = getNote(selectedLine.get(), selectedPosition.get());
		stp.getChildren().add(accSilhouette);
	}
    /**
     * @param acc
     *            The offset that we are deciding upon.
     * @return An <code>ImageIndex</code> based on the amount of sharp or flat
     *         we want to implement.
     */
    public static ImageIndex switchAcc(int acc) {
        switch (acc) {
        case 2:
            return ImageIndex.DOUBLESHARP;
        case 1:
            return ImageIndex.SHARP;
        case 0:
            return ImageIndex.BLANK;
        case -1:
            return ImageIndex.FLAT;
        case -2:
            return ImageIndex.DOUBLEFLAT;
        default:
            return ImageIndex.BLANK;
        }
    }
    
    /**
     * Clears the note display on the staff.
     *
     * @param index
     *            The index that we are clearing.
     */
    private synchronized void clearNoteDisplay(int index) {
        ArrayList<StackPane> ac = accMatrix.get(index);
        for (int i = 0; i < Values.NOTES_IN_A_LINE; i++) {
            ObservableList<Node> acList = ac.get(i).getChildren();
            acList.clear();
        }
    }

    /**
     * Repopulates the note display on the staff.
     *
     * @param stl
     *            The StaffNoteLine that we are interested in.
     * @param index
     *            The index to repopulate.
     */
	private void populateNoteDisplay(StaffNoteLine stl, int index) {
		ObservableList<StaffNote> st = stl.getNotes();
		for (StaffNote s : st) {
			StackPane accSP = getNote(index, s.getPosition());
			ImageView accidental = new ImageView();
			accidental.setImage(il.getSpriteFX(switchAcc(s.getAccidental())));
			accSP.getChildren().add(accidental);
		}
    }
	
    /**
     * Gets you an object based on the coordinate that you give this method.
     * This method should help a lot when working on those portions of code that
     * ask the entire staff to update its images. Bypassing the individual
     * StackPane object links should be a lot easier with this here.
     *
     * @param x
     *            The note line number.
     * @param y
     *            The note number.
     * @return Index 0 is the <code>StackPane</code> of the note that is located
     *         at the location. Index 1 is the <code>StackPane</code> of the
     *         flat / sharp / etc box that it is associated with.
     */
    public StackPane getNote(int x, int y) {
        return accMatrix.get(x).get(Values.NOTES_IN_A_LINE - y - 1);
    }
}
<file_sep>package smp.presenters.api.reattachers;

import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import smp.models.staff.StaffNote;
import smp.models.staff.StaffNoteLine;

/**
 * This will reattach listeners to the StaffNoteLine's member properties when a
 * new noteline gets set.
 * 
 * @author J
 *
 */
public class NoteLineReattacher {

	// ====(not a Model)====
	private ObjectProperty<StaffNoteLine> noteLine;

	public ChangeListener<Number> volumeListener;

	public ListChangeListener<StaffNote> notesListener;

	public ChangeListener<Number> notesSizeListener;
	/**
	 * The listener for when a new noteLine is set. For instance, a new note
	 * line is set: the new note line's volume should be listened for and the
	 * volume view updated.
	 */
	public ChangeListener<Object> onReattachListener;
	
	public NoteLineReattacher(ObjectProperty<StaffNoteLine> noteLine) {
		this.noteLine = noteLine;
		setupReattacher();
	}

	private void setupReattacher() {
		this.noteLine.addListener(new ChangeListener<Object>() {

			@Override
			public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
				StaffNoteLine oldNoteLine = (StaffNoteLine) oldValue;
				if (volumeListener != null)
					oldNoteLine.getVolume().removeListener(volumeListener);
				if (notesListener != null)
					oldNoteLine.getNotes().removeListener(notesListener);

				StaffNoteLine newNoteLine = (StaffNoteLine) newValue;
				if (volumeListener != null)
					newNoteLine.getVolume().addListener(volumeListener);
				if (notesListener != null)
					newNoteLine.getNotes().addListener(notesListener);
			}
		});
	}

	public void setNewVolumeListener(ChangeListener<Number> volumeListener) {
		if (this.volumeListener != null)
			this.noteLine.get().getVolume().removeListener(this.volumeListener);
		this.volumeListener = volumeListener;
		this.noteLine.get().getVolume().addListener(this.volumeListener);
	}

	public void setNewNotesListener(ListChangeListener<StaffNote> notesListener) {
		if (this.notesListener != null)
			this.noteLine.get().getNotes().removeListener(this.notesListener);
		this.notesListener = notesListener;
		this.noteLine.get().getNotes().addListener(this.notesListener);
	}

	public void setNewNotesSizeListener(ChangeListener<Number> notesSizeListener) {
		if (this.notesSizeListener != null)
			this.noteLine.get().getNotesSize().removeListener(this.notesSizeListener);
		this.notesSizeListener = notesSizeListener;
		this.noteLine.get().getNotesSize().addListener(this.notesSizeListener);
	}

	public void setOnReattachListener(ChangeListener<Object> onReattachListener) {
		this.onReattachListener = onReattachListener;
		this.noteLine.addListener(onReattachListener);
	}
}
<file_sep>package smp.presenters;

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Slider;
import smp.components.Values;
import smp.models.staff.StaffSequence;
import smp.models.stateMachine.StateMachine;
import smp.models.stateMachine.Variables;
import smp.presenters.api.reattachers.SequenceReattacher;

public class ScrollbarPresenter {

	//TODO: auto-add these model comments
	//====Models====
	private DoubleProperty measureLineNum;
	private ObjectProperty<StaffSequence> theSequence;
	
	private Slider scrollbar;
	
	private SequenceReattacher sequenceReattacher;

	public ScrollbarPresenter(Slider scrollbar) {
		this.scrollbar = scrollbar;
		
		this.measureLineNum = StateMachine.getMeasureLineNum();
		this.theSequence = Variables.theSequence;
		this.sequenceReattacher = new SequenceReattacher(this.theSequence);
		setupViewUpdater();
	}
	
	private void setupViewUpdater() {
		scrollbar.valueProperty().bindBidirectional(this.measureLineNum);
		this.sequenceReattacher.setNewTheLinesSizeListener(new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> arg0, Number oldVal, Number newSize) {
				// TODO: confirm that max-checking occurs in the
				// StaffSequence OR whatever function is doing the resizing
				
				// Make sure the newVal is not less than the default size.
				// int newMax = Math.max(Values.DEFAULT_LINES_PER_SONG,
				// newSize.intValue());
				scrollbar.setMax(newSize.intValue() - Values.NOTELINES_IN_THE_WINDOW);
			}
		});
		this.sequenceReattacher.setOnReattachListener(new ChangeListener<StaffSequence>() {

			@Override
			public void changed(ObservableValue<? extends StaffSequence> observable, StaffSequence oldValue,
					StaffSequence newValue) {
				measureLineNum.set(0);
			}
		});
	}
}
<file_sep>package smp.models.stateMachine;

/**
 * This class defines the states that the
 * state machine references. Listeners may also check
 * these states to determine whether they are allowed
 * to react to events.
 * @author RehdBlob
 * @since 2012.08.12
 */
public enum ProgramState {

    /**
     * This is the default state of the program, which allows
     * the staff to be edited, moved, etc.
     */
    EDITING,

    /**
     * This state denotes that we are currently editing an arrangement file.
     */
    ARR_EDITING,

    /**
     * This state denotes that we are currently playing a song.
     */
    SONG_PLAYING,

    /**
     * Just like the SONG_PLAYING state, except that we are now playiing an
     * arrangement.
     */
    ARR_PLAYING,

    /**
     * This state disables all buttons except for the play and the stop
     * buttons, since the song is paused in this case.
     */
    PAUSE,

    /**
     * Whenever a menu is open, all buttons are disabled except for those in
     * the menu.
     */
    MENU_OPEN,

    /**
     * Easter egg.
     */
    RPG;
}
