public class CheckBox
{
  boolean checked;
  float x, y, boxWidth, boxHeight;
  String label;
  float padx = 7;
  boolean visible = true;
  float labelWidth;
  
  CheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    label = l;
    labelWidth = labelSize;
    x = xx; y = yy; boxWidth = ww; boxHeight = hh;
    Interactive.add( this );
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    checked = !checked;
  }
   
  void draw ()
  {
    if (!menuVisible) {
      return;
    }
    noStroke();
    if (x <= mouseX && mouseX <= x+boxWidth &&
      y <= mouseY && mouseY <= y+boxHeight) {
      fill(#4B9EA5);
    } else {
      fill(#000000);
    }
    rectMode(CORNER);
    rect( x, y, boxWidth, boxHeight, 4 );
    if ( checked ) {
      fill(ds.GUIColor);
      rect( x+2, y+2, boxWidth-4, boxHeight-4, 4 );
    }
    fill(#000000);
    textAlign( LEFT );
    textSize(ds.menuTextSize);
    text( label, x+boxWidth+padx, y+boxHeight );
  }
  
  // this is a special inside test that includes the label text
  boolean isInside ( float mx, float my)
  {
    if (!menuVisible) {
      return false;
    }
    return Interactive.insideRect( x, y, boxWidth+labelWidth, boxHeight, mx, my );
    
  }

  void setVisible(boolean flag)
  {
    visible = flag;
  }

}

/*
public class MovieCheckBox extends CheckBox
{
  MovieCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize); 
  }
  
  void mouseReleased ( float mx, float my )
  {
     if (!menuVisible) {
       return;
     }  
     saveStateForUndo();
     checked = !checked;
     movieMode = !movieMode;
     attachedIds.clear();
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}
*/

public class LabelCheckBox extends CheckBox
{
  LabelCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize); 
    checked = ds.labelMode;
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    checked = !checked;
    ds.labelMode = !ds.labelMode;
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}


public class ResetCircleCheckBox extends CheckBox
{  
  ResetCircleCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize); 
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    saveStateForUndo();
    resetCircle();
    textDisplay = "CIRCLE LAYOUT";
    textDisplayCount = 0;
  }
  
  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}


public class ResetRadialCheckBox extends CheckBox
{  
  ResetRadialCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize); 
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    saveStateForUndo();
    resetRadial();
    textDisplay = "RADIAL LAYOUT";
    textDisplayCount = 0;
  }
  
  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}


public class SnapCheckBox extends CheckBox
{
  // boolean checked;
  // float x, y, width, height;
  // String label;
  // float padx = 7;
  // boolean visible = true;

  SnapCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize);
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    screenshot = true;
    capturingScreen = true;
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}

public class SnapFormatCheckBox extends CheckBox
{
  // boolean checked;
  // float x, y, width, height;
  // String label;
  // float padx = 7;
  // boolean visible = true;

  SnapFormatCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize);
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    checked = !checked;
    if (checked) {
     screenshotFormat = DrawClass.FORMAT_SVG;
    } else {
     screenshotFormat = DrawClass.FORMAT_PNG;
    }
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}

public class SnapSVGCheckBox extends CheckBox
{
  // boolean checked;
  // float x, y, width, height;
  // String label;
  // float padx = 7;
  // boolean visible = true;
  int resx;
  int resy;

  SnapSVGCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize, int _resx, int _resy ) {
    super(l, xx, yy, ww, hh, labelSize);
    resx = _resx;
    resy = _resy;
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    saveStateForUndo();
    if (!BROWSER) {
  //    simWasOn = simulationMode;
  //    simulationMode = false;
      // let user select folder after which function saveScreenSVGFile is envoked as callback
      selectFolder("Select a folder to save to", "saveScreenSVGFile", userFile.getParentFile());
  //    String imageFile = saveScreenSVGFile(new File(outputDir)); // userFile.getParentFile());
  //    if (imageFile != null) {
  //     textDisplay = "SVG file saved to: " + imageFile;
  //     textDisplayCount = 0;
  //    } else {
  //     textDisplay = "Warning: SVG file not saved!";
  //     textDisplayCount = 0;
  //    }
    }
    else {
      if (javascript != null) {
        saveSVGBrowser(drawer.toSVG()); // hook to javascript
      }
    }

  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}

