[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/processing/processing4/main/app/src/processing/app/Sketch.java [Back]  [Original]

/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* Part of the Processing project - http://processing.org Copyright (c) 2012-22 The Processing Foundation Copyright (c) 2004-11 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package processing.app; import processing.app.ui.Editor; import processing.app.ui.Recent; import processing.app.ui.Toolkit; import processing.core.*; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.EventQueue; import java.awt.FileDialog; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.*; import javax.swing.border.EmptyBorder; /** * Stores information about files in the current sketch. */ public class Sketch { private final Editor editor; private final Mode mode; /** main pde file for this sketch. */ private File mainFile; /** * Name of the sketch, which is the name of the folder since 4.0 beta 6. * Prior, it was the "pretty" name of the first tab (they were synonymous). */ private String name; /** true if any of the files have been modified. */ private boolean modified; /** folder that contains this sketch */ private File folder; /** data folder location for this sketch (may not exist yet) */ private File dataFolder; /** code folder location for this sketch (may not exist yet) */ private File codeFolder; private SketchCode current; private int currentIndex; /** * Number of SketchCode objects (tabs) in the current sketch. Note * that this will be the same as code.length, because the getCode() * method returns just the code[] array, rather than a copy of it, * or an array that's been resized to just the relevant files. * (Bugzilla 940) */ private int codeCount; private SketchCode[] code; /** Moved out of Editor and into here for cleaner access. */ private boolean untitled; /** true if we've posted a "sketch disappeared" warning */ private boolean disappearedWarning; /** * Used by the command-line version to create a sketch object. * @param path location of the main .pde file * @param mode what flavor of sketch we're dealing with. */ public Sketch(String path, Mode mode) { this.editor = null; this.mode = mode; load(path); } /** * path is location of the main .pde file, because this is also * simplest to use when opening the file from the finder/explorer. */ public Sketch(String path, Editor editor) throws IOException { this.editor = editor; this.mode = editor.getMode(); load(path); } protected void load(String path) { mainFile = new File(path); folder = mainFile.getParentFile(); /* // get the name of the sketch by chopping .pde or .java // off of the main file name String mainFilename = primaryFile.getName(); int suffixLength = mode.getDefaultExtension().length() + 1; name = mainFilename.substring(0, mainFilename.length() - suffixLength); */ // starting in 4.0 beta 6, use the folder name instead of the main tab name = folder.getName(); disappearedWarning = false; load(); } /** * Build the list of files. *

* Generally this is only done once, rather than * each time a change is made, because otherwise it gets to be * a nightmare to keep track of what files went where, because * not all the data will be saved to disk. *

* This also gets called when the main sketch file is renamed, * because the sketch has to be reloaded from a different folder. *

* Another exception is when an external editor is in use, * in which case the load happens each time "run" is hit. */ protected void load() { codeFolder = new File(folder, "code"); dataFolder = new File(folder, "data"); List filenames = new ArrayList(); List extensions = new ArrayList(); getSketchCodeFiles(filenames, extensions); codeCount = filenames.size(); code = new SketchCode[codeCount]; for (int i = 0; i < codeCount; i++) { String filename = filenames.get(i); String extension = extensions.get(i); code[i] = new SketchCode(new File(folder, filename), extension); } // move the main class to the first tab // start at 1, if it's at zero, don't bother for (int i = 1; i < codeCount; i++) { //if (code[i].file.getName().equals(mainFilename)) { if (code[i].getFile().equals(mainFile)) { SketchCode temp = code[0]; code[0] = code[i]; code[i] = temp; break; } } // sort the entries at the top sortCode(); // set the main file to be the current tab if (editor != null) { setCurrentCode(0); } } public void getSketchCodeFiles(List outFilenames, List outExtensions) { // get list of files in the sketch folder String[] list = folder.list(); if (list != null) { for (String filename : list) { // Ignoring the dot prefix files is especially important to avoid files // with the ._ prefix on Mac OS X. (You'll see this with Mac files on // non-HFS drives, i.e. a thumb drive formatted FAT32.) if (filename.startsWith(".")) continue; // Don't let some wacko name a directory blah.pde or bling.java. if (new File(folder, filename).isDirectory()) continue; // figure out the name without any extension String base = filename; // now strip off the .pde and .java extensions for (String extension : mode.getExtensions()) { if (base.toLowerCase().endsWith("." + extension)) { base = base.substring(0, base.length() - (extension.length() + 1)); // Don't allow people to use files with invalid names, since on load, // it would be otherwise possible to sneak in nasty filenames. [0116] if (isSanitaryName(base)) { if (outFilenames != null) outFilenames.add(filename); if (outExtensions != null) outExtensions.add(extension); } } } } } } /** * Reload the current sketch. Used to update the text area when * an external editor is in use. */ public void reload() { // set current to null so that the tab gets updated // https://download.processing.org/bugzilla/515.html current = null; // nuke previous files and settings load(); } /** * Load a tab that the user added to the sketch or modified with an external * editor. */ public void loadNewTab(String filename, String ext, boolean newAddition) { if (newAddition) { insertCode(new SketchCode(new File(folder, filename), ext)); } else { replaceCode(new SketchCode(new File(folder, filename), ext)); } sortCode(); } protected void replaceCode(SketchCode newCode) { for (int i = 0; i < codeCount; i++) { if (code[i].getFileName().equals(newCode.getFileName())) { code[i] = newCode; break; } } } protected void insertCode(SketchCode newCode) { // make sure the user didn't hide the sketch folder ensureExistence(); // add file to the code/codeCount list, resort the list //if (codeCount == code.length) { code = (SketchCode[]) PApplet.append(code, newCode); codeCount++; //} //code[codeCount++] = newCode; } protected void sortCode() { // cheap-ass sort of the rest of the files // it's a dumb, slow sort, but there shouldn't be more than ~5 files for (int i = 1; i < codeCount; i++) { int who = i; for (int j = i + 1; j < codeCount; j++) { if (code[j].getFileName().compareTo(code[who].getFileName()) < 0) { who = j; // this guy is earlier in the alphabet } } if (who != i) { // swap with someone if changes made SketchCode temp = code[who]; code[who] = code[i]; code[i] = temp; // We also need to update the current tab if (currentIndex == i) { currentIndex = who; } else if (currentIndex == who) { currentIndex = i; } } } } boolean renamingCode; /** * Handler for the New Code menu option. */ public void handleNewCode() { // make sure the user didn't hide the sketch folder ensureExistence(); // if read-only, give an error if (isReadOnly()) { // if the files are read-only, need to first do a "save as". Messages.showMessage(Language.text("new.messages.is_read_only"), Language.text("new.messages.is_read_only.description")); return; } renamingCode = false; // editor.status.edit("Name for new file:", ""); promptForTabName(Language.text("editor.tab.rename.description")+":", ""); } /** * Handler for the "Rename Code" menu option. */ public void handleRenameCode() { // make sure the user didn't hide the sketch folder ensureExistence(); if (currentIndex == 0 && isUntitled()) { Messages.showMessage(Language.text("rename.messages.is_untitled"), Language.text("rename.messages.is_untitled.description")); return; } if (isModified()) { Messages.showMessage(Language.text("menu.file.save"), Language.text("rename.messages.is_modified")); return; } // if read-only, give an error if (isReadOnly()) { // if the files are read-only, need to first do a "save as". Messages.showMessage(Language.text("rename.messages.is_read_only"), Language.text("rename.messages.is_read_only.description")); return; } // ask for new name of file (internal to window) // TODO maybe just pop up a text area? renamingCode = true; String prompt = (currentIndex == 0 && Preferences.getBoolean("editor.sync_folder_and_filename")) ? Language.text("editor.sketch.rename.description") : Language.text("editor.tab.rename.description"); String oldName = (current.isExtension(mode.getDefaultExtension())) ? current.getPrettyName() : current.getFileName(); promptForTabName(prompt + ":", oldName); } /** * Displays a dialog for renaming or creating a new tab */ protected void promptForTabName(String prompt, String oldName) { final JTextField field = new JTextField(oldName); field.addKeyListener(new KeyAdapter() { // Forget ESC, the JDialog should handle it. // Use keyTyped to catch when the feller is actually added to the text // field. With keyTyped, as opposed to keyPressed, the keyCode will be // zero, even if it's enter or backspace or whatever, so the key char // should be used instead. Grr. public void keyTyped(KeyEvent event) { //System.out.println("got event " + event); char ch = event.getKeyChar(); //noinspection StatementWithEmptyBody if ((ch == '_') || (ch == '.') || // allow.pde and .java (('A' { final JFrame frame = new JFrame("Saving " + newFolder.getName() + ""); frame.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); Box box = Box.createVerticalBox(); box.setBorder(new EmptyBorder(16, 16, 16, 16)); if (Platform.isMacOS()) { frame.setBackground(Color.WHITE); } JLabel label = new JLabel("Saving additional files from the sketch folder..."); box.add(label); box.add(Box.createVerticalStrut(8)); final JProgressBar progressBar = new JProgressBar(0, 100); // no luck, stuck with ugly on OS X //progressBar.putClientProperty("JComponent.sizeVariant", "regular"); progressBar.setValue(0); progressBar.setStringPainted(true); box.add(progressBar); frame.getContentPane().add(box); frame.pack(); frame.setLocationRelativeTo(editor); Toolkit.setIcon(frame); frame.setVisible(true); new SwingWorker() { @Override protected Void doInBackground() throws Exception { addPropertyChangeListener(evt -> { if ("progress".equals(evt.getPropertyName())) { progressBar.setValue((Integer) evt.getNewValue()); } }); long totalSize = 0; for (File copyable : copyItems) { totalSize += Util.calcSize(copyable); } long progress = 0; setProgress(0); for (File copyable : copyItems) { if (copyable.isDirectory()) { copyDir(copyable, new File(newFolder, copyable.getName()), progress, totalSize); progress += Util.calcSize(copyable); } else { copyFile(copyable, new File(newFolder, copyable.getName()), progress, totalSize); if (Util.calcSize(copyable) < 512 * 1024) { // If the file length > 0.5MB, the copyFile() function has // been redesigned to change progress every 0.5MB so that // the progress bar doesn't stagnate during that time progress += Util.calcSize(copyable); setProgress((int) (progress * 100L / totalSize)); } } } saving.set(false); return null; } /** * Overloaded copyFile that is called whenever a Save As is being done, * so that the ProgressBar is updated for very large files as well. */ void copyFile(File sourceFile, File targetFile, long progress, long totalSize) throws IOException { BufferedInputStream from = new BufferedInputStream(new FileInputStream(sourceFile)); BufferedOutputStream to = new BufferedOutputStream(new FileOutputStream(targetFile)); byte[] buffer = new byte[16 * 1024]; int bytesRead; int progRead = 0; while ((bytesRead = from.read(buffer)) != -1) { to.write(buffer, 0, bytesRead); progRead += bytesRead; if (progRead >= 512 * 1024) { // to update progress bar every 0.5MB progress += progRead; //progressBar.setValue((int) Math.min(Math.ceil(progress * 100.0 / totalSize), 100)); setProgress((int) (100L * progress / totalSize)); progRead = 0; } } // Final update to progress bar setProgress((int) (100L * progress / totalSize)); from.close(); to.flush(); to.close(); if (!targetFile.setLastModified(sourceFile.lastModified())) { System.err.println("Warning: Could not set modification date/time for " + targetFile); } if (!targetFile.setExecutable(sourceFile.canExecute())) { if (!Platform.isWindows()) { // more of a UNIX thing System.err.println("Warning: Could not set permissions for " + targetFile); } } } long copyDir(File sourceDir, File targetDir, long progress, long totalSize) throws IOException { // Overloaded copyDir so that the Save As progress bar gets updated when the // files are in folders as well (like in the data folder) if (sourceDir.equals(targetDir)) { final String urDum = "source and target directories are identical"; throw new IllegalArgumentException(urDum); } targetDir.mkdirs(); String[] files = sourceDir.list(); if (files != null) { for (String filename : files) { // Ignore dot files (.DS_Store), dot folders (.svn) while copying if (filename.charAt(0) == '.') { continue; } File source = new File(sourceDir, filename); File target = new File(targetDir, filename); if (source.isDirectory()) { progress = copyDir(source, target, progress, totalSize); //progressBar.setValue((int) Math.min(Math.ceil(progress * 100.0 / totalSize), 100)); setProgress((int) (100L * progress / totalSize)); target.setLastModified(source.lastModified()); } else { copyFile(source, target, progress, totalSize); progress += source.length(); //progressBar.setValue((int) Math.min(Math.ceil(progress * 100.0 / totalSize), 100)); setProgress((int) (100L * progress / totalSize)); } } } else { throw new IOException("Could not list files inside " + sourceDir); } return progress; } @Override public void done() { frame.dispose(); editor.statusNotice(Language.text("editor.status.saving.done")); } }.execute(); }); } /** * Update internal state for new sketch name or folder location. */ protected void updateInternal(File sketchFolder) { // reset all the state information for the sketch object mainFile = code[0].getFile(); name = sketchFolder.getName(); folder = sketchFolder; disappearedWarning = false; codeFolder = new File(folder, "code"); dataFolder = new File(folder, "data"); updateNameProperties(); // Name changed, rebuild the sketch menus calcModified(); editor.updateTitle(); editor.getBase().rebuildSketchbook(); } protected void updateModeProperties(Mode mode, Mode defaultMode) { updateModeProperties(folder, mode, defaultMode); } /** * Create or modify a sketch.properties file to specify the given Mode. */ static protected void updateModeProperties(File folder, Mode mode, Mode defaultMode) { try { // Read the old sketch.properties file if it already exists Settings props = loadProperties(folder); // If changing to the default Mode, // remove those entries from sketch.properties if (mode == defaultMode) { props.remove("mode"); props.remove("mode.id"); } else { // Setting to something other than the default Mode, // write that and any other params already in the file. props.set("mode", mode.getTitle()); props.set("mode.id", mode.getIdentifier()); } props.reckon(); } catch (IOException e) { System.err.println("Error while writing sketch.properties"); e.printStackTrace(); } } /* protected Settings loadProperties() throws IOException { return loadProperties(folder); } */ /** * Opens and parses sketch.properties. If it does not exist, returns an * empty Settings object that can be written back to the same location. */ static protected Settings loadProperties(File folder) throws IOException { /* File propsFile = new File(folder, "sketch.properties"); if (propsFile.exists()) { return new Settings(propsFile); } return null; */ return new Settings(new File(folder, "sketch.properties")); } /** * Check through the various modes and see if this is a legit sketch. * Because the default mode will be the first in the list, this will always * prefer that one over the others. */ static protected File findMain(File folder, List modeList) { try { Settings props = Sketch.loadProperties(folder); String main = props.get("main"); if (main != null) { File mainFile = new File(folder, main); if (!mainFile.exists()) { System.err.println(main + " does not exist inside " + folder); // Fall through to the code below in case we can recover. // Not removing the bad entry since this is a find() method. } } } catch (IOException e) { e.printStackTrace(); } for (Mode mode : modeList) { // Test whether a .pde file of the same name as its parent folder exists. String defaultName = folder.getName() + "." + mode.getDefaultExtension(); File entry = new File(folder, defaultName); if (entry.exists()) { return entry; } } return null; } private void updateNameProperties() { // If the main file and the sketch name are not identical, // update sketch.properties. String mainName = mainFile.getName(); String defaultName = name + "." + mode.getDefaultExtension(); //System.out.println("main name is " + mainName + " and default name is " + defaultName); try { // Read the old sketch.properties file if it already exists Settings props = loadProperties(folder); if (mainName.equals(defaultName)) { props.remove("main"); } else { props.set("main", mainName); } //System.out.println("props size is now " + props.getMap().size()); props.reckon(); } catch (IOException e) { System.err.println("Error while updating sketch.properties"); e.printStackTrace(); } } /** * Prompt the user for a new file to the sketch, then call the * other addFile() function to actually add it. */ public void handleAddFile() { // make sure the user didn't hide the sketch folder ensureExistence(); // if read-only, give an error if (isReadOnly()) { // if the files are read-only, need to first do a "save as". Messages.showMessage(Language.text("add_file.messages.is_read_only"), Language.text("add_file.messages.is_read_only.description")); return; } // get a dialog, select a file to add to the sketch String prompt = Language.text("file"); //FileDialog fd = new FileDialog(new Frame(), prompt, FileDialog.LOAD); FileDialog fd = new FileDialog(editor, prompt, FileDialog.LOAD); fd.setVisible(true); String directory = fd.getDirectory(); String filename = fd.getFile(); if (filename == null) return; // copy the file into the folder. if people would rather // move instead of copy, they can do it by hand File sourceFile = new File(directory, filename); // now do the work of adding the file boolean result = addFile(sourceFile); if (result) { // editor.statusNotice("One file added to the sketch."); //Done from within TaskAddFile inner class when copying is completed } } /** * Add a file to the sketch. *

* .pde or .java files will be added to the sketch folder.
* .jar, .class, .dll, .jnilib, and .so files will all * be added to the "code" folder.
* All other files will be added to the "data" folder. *

* If they don't exist already, the "code" or "data" folder * will be created. *

* @return true if successful. */ public boolean addFile(File sourceFile) { if (sourceFile.isDirectory()) { System.err.println("Skipping folder " + sourceFile); System.err.println("Dragging and dropping a folder is not supported."); return false; } String filename = sourceFile.getName(); File destFile = null; String codeExtension = null; boolean replacement = false; boolean isCode = false; // if the file appears to be code related, drop it // into the code folder, instead of the data folder if (filename.toLowerCase().endsWith(".class") || filename.toLowerCase().endsWith(".jar") || filename.toLowerCase().endsWith(".dll") || filename.toLowerCase().endsWith(".dylib") || filename.toLowerCase().endsWith(".jnilib") || filename.toLowerCase().endsWith(".so")) { if (!codeFolder.exists()) { boolean success = codeFolder.mkdirs(); if (!success) { System.err.println("Could not create " + codeFolder); return false; } } destFile = new File(codeFolder, filename); isCode = true; } else { for (String extension : mode.getExtensions()) { String lower = filename.toLowerCase(); if (lower.endsWith("." + extension)) { destFile = new File(this.folder, filename); codeExtension = extension; } } if (codeExtension == null) { prepareDataFolder(); destFile = new File(dataFolder, filename); } } // check whether this file already exists if (destFile.exists()) { Object[] options = { Language.text("prompt.ok"), Language.text("prompt.cancel") }; String prompt = Language.interpolate("add_file.messages.confirm_replace", filename); int result = JOptionPane.showOptionDialog(editor, prompt, "Replace", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); if (result == JOptionPane.YES_OPTION) { replacement = true; } else { return false; } } // If it's a replacement, delete the old file first, // otherwise case changes will not be preserved. // https://download.processing.org/bugzilla/969.html if (replacement) { boolean muchSuccess = destFile.delete(); if (!muchSuccess) { Messages.showWarning(Language.text("add_file.messages.error_adding"), Language.interpolate("add_file.messages.cannot_delete.description", filename)); return false; } } // make sure they aren't the same file if ((codeExtension == null) && sourceFile.equals(destFile)) { Messages.showWarning(Language.text("add_file.messages.same_file"), Language.text("add_file.messages.same_file.description")); return false; } // Handles "Add File" when a .pde is used. For 3.0b1, this no longer runs // on a separate thread because it's totally unnecessary (a .pde file is // not going to be so large that it's ever required) and otherwise we have // to introduce a threading block here. // https://github.com/processing/processing/issues/3383 if (!sourceFile.equals(destFile)) { try { Util.copyFile(sourceFile, destFile); } catch (IOException e) { Messages.showWarning(Language.text("add_file.messages.error_adding"), Language.interpolate("add_file.messages.cannot_add.description", filename), e); return false; } } if (isCode) { editor.codeFolderChanged(); } if (codeExtension != null) { SketchCode newCode = new SketchCode(destFile, codeExtension); if (replacement) { replaceCode(newCode); } else { insertCode(newCode); sortCode(); } setCurrentCode(filename); editor.repaintHeader(); if (isUntitled()) { // TODO probably not necessary? problematic? // Mark the new code as modified so that the sketch is saved current.setModified(true); } } else { if (isUntitled()) { // TODO probably not necessary? problematic? // If a file has been added, mark the main code as modified so // that the sketch is properly saved. code[0].setModified(true); } } return true; } /** * Change what file is currently being edited. Changes the current tab index. *

    *
  1. store the String for the text of the current file. *
  2. retrieve the String for the text of the new file. *
  3. change the text that's visible in the text area *
*/ public void setCurrentCode(int which) { // if current is null, then this is the first setCurrent(0) if (which < 0 || which >= codeCount || ((currentIndex == which) && (current == code[currentIndex]))) { return; } // get the text currently being edited if (current != null) { current.setState(editor.getText(), editor.getSelectionStart(), editor.getSelectionStop(), editor.getScrollPosition()); } current = code[which]; currentIndex = which; current.visited = System.currentTimeMillis(); editor.setCode(current); editor.repaintHeader(); } /** * Internal helper function to set the current tab based on a name. * @param findName the file name (not pretty name) to be shown */ public void setCurrentCode(String findName) { for (int i = 0; i < codeCount; i++) { if (findName.equals(code[i].getFileName()) || findName.equals(code[i].getPrettyName())) { setCurrentCode(i); return; } } } /** * Create a temporary folder that includes the sketch's name in its title. */ public File makeTempFolder() { try { return Util.createTempFolder(name, "temp", null); } catch (IOException e) { Messages.showWarning(Language.text("temp_dir.messages.bad_build_folder"), Language.text("temp_dir.messages.bad_build_folder.description"), e); } return null; } /** * Make sure the sketch hasn't been moved or deleted by a nefarious user. * If they did, try to re-create it and save. Only checks whether the * main folder is still around, but not its contents. */ public void ensureExistence() { if (!folder.exists()) { // Avoid an infinite loop if we've already warned about this // https://github.com/processing/processing/issues/4805 if (!disappearedWarning) { disappearedWarning = true; // Disaster recovery, try to salvage what's there already. Messages.showWarning(Language.text("ensure_exist.messages.missing_sketch"), Language.text("ensure_exist.messages.missing_sketch.description")); try { folder.mkdirs(); modified = true; for (int i = 0; i < codeCount; i++) { code[i].save(); // this will force a save } calcModified(); } catch (Exception e) { // disappearedWarning prevents infinite loop in this scenario Messages.showWarning(Language.text("ensure_exist.messages.unrecoverable"), Language.text("ensure_exist.messages.unrecoverable.description"), e); } } } } /** * Returns true if this is a read-only sketch. Used for the * "examples" directory, or when sketches are loaded from read-only * volumes or folders without appropriate permissions. */ public boolean isReadOnly() { String path = folder.getAbsolutePath(); List modes = editor.getBase().getModeList(); // Make sure it's not read-only for another Mode besides this one // https://github.com/processing/processing/issues/773 for (Mode mode : modes) { if (path.startsWith(mode.getExamplesFolder().getAbsolutePath()) || path.startsWith(mode.getLibrariesFolder().getAbsolutePath())) { return true; } } // Check to see if each modified code file can be written. // Note: canWrite() does not work on directories. for (int i = 0; i < codeCount; i++) { if (code[i].isModified() && code[i].fileReadOnly() && code[i].fileExists()) { return true; } } return false; } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . // Additional accessors added in 0136 because of package work. // These will also be helpful for tool developers. /** * Returns the name of this sketch. (The pretty name of the main tab.) */ public String getName() { return name; } // /** // * Returns a File object for the main .pde file for this sketch. // */ // public File getMainFile() { // return mainFile; // } /** * Returns the name (without extension) of the main tab. * (This version still has underscores and is a legit class name.) * Most getName() calls before 4.0 were to get the main class, * so this method addition allows the sketch name to be decoupled * from the name of the main tab. */ public String getMainName() { return code[0].getPrettyName(); } /** * Returns path to the main .pde file for this sketch. */ public String getMainPath() { return mainFile.getAbsolutePath(); } /** * Returns the sketch folder. */ public File getFolder() { return folder; } /** * Returns the location of the sketch's data folder. (It may not exist yet.) */ public File getDataFolder() { return dataFolder; } public boolean hasDataFolder() { return dataFolder.exists(); } /** * Create the data folder if it does not exist already. As a convenience, * it also returns the data folder, since it's likely about to be used. */ public File prepareDataFolder() { if (!dataFolder.exists()) { dataFolder.mkdirs(); } return dataFolder; } /** * Returns the location of the sketch's code folder. (It may not exist yet.) */ public File getCodeFolder() { return codeFolder; } public boolean hasCodeFolder() { return (codeFolder != null) && codeFolder.exists(); } public SketchCode[] getCode() { return code; } public int getCodeCount() { return codeCount; } // Used by GUI Builder for Processing // https://github.com/processing/processing4/issues/545 // https://github.com/processing/processing4/issues/596 public SketchCode getCode(int index) { return code[index]; } public SketchCode getCurrentCode() { return current; } public int getCurrentCodeIndex() { return currentIndex; } /** * Tried to remove in beta 6, but in use by Python Mode. * When it's removed there, let me know, and I'll remove it here. */ @Deprecated public String getMainProgram() { return getCode(0).getProgram(); } public void setUntitled(boolean untitled) { this.untitled = untitled; editor.updateTitle(); } public boolean isUntitled() { return untitled; } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . // /** // * Convert to sanitized name and alert the user // * if changes were made. // */ // static public String checkName(String origName) { // String newName = sanitizeName(origName); // // if (!newName.equals(origName)) { // String msg = // Language.text("check_name.messages.is_name_modified"); // System.out.println(msg); // } // return newName; // } /** * Return true if the name is valid for a Processing sketch. * Extensions of the form .foo are ignored. */ public static boolean isSanitaryName(String name) { final int dot = name.lastIndexOf('.'); if (dot >= 0) { name = name.substring(0, dot); } return sanitizeName(name).equals(name); } static boolean isAsciiLetter(char c) { return (c >= 'a' && c = 'A' && c 63) { sb.setLength(63); } // Remove underscores from the beginning, these seem to be a reserved // thing on Android, plus it sometimes causes trouble elsewhere. int underscore = 0; while (underscore < sb.length() && sb.charAt(underscore) == '_') { underscore++; } if (underscore == sb.length()) { return "bad_sketch_name_please_fix"; } else if (underscore != 0) { return sb.substring(underscore); } return sb.toString(); } public Mode getMode() { return mode; } @Override public boolean equals(Object another) { if (another instanceof Sketch) { return getMainPath().equals(((Sketch) another).getMainPath()); } return false; } }
Web Proxy Viewer  |  New URL  |  Original Page