dotaSoundEditor.Controls.EditorPanel.java Source code

Java tutorial

Introduction

Here is the source code for dotaSoundEditor.Controls.EditorPanel.java

Source

/* 
 * The MIT License
 *
 * Copyright 2015 Neil McAlister.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package dotaSoundEditor.Controls;

import dotaSoundEditor.Helpers.SoundPlayer;
import dotaSoundEditor.Helpers.ScriptParser;
import dotaSoundEditor.Helpers.Utility;
import dotaSoundEditor.*;
import dotaSoundEditor.Helpers.CacheManager;
import info.ata4.vpk.VPKArchive;
import info.ata4.vpk.VPKEntry;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Scanner;
import javax.sound.sampled.AudioInputStream;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.tree.*;
import org.apache.commons.io.FileUtils;

public abstract class EditorPanel extends JPanel {
    protected TreeModel currentTreeModel;
    protected SoundPlayer soundPlayer;
    protected JTree currentTree;
    protected JComboBox currentDropdown;
    protected String vpkPath;
    protected String installDir;
    protected CacheManager cacheManager;
    //TODO: Create some ActionListeners that pay attention to this value and react to it changing
    boolean inAdvancedMode = false;

    abstract void populateSoundList();

    abstract void fillImageFrame(Object selectedItem) throws IOException;

    abstract void populateDropdownBox();

    abstract String getCurrentScriptString();

    abstract String getCustomSoundPathString();

    abstract void updateCache(String scriptString, long internalCrc);

    protected File promptUserForNewFile(String wavePath) {
        JFileChooser chooser = new JFileChooser(new File(UserPrefs.getInstance().getWorkingDirectory()));
        FileNameExtensionFilter filter = new FileNameExtensionFilter("MP3s and WAVs", "mp3", "wav");
        chooser.setAcceptAllFileFilterUsed((false));
        chooser.setFileFilter(filter);
        chooser.setMultiSelectionEnabled(false);

        int chooserRetVal = chooser.showOpenDialog(chooser);
        if (chooserRetVal == JFileChooser.APPROVE_OPTION) {
            DefaultMutableTreeNode selectedFile = (DefaultMutableTreeNode) getTreeNodeFromWavePath(wavePath);
            Path chosenFile = Paths.get(chooser.getSelectedFile().getAbsolutePath());
            //Strip caps and spaces out of filenames. The item sound parser seems to have trouble with them.
            String destFileName = chosenFile.getFileName().toString().toLowerCase().replace(" ", "_");
            Path destPath = Paths.get(installDir, "/dota/sound/" + getCustomSoundPathString() + destFileName);
            UserPrefs.getInstance().setWorkingDirectory(chosenFile.getParent().toString());

            try {
                new File(destPath.toString()).mkdirs();
                Files.copy(chosenFile, destPath, StandardCopyOption.REPLACE_EXISTING);

                String waveString = selectedFile.getUserObject().toString();
                int startIndex = -1;
                int endIndex = -1;
                if (waveString.contains("\"wave\"")) {
                    startIndex = Utility.nthOccurrence(selectedFile.getUserObject().toString(), '\"', 2);
                    endIndex = Utility.nthOccurrence(selectedFile.getUserObject().toString(), '\"', 3);
                } else //Some wavestrings don't have the "wave" at the beginning for some reason
                {
                    startIndex = Utility.nthOccurrence(selectedFile.getUserObject().toString(), '\"', 0);
                    endIndex = Utility.nthOccurrence(selectedFile.getUserObject().toString(), '\"', 1);
                }

                String waveStringFilePath = waveString.substring(startIndex, endIndex + 1);
                waveString = waveString.replace(waveStringFilePath,
                        "\"" + getCustomSoundPathString() + destFileName + "\"");
                selectedFile.setUserObject(waveString);

                //Write out modified tree to scriptfile.
                ScriptParser parser = new ScriptParser(this.currentTreeModel);
                String scriptString = getCurrentScriptString();
                Path scriptPath = Paths.get(scriptString);
                parser.writeModelToFile(scriptPath.toString());

                //Update UI
                ((DefaultMutableTreeNode) currentTree.getLastSelectedPathComponent()).setUserObject(waveString);
                ((DefaultTreeModel) currentTree.getModel())
                        .nodeChanged((DefaultMutableTreeNode) currentTree.getLastSelectedPathComponent());
                JOptionPane.showMessageDialog(this, "Sound file successfully replaced.");

            } catch (IOException ex) {
                JOptionPane.showMessageDialog(null, "Unable to replace sound.\nDetails: " + ex.getMessage(),
                        "Error", JOptionPane.ERROR_MESSAGE);
            }
        }
        return null;
    }

    //Overridden in VoicePanel, due to using a different VPK.
    protected void attachDoubleClickListenerToTree() {
        MouseListener ml = new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                int selRow = currentTree.getRowForLocation(e.getX(), e.getY());
                TreePath selPath = currentTree.getPathForLocation(e.getX(), e.getY());
                if (selRow != -1 && ((DefaultMutableTreeNode) selPath.getLastPathComponent()).isLeaf()) {
                    if (e.getClickCount() == 2) {
                        playSelectedTreeSound(selPath, Paths.get(vpkPath));
                    }
                }
            }
        };
        currentTree.addMouseListener(ml);
    }

    protected void playSelectedTreeSound(TreePath selPath, Path vpkToPlayFrom) {
        try {
            DefaultMutableTreeNode selectedFile = ((DefaultMutableTreeNode) selPath.getLastPathComponent());
            String waveString = selectedFile.getUserObject().toString();
            File soundFile = createSoundFileFromWaveString(waveString, vpkToPlayFrom);
            soundPlayer.loadSound(soundFile.getAbsolutePath());
            soundPlayer.playSound();
        } catch (Exception ex) {
            JOptionPane.showMessageDialog(null, "The selected node does not represent a valid sound file.", "Error",
                    JOptionPane.ERROR_MESSAGE);
        }
    }

    protected ArrayList<String> getWavePathsAsList(TreeNode selectedFile) {
        ArrayList<String> wavePathsList = new ArrayList<>();
        Enumeration e = selectedFile.children();
        while (e.hasMoreElements()) {
            Object currentElement = e.nextElement();

            //If a soundfile has multiple possible wavefiles
            if (currentElement.toString().contains("\"rndwave\"")) {
                Enumeration innerE = ((TreeNode) currentElement).children();
                while (innerE.hasMoreElements()) {
                    Object currentInnerElement = innerE.nextElement();
                    if ((currentInnerElement.toString().contains("\"wave\"")
                            || currentInnerElement.toString().contains(".wav")
                            || currentInnerElement.toString().contains(".mp3"))
                            && !currentInnerElement.toString().trim().startsWith("//")) {
                        //Maybe do some string massaging here before we just hand it back
                        wavePathsList.add(((TreeNode) currentInnerElement).toString());
                    }
                }
            }
            //If it only has one
            else if (currentElement.toString().contains("\"wave\"")) {
                if (!currentElement.toString().trim().startsWith("//")) {
                    wavePathsList.add(((TreeNode) currentElement).toString());
                }
            }
        }
        return wavePathsList;
    }

    private File createSoundFileFromWaveString(String waveString, Path vpkToPlayFrom) {
        if (!(waveString.contains(".wav") || (waveString.contains(".mp3")))) {
            return null;
        }

        File file = new File(vpkToPlayFrom.toString());
        VPKArchive vpk = new VPKArchive();
        File entryFile = new File("");

        String waveSubstring = "";
        int startIndex = -1;
        int endIndex = -1;
        if (waveString.contains("\"wave\"")) {
            startIndex = Utility.nthOccurrence(waveString, '\"', 2);
            endIndex = Utility.nthOccurrence(waveString, '\"', 3);
        }
        //For weird special cases where the "wave" part of the string is missing, i.e. Treant's Overgrowth.Target spell
        else {
            startIndex = Utility.nthOccurrence(waveString, '\"', 0);
            endIndex = Utility.nthOccurrence(waveString, '\"', 1);
        }

        waveSubstring = waveString.substring(startIndex, endIndex + 1);
        waveSubstring = waveSubstring.replace(")", "");
        waveSubstring = waveSubstring.replace("\"", "");
        waveSubstring = waveSubstring.replace("\\", "/");
        waveSubstring = waveSubstring.replace("#", "");
        waveSubstring = waveSubstring.replace("*", "");

        if (!waveString.contains("custom") && !waveString.contains("//Replaced")) {
            File localFile = new File(Paths.get(installDir + "/sound/" + waveSubstring).toString());
            if (localFile.isFile()) {
                return localFile;
            }

            try {
                vpk.load(file);
            } catch (Exception ex) {
                System.err.println("Can't open archive: " + ex.getMessage());
            }
            waveSubstring = "sound/" + waveSubstring;
            VPKEntry entry = vpk.getEntry(waveSubstring.toLowerCase());

            entryFile = entry.getType().contains("wav")
                    ? new File(Paths.get(System.getProperty("user.dir") + "/scratch/scratch.wav").toString())
                    : new File(Paths.get(System.getProperty("user.dir") + "/scratch/scratch.mp3").toString());

            try (FileChannel fc = FileUtils.openOutputStream(entryFile).getChannel()) {
                fc.write(entry.getData());
            } catch (IOException ex) {
                ex.printStackTrace();
            }

            return entryFile;
        } else //If it's NOT stored in the VPK, it's on the local filesys
        {
            entryFile = new File(Paths.get(installDir, "/dota/sound/" + waveSubstring).toString());
            return entryFile;
        }
    }

    protected TreeNode getTreeNodeFromWavePath(String wavePath) {
        TreeModel model = this.currentTreeModel;

        TreeNode root = (TreeNode) model.getRoot();
        for (Enumeration e = ((DefaultMutableTreeNode) root).breadthFirstEnumeration(); e.hasMoreElements()
                && root != null;) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement();
            if (node.toString().contains(wavePath)) {
                return node;
            }
        }
        return null;
    }

    protected void deleteSoundFileByWaveString(String selectedWaveString) {
        int startIndex = -1;
        int endIndex = -1;
        if (selectedWaveString.contains("\"wave\"")) {
            startIndex = Utility.nthOccurrence(selectedWaveString, '\"', 2);
            endIndex = Utility.nthOccurrence(selectedWaveString, '\"', 3);
        } else {
            startIndex = Utility.nthOccurrence(selectedWaveString, '\"', 1);
            endIndex = Utility.nthOccurrence(selectedWaveString, '\"', 2);
        }

        String waveSubstring = selectedWaveString.substring(startIndex, endIndex + 1);
        waveSubstring = waveSubstring.replace(")", "");
        waveSubstring = waveSubstring.replace("\"", "");
        waveSubstring = waveSubstring.replace("*", "");
        File soundFileToDelete = new File(Paths.get(installDir, "/dota/sound/" + waveSubstring).toString());
        if (soundFileToDelete.isFile()) {
            soundFileToDelete.delete();
        } else {
            System.err.println("Cannot find and delete custom sound file " + waveSubstring);
        }
    }

    protected ArrayList<String> getWavePathListFromString(String scriptString) {
        ArrayList<String> wavePathsList = new ArrayList<>();
        BufferedReader buf = new BufferedReader(new StringReader(scriptString));
        String line = null;
        try {
            while ((line = buf.readLine()) != null) {
                //Stop reading after we're done with the wave paths               
                if (line.contains("soundentry")) {
                    break;
                }

                if (line.contains("\"wave\"") || line.contains(".wav") || line.contains(".mp3")) {
                    wavePathsList.add(line);
                }
            }
            return wavePathsList;
        } catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }
    }

    public boolean validateScriptFile(String scriptKey, String scriptPath) {
        if (cacheManager == null) {
            cacheManager = new CacheManager();
        }
        cacheManager.putScriptPath(scriptKey, scriptPath);
        long crc = cacheManager.getSessionCrc(scriptKey);
        if (crc == 0) {
            return false;
        }
        return validateScriptFile(scriptKey, crc);
    }

    public boolean validateScriptFile(String scriptKey, long internalCrc) {
        if (cacheManager == null) {
            cacheManager = new CacheManager();
        }
        if (!cacheManager.isUpToDate(scriptKey, internalCrc)) {
            return false;
        } else {
            return true;
        }
    }

    /**
     *
     * @param oldTree The tree that was previously in use.
     * @param scriptPath The full filepath to the script.
     * @return The merged tree.
     */
    TreeModel mergeNewChanges(TreeModel oldTree, String scriptPath) {
        return mergeNewChanges(oldTree, new File(scriptPath));
    }

    /**
     * @param oldTree The tree that was previously in use
     * @param scriptPath The full filepath to the script.
     * @return The merged tree.
     */
    TreeModel mergeNewChanges(TreeModel oldTree, Path scriptPath) {
        return mergeNewChanges(oldTree, scriptPath.toFile());
    }

    /**
     * @param oldTree The tree that was previously in use
     * @param scriptFilePath A File object pointing to or containing a script.
     * @return The merged tree.
     */
    TreeModel mergeNewChanges(TreeModel oldTree, File scriptFilePath) {
        //Look for any modified wavestrings. Save their nodes, and note their indices.
        //Parse in updated script tree, replace nodes at indices with saved nodes.   
        //Return merged tree
        System.out.println("Running a merge operation!");

        TreeNode oldRoot = (TreeNode) oldTree.getRoot();
        ArrayList<DefaultMutableTreeNode> savedNodeList = new ArrayList<>();
        for (Enumeration e = ((DefaultMutableTreeNode) oldRoot).depthFirstEnumeration(); e.hasMoreElements()
                && oldRoot != null;) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement();
            if (node.getUserObject().toString().contains("custom\\")
                    || node.getUserObject().toString().contains("custom/")) {
                savedNodeList.add(node);
            }
        }
        ScriptParser parser = new ScriptParser(scriptFilePath);
        TreeModel newTree = parser.getTreeModel();
        TreeNode newRoot = (TreeNode) newTree.getRoot();
        for (DefaultMutableTreeNode savedNode : savedNodeList) {
            int rndwaveIndex = -1;
            int childIndex = -1;
            int parentIndex = -1;
            DefaultMutableTreeNode parent = (DefaultMutableTreeNode) savedNode.getParent();
            if (parent.getUserObject().toString().contains("rndwave")) {
                rndwaveIndex = parent.getParent().getIndex(parent);
                childIndex = parent.getIndex(savedNode);
                parent = (DefaultMutableTreeNode) parent.getParent();
                parentIndex = parent.getParent().getIndex(parent);
            } else {
                parentIndex = parent.getParent().getIndex(parent);
                childIndex = parent.getIndex(savedNode);
            }

            TreeNode newParentNode = newRoot.getChildAt(parentIndex);
            TreeNode newChildNode;
            if (rndwaveIndex != -1) {
                newChildNode = newParentNode.getChildAt(rndwaveIndex);
                newChildNode = newChildNode.getChildAt(childIndex);
            } else {
                newChildNode = newParentNode.getChildAt(childIndex);
            }
            newChildNode = savedNode;
        }
        return newTree;
    }

    void advancedButtonActionPerformed(java.awt.event.ActionEvent evt, JButton advancedButton) {
        if (!getAdvancedMode()) {
            setAdvancedMode(true);
            String scriptPath = getCurrentScriptString();
            ScriptParser parser = new ScriptParser(new File(Paths.get(scriptPath).toString()));
            TreeModel model = parser.getTreeModel();
            currentTree.setModel(model);
            currentTree.setEditable(true);
            for (int i = 0; i < currentTree.getRowCount(); i++) {
                currentTree.expandRow(i);
            }

            //Change button and action to Basic-revert:
            advancedButton.setText("Basic <<");
            advancedButton.setMnemonic('a');
        } else if (getAdvancedMode()) {
            setAdvancedMode(false);
            this.populateSoundList();
            advancedButton.setText("Advanced >>");
            advancedButton.setMnemonic('a');
            currentTree.setEditable(false);
        }
    }

    /**
     * @return Whether or not the panel is currently in advanced mode or not.
     */
    public boolean getAdvancedMode() {
        return inAdvancedMode;
    }

    public void setAdvancedMode(boolean _newMode) {
        inAdvancedMode = _newMode;
    }

    protected TreeModel buildSoundListTree(TreeModel scriptTree) {
        TreeNode rootNode = (TreeNode) scriptTree.getRoot();
        int childCount = rootNode.getChildCount();
        TreeModel soundListTreeModel = new DefaultTreeModel(new DefaultMutableTreeNode("root"));
        ArrayList<String> wavePathsList = new ArrayList<>();
        for (int i = 0; i < childCount; i++) {
            String nodeValue = scriptTree.getChild(rootNode, i).toString();
            if (nodeValue.trim().startsWith("//")) {
                continue;
            }
            wavePathsList = getWavePathsAsList((TreeNode) scriptTree.getChild(rootNode, i));
            DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(nodeValue);
            for (String s : wavePathsList) {
                DefaultMutableTreeNode tempNode = new DefaultMutableTreeNode(s);
                newNode.add(tempNode);
            }
            ((DefaultMutableTreeNode) soundListTreeModel.getRoot()).add(newNode);
        }
        return soundListTreeModel;
    }

    protected void writeScriptFileToDisk(VPKEntry entryToWrite, boolean overwriteExisting) {
        File existsChecker = new File(Paths.get(installDir, entryToWrite.getPath()).toString());
        boolean fileExistsLocally = existsChecker.exists();
        if (fileExistsLocally && !overwriteExisting) {
            return;
        }
        File entryFile = new File(Paths.get(installDir, "/dota/").toFile(), entryToWrite.getPath());
        File entryDir = entryFile.getParentFile();
        if (entryDir != null && !entryDir.exists()) {
            entryDir.mkdirs();
        }
        try (final FileChannel fc = FileUtils.openOutputStream(entryFile).getChannel()) {
            fc.write(entryToWrite.getData());
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(this,
                    "Error: Unable to write script file to disk.\nDetails: " + ex.getMessage(),
                    "Error writing script file", JOptionPane.ERROR_MESSAGE);
        }
    }

    protected void revertButtonActionPerformed(ActionEvent evt) {
        revertButtonActionPerformed(evt, Paths.get(vpkPath));
    }

    protected void revertButtonActionPerformed(ActionEvent evt, Path vpkToRevert) {
        //TODO: See if we can abstract away some of this functionality
        if (currentTree.getSelectionRows().length != 0
                && ((TreeNode) currentTree.getSelectionPath().getLastPathComponent()).isLeaf()) {
            DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) currentTree.getSelectionPath()
                    .getLastPathComponent();
            String selectedWaveString = ((DefaultMutableTreeNode) selectedNode).getUserObject().toString();
            String selectedWaveParentString = ((DefaultMutableTreeNode) ((DefaultMutableTreeNode) selectedNode)
                    .getParent()).getUserObject().toString();
            selectedNode = (DefaultMutableTreeNode) this.getTreeNodeFromWavePath(selectedWaveString);
            //First go in and delete the sound in customSounds
            deleteSoundFileByWaveString(selectedWaveString);
            //Get the relevant wavestring from the internal scriptfile
            VPKArchive vpk = new VPKArchive();
            try {
                vpk.load(new File(vpkToRevert.toString()));
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            String scriptDir = getCurrentScriptString();
            scriptDir = scriptDir.replace(Paths.get(installDir, "/dota/").toString(), "");
            scriptDir = scriptDir.replace("\\", "/"); //Match internal forward slashes
            scriptDir = scriptDir.substring(1); //Cut off leading slash
            byte[] bytes = null;
            VPKEntry entry = vpk.getEntry(scriptDir);
            try {
                ByteBuffer scriptBuffer = entry.getData();
                bytes = new byte[scriptBuffer.remaining()];
                scriptBuffer.get(bytes);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            String scriptFileString = new String(bytes, Charset.forName("UTF-8"));
            ArrayList<String> wavePathList = this.getWavePathsAsList(selectedNode.getParent());
            int waveStringIndex = wavePathList.indexOf(selectedWaveString);
            //Cut off every part of the scriptFileString before we get to the entry describing the relevant hero action, so we don't accidentally get the wrong wavepaths
            StringBuilder scriptFileStringShortened = new StringBuilder();
            Scanner scan = new Scanner(scriptFileString);
            boolean found = false;
            while (scan.hasNextLine()) {
                String curLine = scan.nextLine();
                if (curLine.equals(selectedWaveParentString)) {
                    found = true;
                }
                if (found == true) {
                    scriptFileStringShortened.append(curLine).append(System.lineSeparator());
                }
            }
            scriptFileString = scriptFileStringShortened.toString();
            ArrayList<String> internalWavePathsList = getWavePathListFromString(scriptFileString);
            String replacementString = internalWavePathsList.get(waveStringIndex);
            selectedNode.setUserObject(replacementString);
            ScriptParser parser = new ScriptParser(this.currentTreeModel);
            parser.writeModelToFile(getCurrentScriptString());
            //Modify the UI treeNode in addition to the backing TreeNode
            ((DefaultMutableTreeNode) currentTree.getLastSelectedPathComponent()).setUserObject(replacementString);
            ((DefaultTreeModel) currentTree.getModel())
                    .nodeChanged((DefaultMutableTreeNode) currentTree.getLastSelectedPathComponent());
        }
    }

    protected void playSoundButtonActionPerformed(ActionEvent evt) {
        playSoundButtonActionPerformed(evt, Paths.get(vpkPath));
    }

    protected void playSoundButtonActionPerformed(ActionEvent evt, Path vpkToPlayFrom) {
        if (currentTree.getSelectionRows().length != 0
                && ((TreeNode) currentTree.getSelectionPath().getLastPathComponent()).isLeaf()) {
            this.playSelectedTreeSound(currentTree.getSelectionPath(), vpkToPlayFrom);
        }
    }

    protected void replaceButtonActionPerformed(ActionEvent evt) {
        if (currentTree.getSelectionRows() != null && currentTree.getSelectionRows().length != 0
                && ((TreeNode) currentTree.getSelectionPath().getLastPathComponent()).isLeaf()) {
            TreeNode selectedFile = (TreeNode) currentTree.getSelectionPath().getLastPathComponent();
            promptUserForNewFile(selectedFile.toString());
        }
    }

    protected void revertAllButtonActionPerformed(ActionEvent evt) {
        //Delete existing script file
        String scriptFilePath = getCurrentScriptString();
        File scriptFileToDelete = new File(scriptFilePath);
        if (scriptFileToDelete.isFile()) {
            try {
                Files.delete(Paths.get(scriptFilePath));
            } catch (NoSuchFileException | DirectoryNotEmptyException | SecurityException ex) {
                ex.printStackTrace();
            } catch (IOException ex) {
                System.err.println("IOException in delete.");
            }
        } else {
            System.err.println("Unable to delete script file at " + scriptFileToDelete.getAbsolutePath());
        }
        //Repopulate soundtree
        populateSoundList();
    }
}