public class OutlineCheckBox extends CheckBox
{
  // boolean checked;
  // float x, y, width, height;
  // String label;
  // float padx = 7;
  // boolean visible = true;

  OutlineCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize);
    checked = ds.outlineMode;
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    checked = !checked;
    ds.outlineMode = checked;
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}


public class SimulateAllCheckBox extends CheckBox
{
  // boolean checked;
  // float x, y, width, height;
  // String label;
  // float padx = 7;
  // boolean visible = true;

  SimulateAllCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize);
    checked = !simulateAllMode;
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    checked = !checked;
    simulateAllMode = !checked;
    
    if (!simulateAllMode) {
      textDisplay = "SIMULATE SELECTED ONLY";
    } else {
      textDisplay = "SIMULATE ALL";
    }
    textDisplayCount = 0;
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}


public class RigidHairpinsCheckBox extends CheckBox
{
  // boolean checked;
  // float x, y, width, height;
  // String label;
  // float padx = 7;
  // boolean visible = true;

  RigidHairpinsCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize);
    checked = rigidHairpins;
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    checked = !checked;
    rigidHairpins = checked;
    wasRigidHairpins = rigidHairpins;
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}

public class RigidLoopsCheckBox extends CheckBox
{
  // boolean checked;
  // float x, y, width, height;
  // String label;
  // float padx = 7;
  // boolean visible = true;

  RigidLoopsCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize);
    checked = rigidLoops;
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    checked = !checked;
    rigidLoops = checked;
    wasRigidLoops = rigidLoops;
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}


public class MovementCheckBox extends CheckBox
{
  MovementCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize); 
    checked = simulationMode;
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    saveStateForUndo();
    checked = !checked;
    simulationMode = !simulationMode;
    if (!simulationMode) {
      textDisplay = "FREEZE";
    } else {
      textDisplay = "UNFREEZE";
    }
    textDisplayCount = 0;
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}


public class RigidHelixCheckBox extends CheckBox
{
  // boolean checked;
  // float x, y, width, height;
  // String label;
  // float padx = 7;
  // boolean visible = true;

  RigidHelixCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize); 
    checked = rigidHelices;
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    saveStateForUndo();
    checked = !checked;
    rigidHelices = !rigidHelices;
    if (rigidHelices) {
      rigidHairpins = wasRigidHairpins;
      rigidLoops = wasRigidLoops;
      checkBoxes.get("Rigid Loops").checked = rigidLoops;
      checkBoxes.get("Rigid Hairpins").checked = rigidHairpins;
    } else {
      wasRigidLoops = rigidLoops;
      wasRigidHairpins = rigidHairpins;
      
      rigidLoops = false;
      checkBoxes.get("Rigid Loops").checked = false;
      
      rigidHairpins = false;
      checkBoxes.get("Rigid Hairpins").checked = false;
    }
    
    if (rigidHelices) {
      textDisplay = "HELICES FIXED";
    } else {
      textDisplay = "HELICES UNFIXED";
    }
    textDisplayCount = 0;
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}

/*
public class ColorCheckBox extends CheckBox
{
  ColorCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize); 
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    textDisplay = "FEATURE COMING SOON";
    textDisplayCount = 0;
    
    saveStateForUndo();
    colorSelecting = true;
    runningSimulation = false;
    menuVisible = false;
    colorMode(RGB, 255);
    int paletteW = (int)((width-40) / 2);
    int paletteH = (int)((height-40) / 2);
    println(paletteW + " " + paletteH);
    palette = new ColorPicker(20, 20, paletteW, paletteH, 255);
    
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}
*/

