| [ Web Proxy ] |
| Viewing: https://raw.githubusercontent.com/processing/processing4/main/app/src/processing/app/Sketch.java | [Back] [Original] |
* 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.| Web Proxy Viewer | New URL | Original Page |