public class ColorLoadCheckBox extends CheckBox
{
  ColorLoadCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize);
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    
    if (!BROWSER) {
      selectInput("Select a color file to load", "loadColorFile");
    }
    else {
      loadColorBrowserCall();
    }
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}


public class BasePairLoadCheckBox extends CheckBox
{
  BasePairLoadCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize);
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    
    if (!BROWSER) {
      selectInput("Select a basepair file to load", "loadBondFile");
    }
    else {
      loadBondsBrowserCall();
    }
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}


public class SaveCheckBox extends CheckBox
{
  SaveCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize);
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
    return;
    }
    saveStateForUndo();
    if (!BROWSER) {
      simWasOn = simulationMode;
      simulationMode = false;
      //movieMode = false;
      
      selectFolder("Select a folder to save to", "saveFolderSelected", userFile.getParentFile());
    }
    else {
      if (javascript != null) {
        saveStateBrowser();
      }
    }
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}


public class LoadCheckBox extends CheckBox
{
  LoadCheckBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize);
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
    
    loadCalled();
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}

public class ZoomResetBox extends CheckBox
{
  // boolean checked;
  // float x, y, width, height;
  // String label;
  // float padx = 7;
  // boolean visible = true;

  ZoomResetBox ( String l, float xx, float yy, float ww, float hh, float labelSize )
  {
    super(l, xx, yy, ww, hh, labelSize);
  }
  
  void mouseReleased ( float mx, float my )
  {
    if (!menuVisible) {
      return;
    }
//    print("Resetting zoom factor!");
    ds.resetZoom(); // reset zoom factor
    zoomFactorSave = 1.0; // workaround: these stored values are used to recreate the zoom state after the menue is deactivated
    zoomTranslateXSave = 0.0; // in this special case we want to reset these saved values
    zoomTranslateYSave = 0.0; 
  }

  void draw ()
  {
    super.draw();
  }
  
  // this is a special inside test that includes the label text
  boolean isInside( float mx, float my )
  {
    return super.isInside(mx,my);
  }

  void setVisible(boolean flag) {
    super.setVisible(flag);
  }
}

void loadCalled() {
  if (!BROWSER) {
    if (runningSimulation) {
      saveStateForUndo();
      simWasOn = simulationMode;
      simulationMode = false;
      //movieWasOn = movieMode;
      //movieMode = false;
      selectInput("Select a .ct, .bpseq, or .rs file to load", "loadFile");
    }
  }
  else {
    //println("In browser loadCalled");
    if (javascript != null && !selectingFileType) {
      if (errorMessage.equals("")) {
        if (runningSimulation) {
          saveStateForUndo();
          multiSelecting = false;
          dragging = false;
          simWasOn = simulationMode;
          simulationMode = false;
          //movieWasOn = movieMode;
          //movieMode = false;
          returnToSim = true;
          runningSimulation = false;
          
        }
        setupState();
      } else {
        clickCheck = true;
      }
    }
  }
}

void loadFile(File selection) {
  if (selection == null) {
    println("Window was closed or the user hit cancel.");
    simulationMode = simWasOn;
    //movieMode = movieWasOn;
  } else {
    multiSelecting = false;
    dragging = false;
    runningSimulation = false;
    userFile = selection;
    returnToSim = true;
    setupState(selection.getAbsolutePath());
  }
}

void loadColorFile(File selection) {
  if (selection == null) {
    println("Window was closed or the user hit cancel.");
  } else {
    loadColor(loadStrings(selection));
  }
}

void loadColorBrowserCall() {
  if (javascript != null) {
    String rawInput = javascript.getTextInput("inputcolor");
    String[] rawInputLines = rawInput.trim().split("\\r\\n|\\n|\\r");
    loadColor(rawInputLines);
  } else {
    println("javascript object is null in loadColorBrowserCall()");
  }
}

void loadColor(String[] rawLines) {
  ArrayList<Double> colorData = new ArrayList<Double>();
  
  // Seperate based on whitespace
  boolean flag = false;
  String splitter = BROWSER ? " " : "\\s++";
  for(String line : rawLines) {
    if (flag) { break; }
    String[] line2 = line.trim().split(splitter);
    if ("".equals(line2[0]) || line2[0].trim().substring(0, 1).equals("#")) { continue; } // Ignore comments and empty lines
    
    for (String number : line2) {
      number = number.trim();
      if (number.length() > 0 && number.substring(0, 1).equals("#")) { break; } // ignore comments
      if (number.matches("-?\\d+(\\.\\d+)?")) { // Check if it is numeric
        colorData.add(stringToDouble(number));
      }
      else {
        flag = true;
        break;
      }
    }
  }
  
  if (colorData.size() == 0) {
    textDisplay = BROWSER ? "INPUT COLORING BELOW" : "COLOR FILE IS EMPTY";
    textDisplayCount = 0;
    return;
  }
  
  if (currentState.spheres == null || colorData.size() > currentState.spheres.size()) {
    if (debugPrints) {
      println("Warning: invalid color input. Should be a space or line seperated list of numbers, equal in length to the number of residues.");
      println("Spheres size: " + currentState.spheres.size() + ", color size: " + colorData.size());
    }
    textDisplay = "TOO MANY NUMBERS";
    textDisplayCount = 0;
    return;
  }
  
  double minColor = colorData.get(0);
  double maxColor = minColor;
  for (Double number : colorData) {
    if (number < minColor) {
      minColor = number;
    }
    
    if (number > maxColor) {
      maxColor = number;
    }
  }
  
  double dist0 = 0 - minColor;
  for (int i = 0, l = colorData.size(); i < l; ++i) {
    double denominator = (maxColor + dist0);
    float colorNum;
    if (denominator <= 0) {
      colorNum = 0;
    } else {
      colorNum = (float)((colorData.get(i) + dist0) / denominator);
    }
    
    if (colorNum < .001) {
      colorNum = 0;
    }
    
    currentState.spheres.get(i).baseColor = lerpColor(ds.startGradient, ds.endGradient, colorNum);
    currentState.spheres.get(i).colorNum = colorNum;
  }
  
  for (int i = colorData.size(); i < currentState.spheres.size(); ++i) {
    currentState.spheres.get(i).baseColor = lerpColor(ds.startGradient, ds.endGradient, 0);
    currentState.spheres.get(i).colorNum = 0;
  }
  
  ds.colorMode = DisplaySettings.COLOR_MODE_CUSTOM;
  sliders[3].setValue( ((float)ds.colorMode - 1)/((float)DisplaySettings.COLOR_MODE_MAX - 1) );
  displayColorMode();
}

void loadBondFile(File selection) {
  if (selection == null) {
    println("Window was closed or the user hit cancel.");
  } else {
    loadBonds(loadStrings(selection));
  }
}

void loadBondsBrowserCall() {
  if (javascript != null) {
    String rawInput = javascript.getTextInput("inputbonds");
    String[] rawInputLines = rawInput.trim().split("\\r\\n|\\n|\\r");
    loadBonds(rawInputLines);
  } else {
    println("javascript object is null in loadBondsBrowserCall()");
  }
}

void loadBonds(String[] rawLines) {
  // Turn off movie mode
  HashMap<ArrayList<Integer>, String> inputPairs = new HashMap<ArrayList<Integer>, String>();
  boolean flag = false;
  textDisplay = "ERROR: ";
  SimulationSetup s = currentState.sim;
  
  String splitter = BROWSER ? " " : "\\s++";
  outer: for(String line : rawLines) {
    String[] pairs = line.trim().split(",");
    if ("".equals(pairs[0]) || pairs[0].trim().substring(0, 1).equals("#")) { continue; } // Ignore comments and empty lines
    
    for (String pair : pairs) {
      String[] entries = pair.trim().split(splitter);
      if (entries.length < 2 || entries.length > 3 || !entries[0].matches("^\\d+$") || !entries[1].matches("^\\d+$")) {
        flag = true;
        break outer;
      }
      
      int id1 = int(entries[0]) - 1;
      int id2 = int(entries[1]) - 1;
      
      if (id1 < 0 || id1 >= s.lenTot || id2 < 0 || id2 >= s.lenTot) {
        flag = true;
        textDisplay += "ID OUT OF BOUNDS!";
        break outer;
      }
      if (id1 == id2) {
        flag = true;
        textDisplay += "A BASE CANNOT PAIR WITH ITSELF!";
        break outer;
      }
      
      if (id1 > id2) {
        int tempId;
        tempId = id1;
        id1 = id2;
        id2 = tempId;
      }
      
      String type = "cWW";
      if (entries.length == 3 && !"".equals(entries[2])) {
        type = entries[2];
      }
      
      ArrayList<Integer> inputKey = new ArrayList<Integer>(2);
      inputKey.add(id1);
      inputKey.add(id2);
      inputPairs.put(inputKey, type);
    }
  }
  
  if (debugPrints) {
    println("\nNew non-canonicals:");
    for (Map.Entry<ArrayList<Integer>, String> nc : inputPairs.entrySet()) {
      ArrayList<Integer> bases = nc.getKey();
      println(bases.get(0) + " " + bases.get(1) + " " + nc.getValue());
    }
  }
  
  if (!flag) {
    if (inputPairs.size() == 0) {
      textDisplay = BROWSER ? "INPUT BONDS BELOW" : "BOND FILE EMPTY";
    } else {
      for (Map.Entry<ArrayList<Integer>, String> nc : inputPairs.entrySet()) {
        ArrayList<Integer> bases = nc.getKey();
        setNewPair(currentState.spheres.get(bases.get(0)), currentState.spheres.get(bases.get(1)), nc.getValue(), s);
      }
      textDisplay = "ADDED BONDS";
    }
  }
  
  textDisplayCount = 0;
}

void saveStateBrowser() {
  String[] saveLines = currentState.writeSaveFile();
  javascript.saveFileBrowser(saveLines);
  textDisplay = "SAVED STATE TO BOX BELOW";
  textDisplayCount = 0;
}

void saveSVGBrowser(String[] saveLines) {
//  String[] saveLines = drawer.toSVG(width, height);
  javascript.saveSVGBrowser(saveLines);
  textDisplay = "SAVED SVG IMAGE";
  textDisplayCount = 0;
}

void saveFolderSelected(File selection) {
  if (selection == null) { return; }
  if (!selection.isDirectory()) { return; }
  try {
    // Determine save file name
    String inputFileName = userFile.getName();
    String fileBase = inputFileName; // default if this checker fails
    for (int i = inputFileName.length(); i > 0; i--) {
      if ( ".".equals(inputFileName.substring(i-1, i)) ) {
        fileBase = inputFileName.substring(0, i-1);
    
        if (inputFileName.substring(i).equals("rs")) {
          for (int j = fileBase.length() - 5; j > 0; j--) {
            if (inputFileName.substring(j, j+5).equals("_save")) {
              fileBase = inputFileName.substring(0, j);
              break;
            }
          }
        }
        break;
      }
    }
    File saveFile;
    int saveNum = 1;
    while (true) {
      String saveFileName = fileBase + "_save" + saveNum + ".rs";
      saveFile = new File(selection.getAbsolutePath(), saveFileName);
      if (!saveFile.exists()) { break; }
      ++saveNum;
    }
  
    simulationMode = simWasOn;
  
    String[] saveLines = currentState.writeSaveFile();
    PrintWriter output = createWriter(saveFile.getAbsolutePath());
    for (String s : saveLines) {
      output.println(s);
    }
    output.flush();
    output.close();
  
    textDisplay = "STATE SAVED AS " + saveFile.getName();
    textDisplayCount = 0;
  
    surface.setTitle(saveFile.getName());
  
  } catch (Exception e) {
    e.printStackTrace();
    simulationMode = simWasOn;
    textDisplay = "COULD NOT SAVE";
    textDisplayCount = 0;
  }
}