diff --git a/.gitignore b/.gitignore index 26b5b97563..81a4513bd2 100644 --- a/.gitignore +++ b/.gitignore @@ -102,6 +102,7 @@ processing-examples core/build/ build/publish/ app/build +app/utils/build java/build/ /build/reports /java/bin diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5323a1a829..64d7ddbd9d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -97,6 +97,7 @@ compose.desktop { dependencies { implementation(project(":core")) + implementation(project(":app:utils")) runtimeOnly(project(":java")) implementation(libs.flatlaf) diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index b5aa599b98..8204bdacca 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -43,6 +43,7 @@ import processing.app.ui.Toolkit; import processing.core.*; import processing.data.StringList; +import processing.utils.Util; /** * The base class for the main processing application. @@ -67,8 +68,6 @@ public class Base { */ static public boolean DEBUG = System.getenv().containsKey("DEBUG"); - /** True if running via Commander. */ - static private boolean commandLine; /** * If settings.txt is present inside lib, it will be used to override @@ -472,12 +471,12 @@ static public String getVersionName() { public static void setCommandLine() { - commandLine = true; + System.setProperty("processing.cli", "true"); } static public boolean isCommandLine() { - return commandLine; + return Boolean.getBoolean("processing.cli"); } diff --git a/app/src/processing/app/Language.java b/app/src/processing/app/Language.java index d55c8b710c..3b66c31819 100644 --- a/app/src/processing/app/Language.java +++ b/app/src/processing/app/Language.java @@ -26,6 +26,7 @@ import processing.core.PApplet; import processing.data.StringList; +import processing.utils.Util; /** diff --git a/app/src/processing/app/Library.java b/app/src/processing/app/Library.java index f23354284b..5bd1809c67 100644 --- a/app/src/processing/app/Library.java +++ b/app/src/processing/app/Library.java @@ -8,6 +8,7 @@ import processing.core.*; import processing.data.StringDict; import processing.data.StringList; +import processing.utils.Util; public class Library extends LocalContribution { diff --git a/app/src/processing/app/Messages.kt b/app/src/processing/app/Messages.kt index cae54e6e97..3a4525e986 100644 --- a/app/src/processing/app/Messages.kt +++ b/app/src/processing/app/Messages.kt @@ -26,45 +26,8 @@ import java.io.StringWriter import javax.swing.JFrame import javax.swing.JOptionPane -class Messages { +class Messages : processing.utils.Messages() { companion object { - /** - * "No cookie for you" type messages. Nothing fatal or all that - * much of a bummer, but something to notify the user about. - */ - @JvmStatic - fun showMessage(title: String = "Message", message: String) { - if (Base.isCommandLine()) { - println("$title: $message") - } else { - JOptionPane.showMessageDialog( - Frame(), message, title, - JOptionPane.INFORMATION_MESSAGE - ) - } - } - - - /** - * Non-fatal error message with optional stack trace side dish. - */ - /** - * Non-fatal error message. - */ - @JvmStatic - @JvmOverloads - fun showWarning(title: String = "Warning", message: String, e: Throwable? = null) { - if (Base.isCommandLine()) { - println("$title: $message") - } else { - JOptionPane.showMessageDialog( - Frame(), message, title, - JOptionPane.WARNING_MESSAGE - ) - } - e?.printStackTrace() - } - /** * Non-fatal error message with two levels of formatting. * Unlike the others, this is non-blocking and will run later on the EDT. @@ -92,26 +55,6 @@ class Messages { } - /** - * Show an error message that's actually fatal to the program. - * This is an error that can't be recovered. Use showWarning() - * for errors that allow P5 to continue running. - */ - @JvmStatic - fun showError(title: String = "Error", message: String, e: Throwable?) { - if (Base.isCommandLine()) { - System.err.println("$title: $message") - } else { - JOptionPane.showMessageDialog( - Frame(), message, title, - JOptionPane.ERROR_MESSAGE - ) - } - e?.printStackTrace() - System.exit(1) - } - - /** * Warning window that includes the stack trace. */ @@ -218,56 +161,6 @@ class Messages { return -1 } - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - @JvmStatic - @Deprecated("Use log() instead") - fun log(from: Any, message: String) { - if (Base.DEBUG) { - val callingClass = Throwable() - .stackTrace[2] - .className - .formatClassName() - println("$callingClass: $message") - } - } - - @JvmStatic - fun log(message: String?) { - if (Base.DEBUG) { - val callingClass = Throwable() - .stackTrace[2] - .className - .formatClassName() - println("$callingClass$message") - } - } - - @JvmStatic - fun logf(message: String?, vararg args: Any?) { - if (Base.DEBUG) { - val callingClass = Throwable() - .stackTrace[2] - .className - .formatClassName() - System.out.printf("$callingClass$message", *args) - } - } - - @JvmStatic - @JvmOverloads - fun err(message: String?, e: Throwable? = null) { - if (Base.DEBUG) { - if (message != null) { - val callingClass = Throwable() - .stackTrace[4] - .className - .formatClassName() - System.err.println("$callingClass$message") - } - e?.printStackTrace() - } - } } } diff --git a/app/src/processing/app/Mode.java b/app/src/processing/app/Mode.java index 29e9ad6d3a..ec860c3a96 100644 --- a/app/src/processing/app/Mode.java +++ b/app/src/processing/app/Mode.java @@ -40,6 +40,7 @@ import processing.app.ui.Recent; import processing.app.ui.Toolkit; import processing.core.PApplet; +import processing.utils.Util; public abstract class Mode { diff --git a/app/src/processing/app/Platform.java b/app/src/processing/app/Platform.java index b911d7e0ae..73318c4808 100644 --- a/app/src/processing/app/Platform.java +++ b/app/src/processing/app/Platform.java @@ -36,6 +36,8 @@ import processing.core.PApplet; import processing.core.PConstants; import processing.data.StringDict; +import processing.utils.SettingsResolver; +import processing.utils.Util; public class Platform { @@ -129,7 +131,12 @@ static public float getSystemZoom() { static public File getSettingsFolder() throws Exception { - return inst.getSettingsFolder(); + File override = Base.getSettingsOverride(); + if (override != null) { + return override; + } + + return SettingsResolver.getSettingsFolder(); } diff --git a/app/src/processing/app/Preferences.java b/app/src/processing/app/Preferences.java index 640c77eade..fe19d12649 100644 --- a/app/src/processing/app/Preferences.java +++ b/app/src/processing/app/Preferences.java @@ -1,398 +1,108 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2014-19 The Processing Foundation - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - 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 java.awt.Color; -import java.awt.Font; -import java.awt.SystemColor; -import java.io.*; -import java.util.*; - import processing.app.ui.Toolkit; -import processing.core.*; - +import processing.core.PApplet; -/** - * Storage class for user preferences and environment settings. - *

- * This class does not use the Properties class because .properties files use - * ISO 8859-1 encoding, which is highly likely to be a problem when trying to - * save sketch folders and locations. Like the rest of Processing, we use UTF8. - *

- * We don't use the Java Preferences API because it would entail writing to - * the registry (on Windows), or an obscure file location (on Mac OS X) and - * make it far more difficult (impossible) to remove the preferences.txt to - * reset them (when they become corrupt), or to find the the file to make - * edits for numerous obscure preferences that are not part of the preferences - * window. If we added a generic editor (e.g. about:config in Mozilla) for - * such things, we could start using the Java Preferences API. But wow, that - * sounds like a lot of work. Not unlike writing this paragraph. - */ -public class Preferences { - // had to rename the defaults file because people were editing it - static final String DEFAULTS_FILE = "defaults.txt"; //$NON-NLS-1$ - static final String PREFS_FILE = "preferences.txt"; //$NON-NLS-1$ +import java.awt.*; - static Map defaults; - static Map table = new HashMap<>(); - static File preferencesFile; - private static boolean initialized = false; +public class Preferences extends processing.utils.Preferences { + static public void init() { + processing.utils.Preferences.init(); -// /** @return true if the sketchbook file did not exist */ -// static public boolean init() { - static public void init() { - initialized = true; - // start by loading the defaults, in case something - // important was deleted from the user prefs - try { - // Name changed for 2.1b2 to avoid problems with users modifying or - // replacing the file after doing a search for "preferences.txt". - load(Base.getLibStream(DEFAULTS_FILE)); - } catch (Exception e) { - Messages.showError(null, "Could not read default settings.\n" + - "You'll need to reinstall Processing.", e); - } - - // Clone the defaults, then override any them with the user's preferences. - // This ensures that any new/added preference will be present. - defaults = new HashMap<>(table); - - // other things that have to be set explicitly for the defaults - setColor("run.window.bgcolor", SystemColor.control); //$NON-NLS-1$ - - // For CJK users, enable IM support by default - if (Language.useInputMethod()) { - setBoolean("editor.input_method_support", true); - } - - // next load user preferences file - preferencesFile = Base.getSettingsFile(PREFS_FILE); - boolean firstRun = !preferencesFile.exists(); - if (!firstRun) { - try { - load(new FileInputStream(preferencesFile)); - - } catch (Exception ex) { - Messages.showError("Error reading preferences", - "Error reading the preferences file. " + - "Please delete (or move)\n" + - preferencesFile.getAbsolutePath() + - " and restart Processing.", ex); - } - } - - if (checkSketchbookPref() || firstRun) { -// if (firstRun) { - // create a new preferences file if none exists - // saves the defaults out to the file - save(); - } - - PApplet.useNativeSelect = - Preferences.getBoolean("chooser.files.native"); //$NON-NLS-1$ - - // Adding option to disable this in case it's getting in the way - if (get("proxy.system").equals("true")) { - // Use the system proxy settings by default - // https://github.com/processing/processing/issues/2643 - System.setProperty("java.net.useSystemProxies", "true"); - } - - // Set HTTP, HTTPS, and SOCKS proxies for individuals - // who want/need to override the system setting - // http://docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html - // Less readable version with the Oracle style sheet: - // http://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html - handleProxy("http", "http.proxyHost", "http.proxyPort"); - handleProxy("https", "https.proxyHost", "https.proxyPort"); - handleProxy("socks", "socksProxyHost", "socksProxyPort"); - } - - - /** - * For testing, pretend to load preferences without a real file. - */ - static public void skipInit() { - initialized = true; - } - - - static void handleProxy(String protocol, String hostProp, String portProp) { - String proxyHost = get("proxy." + protocol + ".host"); - String proxyPort = get("proxy." + protocol + ".port"); - if (proxyHost != null && proxyHost.length() != 0 && - proxyPort != null && proxyPort.length() != 0) { - System.setProperty(hostProp, proxyHost); - System.setProperty(portProp, proxyPort); - } - - } - - - static public String getPreferencesPath() { - return preferencesFile.getAbsolutePath(); - } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - - /** - * Load a set of key/value pairs from a UTF-8 encoded file into 'table'. - * For 3.0a6, this removes any platform-specific extensions from keys, so - * that we don't have platform-specific entries in a user's preferences.txt - * file, which would require all prefs to be changed twice, or risk being - * overwritten by the unchanged platform-specific version on reload. - */ - static public void load(InputStream input) throws IOException { - HashMap platformSpecific = new HashMap<>(); - - String[] lines = PApplet.loadStrings(input); // Reads as UTF-8 - for (String line : lines) { - if ((line.length() == 0) || - (line.charAt(0) == '#')) continue; - - // this won't properly handle = signs being in the text - int equals = line.indexOf('='); - if (equals != -1) { - String key = line.substring(0, equals).trim(); - String value = line.substring(equals + 1).trim(); - if (!isPlatformSpecific(key, value, platformSpecific)) { - table.put(key, value); + // For CJK users, enable IM support by default + if (Language.useInputMethod() && !getBoolean("editor.input_method_support")) { + setBoolean("editor.input_method_support", true); } - } - } - // Now override the keys with any platform-specific defaults we've found. - for (String key : platformSpecific.keySet()) { - table.put(key, platformSpecific.get(key)); - } - } - - /** - * @param key original key (may include platform extension) - * @param value the value that goes with the key - * @param specific where to put the key/value pairs for *this* platform - * @return true if a platform-specific key - */ - static protected boolean isPlatformSpecific(String key, String value, - Map specific) { - for (String platform : PConstants.platformNames) { - String ext = "." + platform; - if (key.endsWith(ext)) { - String thisPlatform = PConstants.platformNames[PApplet.platform]; - if (platform.equals(thisPlatform)) { - key = key.substring(0, key.lastIndexOf(ext)); - // store this for later overrides - specific.put(key, value); - //} else { - // ignore platform-specific defaults for other platforms, - // but return 'true' because it needn't be added to the big list + if(get("run.window.bgcolor").isEmpty()){ + setColor("run.window.bgcolor", SystemColor.control); } - return true; - } - } - return false; - } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - static public void save() { - // On startup, this is null, but ignore it. It's trying to update the - // prefs for the open sketch before Preferences.init() has been called. - if (preferencesFile != null) { - try { - File dir = preferencesFile.getParentFile(); - File preferencesTemp = File.createTempFile("preferences", ".txt", dir); - if (!preferencesTemp.setWritable(true, false)) { - throw new IOException("Could not set " + preferencesTemp + " writable"); + if(checkSketchbookPref()){ + save(); } - // Fix for 0163 to properly use Unicode when writing preferences.txt - PrintWriter writer = PApplet.createWriter(preferencesTemp); + PApplet.useNativeSelect = + Preferences.getBoolean("chooser.files.native"); //$NON-NLS-1$ - String[] keyList = table.keySet().toArray(new String[0]); - // Sorting is really helpful for debugging, diffing, and finding keys - keyList = PApplet.sort(keyList); - for (String key : keyList) { - writer.println(key + "=" + table.get(key)); //$NON-NLS-1$ + // Adding option to disable this in case it's getting in the way + if (get("proxy.system").equals("true")) { + // Use the system proxy settings by default + // https://github.com/processing/processing/issues/2643 + System.setProperty("java.net.useSystemProxies", "true"); } - writer.flush(); - writer.close(); - // Rename preferences.txt to preferences.old - File oldPreferences = new File(dir, "preferences.old"); - if (oldPreferences.exists()) { - if (!oldPreferences.delete()) { - throw new IOException("Could not delete preferences.old"); - } - } - if (preferencesFile.exists() && - !preferencesFile.renameTo(oldPreferences)) { - throw new IOException("Could not replace preferences.old"); - } - // Make the temporary file into the real preferences - if (!preferencesTemp.renameTo(preferencesFile)) { - throw new IOException("Could not move preferences file into place"); - } + // Set HTTP, HTTPS, and SOCKS proxies for individuals + // who want/need to override the system setting + // http://docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html + // Less readable version with the Oracle style sheet: + // http://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html + handleProxy("http", "http.proxyHost", "http.proxyPort"); + handleProxy("https", "https.proxyHost", "https.proxyPort"); + handleProxy("socks", "socksProxyHost", "socksProxyPort"); - } catch (IOException e) { - Messages.showWarning("Preferences", - "Could not save the Preferences file.", e); - } } - } - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + static public Font getFont(String familyAttr, String sizeAttr, int style) { + int fontSize = getInteger(sizeAttr); - // all the information from preferences.txt - - static public String get(String attribute /*, String defaultValue */) { - if (!initialized) { - throw new RuntimeException( - "Tried reading preferences prior to initialization." - ); + String fontFamily = get(familyAttr); + if ("processing.mono".equals(fontFamily) || + Toolkit.getMonoFontName().equals(fontFamily)) { + return Toolkit.getMonoFont(fontSize, style); + } + return new Font(fontFamily, style, fontSize); } - return table.get(attribute); - } - - - static public String getDefault(String attribute) { - return defaults.get(attribute); - } - - - static public void set(String attribute, String value) { - table.put(attribute, value); - } - - - static public void unset(String attribute) { - table.remove(attribute); - } - - static public boolean getBoolean(String attribute) { - String value = get(attribute); //, null); - return Boolean.parseBoolean(value); - /* - supposedly not needed, because anything besides 'true' - (ignoring case) will just be false.. so if malformed -> false - if (value == null) return defaultValue; + static void handleProxy(String protocol, String hostProp, String portProp) { + String proxyHost = get("proxy." + protocol + ".host"); + String proxyPort = get("proxy." + protocol + ".port"); + if (proxyHost != null && proxyHost.length() != 0 && + proxyPort != null && proxyPort.length() != 0) { + System.setProperty(hostProp, proxyHost); + System.setProperty(portProp, proxyPort); + } - try { - return (new Boolean(value)).booleanValue(); - } catch (NumberFormatException e) { - System.err.println("expecting an integer: " + attribute + " = " + value); } - return defaultValue; - */ - } - - - static public void setBoolean(String attribute, boolean value) { - set(attribute, value ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ - } - - - static public int getInteger(String attribute /*, int defaultValue*/) { - return Integer.parseInt(get(attribute)); - } - static public void setInteger(String key, int value) { - set(key, String.valueOf(value)); - } - - - static public Color getColor(String name) { - Color parsed = Color.GRAY; // set a default - String s = get(name); - if ((s != null) && (s.indexOf("#") == 0)) { //$NON-NLS-1$ - try { - parsed = new Color(Integer.parseInt(s.substring(1), 16)); - } catch (Exception ignored) { } + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + /** + * Check for a 4.0 sketchbook location, and if none exists, + * try to grab it from the 3.0 sketchbook location. + * @return true if a location was found and the pref didn't exist + */ + static protected boolean checkSketchbookPref() { + // If a 4.0 sketchbook location has never been inited + if (getSketchbookPath() == null) { + String threePath = get("sketchbook.path.three"); //$NON-NLS-1$ + // If they've run the 3.0 version, start with that location + if (threePath != null) { + setSketchbookPath(threePath); + return true; // save the sketchbook right away + } + // Otherwise it'll be null, and reset properly by Base + } + return false; } - return parsed; - } - - static public void setColor(String attr, Color what) { - set(attr, "#" + PApplet.hex(what.getRGB() & 0xffffff, 6)); //$NON-NLS-1$ - } - - static public Font getFont(String familyAttr, String sizeAttr, int style) { - int fontSize = getInteger(sizeAttr); - - String fontFamily = get(familyAttr); - if ("processing.mono".equals(fontFamily) || - Toolkit.getMonoFontName().equals(fontFamily)) { - return Toolkit.getMonoFont(fontSize, style); + static public String getOldSketchbookPath() { + return get("sketchbook.path.three"); //$NON-NLS-1$ } - return new Font(fontFamily, style, fontSize); - } - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - /** - * Check for a 4.0 sketchbook location, and if none exists, - * try to grab it from the 3.0 sketchbook location. - * @return true if a location was found and the pref didn't exist - */ - static protected boolean checkSketchbookPref() { - // If a 4.0 sketchbook location has never been inited - if (getSketchbookPath() == null) { - String threePath = get("sketchbook.path.three"); //$NON-NLS-1$ - // If they've run the 3.0 version, start with that location - if (threePath != null) { - setSketchbookPath(threePath); - return true; // save the sketchbook right away - } - // Otherwise it'll be null, and reset properly by Base + static public String getSketchbookPath() { + return get("sketchbook.path.four"); //$NON-NLS-1$ } - return false; - } - - static public String getOldSketchbookPath() { - return get("sketchbook.path.three"); //$NON-NLS-1$ - } - - static public String getSketchbookPath() { - return get("sketchbook.path.four"); //$NON-NLS-1$ - } - - - static protected void setSketchbookPath(String path) { - set("sketchbook.path.four", path); //$NON-NLS-1$ - } + public static void setSketchbookPath(String path) { + set("sketchbook.path.four", path); //$NON-NLS-1$ + } } diff --git a/app/src/processing/app/Schema.kt b/app/src/processing/app/Schema.kt index a02bf1da76..ad7a94879a 100644 --- a/app/src/processing/app/Schema.kt +++ b/app/src/processing/app/Schema.kt @@ -5,6 +5,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch import processing.app.ui.Editor +import processing.utils.Messages import java.io.File import java.io.FileOutputStream import java.net.URI diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index 8bb50352b0..1bef3da039 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -28,6 +28,7 @@ import processing.app.ui.Recent; import processing.app.ui.Toolkit; import processing.core.*; +import processing.utils.Util; import java.awt.Color; import java.awt.Component; diff --git a/app/src/processing/app/SketchCode.java b/app/src/processing/app/SketchCode.java index 299b9373ae..797172ad90 100644 --- a/app/src/processing/app/SketchCode.java +++ b/app/src/processing/app/SketchCode.java @@ -24,6 +24,8 @@ package processing.app; +import processing.utils.Util; + import java.io.*; import java.util.Stack; diff --git a/app/src/processing/app/contrib/AvailableContribution.java b/app/src/processing/app/contrib/AvailableContribution.java index a190c0bf10..0a682f3905 100644 --- a/app/src/processing/app/contrib/AvailableContribution.java +++ b/app/src/processing/app/contrib/AvailableContribution.java @@ -31,6 +31,7 @@ import processing.core.PApplet; import processing.data.StringDict; import processing.data.StringList; +import processing.utils.Util; /** diff --git a/app/src/processing/app/contrib/ContributionListing.java b/app/src/processing/app/contrib/ContributionListing.java index 08b8d307c7..4271977e77 100644 --- a/app/src/processing/app/contrib/ContributionListing.java +++ b/app/src/processing/app/contrib/ContributionListing.java @@ -32,7 +32,7 @@ import processing.app.Base; import processing.app.Messages; import processing.app.UpdateCheck; -import processing.app.Util; +import processing.utils.Util; import processing.core.PApplet; import processing.data.StringDict; import processing.data.StringList; diff --git a/app/src/processing/app/contrib/ContributionManager.java b/app/src/processing/app/contrib/ContributionManager.java index c4d45f7d7d..31b923a2f3 100644 --- a/app/src/processing/app/contrib/ContributionManager.java +++ b/app/src/processing/app/contrib/ContributionManager.java @@ -31,7 +31,7 @@ import processing.app.Language; import processing.app.Messages; import processing.app.Platform; -import processing.app.Util; +import processing.utils.Util; import processing.app.ui.Editor; import processing.core.PApplet; import processing.data.StringDict; diff --git a/app/src/processing/app/contrib/ContributionType.java b/app/src/processing/app/contrib/ContributionType.java index 83ed9eb687..44f6c16325 100644 --- a/app/src/processing/app/contrib/ContributionType.java +++ b/app/src/processing/app/contrib/ContributionType.java @@ -29,7 +29,7 @@ import processing.app.Base; import processing.app.Library; import processing.app.Messages; -import processing.app.Util; +import processing.utils.Util; import processing.app.ui.Editor; import processing.core.PApplet; import processing.data.StringDict; diff --git a/app/src/processing/app/contrib/ListPanel.java b/app/src/processing/app/contrib/ListPanel.java index 21062d2f82..89133cd631 100644 --- a/app/src/processing/app/contrib/ListPanel.java +++ b/app/src/processing/app/contrib/ListPanel.java @@ -34,7 +34,7 @@ import javax.swing.RowSorter.SortKey; import javax.swing.table.*; -import processing.app.Util; +import processing.utils.Util; import processing.app.ui.Theme; import processing.app.laf.PdeScrollBarUI; import processing.app.ui.Toolkit; diff --git a/app/src/processing/app/contrib/LocalContribution.java b/app/src/processing/app/contrib/LocalContribution.java index df0e8cbfdd..558003b781 100644 --- a/app/src/processing/app/contrib/LocalContribution.java +++ b/app/src/processing/app/contrib/LocalContribution.java @@ -30,13 +30,12 @@ import java.util.*; import java.util.zip.*; -import javax.swing.JOptionPane; - import processing.app.*; import processing.app.ui.Editor; import processing.core.PApplet; import processing.data.StringDict; import processing.data.StringList; +import processing.utils.Util; /** diff --git a/app/src/processing/app/contrib/ModeContribution.java b/app/src/processing/app/contrib/ModeContribution.java index b794d12b3c..8f601eabb0 100644 --- a/app/src/processing/app/contrib/ModeContribution.java +++ b/app/src/processing/app/contrib/ModeContribution.java @@ -34,7 +34,7 @@ import processing.app.Base; import processing.app.Messages; import processing.app.Mode; -import processing.app.Util; +import processing.utils.Util; public class ModeContribution extends LocalContribution { diff --git a/app/src/processing/app/contrib/StatusPanel.java b/app/src/processing/app/contrib/StatusPanel.java index ff2511b5a3..3847595d85 100644 --- a/app/src/processing/app/contrib/StatusPanel.java +++ b/app/src/processing/app/contrib/StatusPanel.java @@ -40,7 +40,7 @@ import javax.swing.text.html.HTMLDocument; import processing.app.Language; -import processing.app.Util; +import processing.utils.Util; import processing.app.laf.PdeButtonUI; import processing.app.laf.PdeProgressBarUI; import processing.app.ui.Theme; diff --git a/app/src/processing/app/platform/DefaultPlatform.java b/app/src/processing/app/platform/DefaultPlatform.java index 18997755b7..de965a6007 100644 --- a/app/src/processing/app/platform/DefaultPlatform.java +++ b/app/src/processing/app/platform/DefaultPlatform.java @@ -32,8 +32,6 @@ import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLightLaf; -import com.sun.jna.Library; -import com.sun.jna.Native; import processing.app.Base; import processing.app.Preferences; @@ -206,23 +204,6 @@ public void setInterfaceZoom() throws Exception { public void saveLanguage(String languageCode) { } - /** - * This function should throw an exception or return a value. - * Do not return null. - */ - public File getSettingsFolder() throws Exception { - File override = Base.getSettingsOverride(); - if (override != null) { - return override; - } - - // If no subclass has a behavior, default to making a - // ".processing" directory in the user's home directory. - File home = new File(System.getProperty("user.home")); - return new File(home, ".processing"); - } - - /** * @return if not overridden, a folder named "sketchbook" in user.home. * @throws Exception so that subclasses can throw a fit diff --git a/app/src/processing/app/platform/LinuxPlatform.java b/app/src/processing/app/platform/LinuxPlatform.java index 3426144cae..9e0eab46bc 100644 --- a/app/src/processing/app/platform/LinuxPlatform.java +++ b/app/src/processing/app/platform/LinuxPlatform.java @@ -26,9 +26,8 @@ import java.awt.Desktop; import java.awt.Toolkit; -import processing.app.Base; -import processing.app.Messages; import processing.app.Preferences; +import processing.app.Base; import processing.core.PApplet; import javax.swing.*; @@ -90,39 +89,6 @@ static public String getHomeDir(String user) throws Exception { } - @Override - public File getSettingsFolder() throws Exception { - File override = Base.getSettingsOverride(); - if (override != null) { - return override; - } - - // https://github.com/processing/processing4/issues/203 - // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html - - File configHome = null; - - // Check to see if the user has set a different location for their config - String configHomeEnv = System.getenv("XDG_CONFIG_HOME"); - if (configHomeEnv != null && !configHomeEnv.isBlank()) { - configHome = new File(configHomeEnv); - if (!configHome.exists()) { - Messages.err("XDG_CONFIG_HOME is set to " + configHomeEnv + " but does not exist."); - configHome = null; // don't use non-existent folder - } - } - String snapUserCommon = System.getenv("SNAP_USER_COMMON"); - if (snapUserCommon != null && !snapUserCommon.isBlank()) { - configHome = new File(snapUserCommon); - } - // If not set properly, use the default - if (configHome == null) { - configHome = new File(getHomeDir(), ".config"); - } - return new File(configHome, "processing"); - } - - @Override public File getDefaultSketchbookFolder() throws Exception { return new File(getHomeDir(), "sketchbook"); diff --git a/app/src/processing/app/platform/MacPlatform.java b/app/src/processing/app/platform/MacPlatform.java index f26c8f2c66..cb4fa82bd8 100644 --- a/app/src/processing/app/platform/MacPlatform.java +++ b/app/src/processing/app/platform/MacPlatform.java @@ -23,7 +23,6 @@ package processing.app.platform; import java.awt.*; -import java.awt.desktop.AppReopenedEvent; import java.awt.desktop.AppReopenedListener; import java.io.File; import java.io.FileNotFoundException; @@ -112,15 +111,6 @@ public void initBase(Base base) { } - public File getSettingsFolder() throws Exception { - File override = Base.getSettingsOverride(); - if (override != null) { - return override; - } - return new File(getLibraryFolder(), "Processing"); - } - - public File getDefaultSketchbookFolder() throws Exception { return new File(getDocumentsFolder(), "Processing"); } diff --git a/app/src/processing/app/platform/WindowsPlatform.java b/app/src/processing/app/platform/WindowsPlatform.java index b74a1674c3..b463887f0f 100644 --- a/app/src/processing/app/platform/WindowsPlatform.java +++ b/app/src/processing/app/platform/WindowsPlatform.java @@ -24,7 +24,6 @@ import java.awt.*; import java.io.File; -import java.io.IOException; import java.io.UnsupportedEncodingException; import com.sun.jna.Library; @@ -351,55 +350,6 @@ protected void checkPath() { } - // looking for Documents and Settings/blah/Application Data/Processing - public File getSettingsFolder() throws Exception { - File override = Base.getSettingsOverride(); - if (override != null) { - return override; - } - - try { - String appDataRoaming = getAppDataPath(); - if (appDataRoaming != null) { - File settingsFolder = new File(appDataRoaming, APP_NAME); - if (settingsFolder.exists() || settingsFolder.mkdirs()) { - return settingsFolder; - } - } - - String appDataLocal = getLocalAppDataPath(); - if (appDataLocal != null) { - File settingsFolder = new File(appDataLocal, APP_NAME); - if (settingsFolder.exists() || settingsFolder.mkdirs()) { - return settingsFolder; - } - } - - if (appDataRoaming == null && appDataLocal == null) { - throw new IOException("Could not get the AppData folder"); - } - - // https://github.com/processing/processing/issues/3838 - throw new IOException("Permissions error: make sure that " + - appDataRoaming + " or " + appDataLocal + - " is writable."); - - } catch (UnsatisfiedLinkError ule) { - String path = new File("lib").getCanonicalPath(); - - String msg = Util.containsNonASCII(path) ? - """ - Please move Processing to a location with only - ASCII characters in the path and try again. - https://github.com/processing/processing/issues/3543 - """ : - "Could not find JNA support files, please reinstall Processing."; - Messages.showError("Windows JNA Problem", msg, ule); - return null; // unreachable - } - } - - /* What's happening internally with JNA https://github.com/java-native-access/jna/blob/master/contrib/platform/src/com/sun/jna/platform/win32/Shell32.java diff --git a/app/src/processing/app/syntax/PdeInputHandler.java b/app/src/processing/app/syntax/PdeInputHandler.java index cc763bd3d7..aca0633646 100644 --- a/app/src/processing/app/syntax/PdeInputHandler.java +++ b/app/src/processing/app/syntax/PdeInputHandler.java @@ -27,8 +27,8 @@ import java.awt.event.KeyEvent; -import processing.app.Platform; import processing.app.Preferences; +import processing.app.Platform; import processing.app.ui.Editor; diff --git a/app/src/processing/app/tools/Archiver.java b/app/src/processing/app/tools/Archiver.java index 98879d3085..0bbfb510fb 100644 --- a/app/src/processing/app/tools/Archiver.java +++ b/app/src/processing/app/tools/Archiver.java @@ -27,6 +27,7 @@ import processing.app.*; import processing.app.ui.Editor; import processing.awt.ShimAWT; +import processing.utils.Util; import java.io.*; import java.text.*; diff --git a/app/src/processing/app/tools/ThemeSelector.java b/app/src/processing/app/tools/ThemeSelector.java index 1b146d02bb..5fd79bd293 100644 --- a/app/src/processing/app/tools/ThemeSelector.java +++ b/app/src/processing/app/tools/ThemeSelector.java @@ -30,6 +30,7 @@ import processing.app.ui.Toolkit; import processing.core.PApplet; import processing.data.StringDict; +import processing.utils.Util; import javax.swing.*; import javax.swing.border.EmptyBorder; diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index a06cbe2383..da4ae0bf7e 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -48,20 +48,9 @@ import javax.swing.text.html.*; import javax.swing.undo.*; -import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.util.SystemInfo; -import processing.app.Base; -import processing.app.Formatter; -import processing.app.Language; -import processing.app.Messages; -import processing.app.Mode; -import processing.app.Platform; +import processing.app.*; import processing.app.Preferences; -import processing.app.Problem; -import processing.app.RunnerListener; -import processing.app.Sketch; -import processing.app.SketchCode; -import processing.app.SketchException; import processing.app.contrib.ContributionManager; import processing.app.laf.PdeMenuItemUI; import processing.app.syntax.*; diff --git a/app/src/processing/app/ui/EditorConsole.java b/app/src/processing/app/ui/EditorConsole.java index c8c40ee487..996831a89c 100644 --- a/app/src/processing/app/ui/EditorConsole.java +++ b/app/src/processing/app/ui/EditorConsole.java @@ -36,8 +36,8 @@ import javax.swing.border.MatteBorder; import javax.swing.text.*; -import processing.app.Console; import processing.app.Preferences; +import processing.app.Console; import processing.app.laf.PdeScrollBarUI; diff --git a/app/src/processing/app/ui/EditorStatus.java b/app/src/processing/app/ui/EditorStatus.java index 02a85f10e1..c97c6c22bf 100644 --- a/app/src/processing/app/ui/EditorStatus.java +++ b/app/src/processing/app/ui/EditorStatus.java @@ -40,8 +40,8 @@ import javax.swing.plaf.basic.BasicSplitPaneDivider; import javax.swing.plaf.basic.BasicSplitPaneUI; -import processing.app.Platform; import processing.app.Preferences; +import processing.app.Platform; import processing.core.PApplet; diff --git a/app/src/processing/app/ui/ExamplesFrame.java b/app/src/processing/app/ui/ExamplesFrame.java index 0d8e89be9a..fff0f04e15 100644 --- a/app/src/processing/app/ui/ExamplesFrame.java +++ b/app/src/processing/app/ui/ExamplesFrame.java @@ -52,13 +52,8 @@ import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; -import processing.app.Base; -import processing.app.Language; -import processing.app.Library; -import processing.app.Mode; -import processing.app.Platform; +import processing.app.*; import processing.app.Preferences; -import processing.app.SketchReference; import processing.app.contrib.Contribution; import processing.app.contrib.ContributionManager; import processing.app.contrib.ContributionType; diff --git a/app/src/processing/app/ui/ExportPrompt.java b/app/src/processing/app/ui/ExportPrompt.java index f4a8cece99..5669e1df5f 100644 --- a/app/src/processing/app/ui/ExportPrompt.java +++ b/app/src/processing/app/ui/ExportPrompt.java @@ -34,9 +34,9 @@ import java.util.ArrayList; import java.util.List; +import processing.app.Preferences; import processing.app.Language; import processing.app.Platform; -import processing.app.Preferences; import processing.app.platform.MacPlatform; import processing.core.PApplet; diff --git a/app/src/processing/app/ui/PreferencesFrame.java b/app/src/processing/app/ui/PreferencesFrame.java index a8cf68c27d..7b206686e2 100644 --- a/app/src/processing/app/ui/PreferencesFrame.java +++ b/app/src/processing/app/ui/PreferencesFrame.java @@ -33,12 +33,8 @@ import com.formdev.flatlaf.FlatClientProperties; -import processing.app.Base; -import processing.app.Language; -import processing.app.Messages; -import processing.app.Platform; +import processing.app.*; import processing.app.Preferences; -import processing.app.SketchName; import processing.awt.ShimAWT; import processing.core.PApplet; diff --git a/app/src/processing/app/ui/Theme.java b/app/src/processing/app/ui/Theme.java index edc14bf584..22b49f7a4f 100644 --- a/app/src/processing/app/ui/Theme.java +++ b/app/src/processing/app/ui/Theme.java @@ -22,9 +22,9 @@ package processing.app.ui; +import processing.app.Preferences; import processing.app.Base; import processing.app.Messages; -import processing.app.Preferences; import processing.app.Settings; import processing.app.syntax.SyntaxStyle; import processing.core.PApplet; diff --git a/app/src/processing/app/ui/Toolkit.java b/app/src/processing/app/ui/Toolkit.java index 8a5ae418bb..18358e0516 100644 --- a/app/src/processing/app/ui/Toolkit.java +++ b/app/src/processing/app/ui/Toolkit.java @@ -72,7 +72,7 @@ import processing.app.Messages; import processing.app.Platform; import processing.app.Preferences; -import processing.app.Util; +import processing.utils.Util; import processing.awt.PGraphicsJava2D; import processing.awt.PShapeJava2D; import processing.core.PApplet; diff --git a/app/src/processing/app/ui/theme/Locale.kt b/app/src/processing/app/ui/theme/Locale.kt index 254c0946c1..b720ef3782 100644 --- a/app/src/processing/app/ui/theme/Locale.kt +++ b/app/src/processing/app/ui/theme/Locale.kt @@ -3,11 +3,10 @@ package processing.app.ui.theme import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.compositionLocalOf -import processing.app.LocalPreferences -import processing.app.Messages import processing.app.Platform import processing.app.PlatformStart import processing.app.watchFile +import processing.utils.Messages import java.io.File import java.io.InputStream import java.util.* diff --git a/app/utils/build.gradle.kts b/app/utils/build.gradle.kts new file mode 100644 index 0000000000..a1a9c97a53 --- /dev/null +++ b/app/utils/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + id("java") + kotlin("jvm") version libs.versions.kotlin +} + +group = "processing.utils" + +repositories { + mavenCentral() + google() + maven { url = uri("https://jogamp.org/deployment/maven") } +} + +sourceSets{ + main{ + java{ + srcDirs("src") + } + kotlin{ + srcDirs("src") + } + resources{ + srcDirs("resources", listOf("languages", "fonts", "theme").map { "../../build/shared/lib/$it" }) + } + } + test{ + kotlin{ + srcDirs("src/test") + } + } +} + +dependencies { + implementation(project(":core")) + + implementation(libs.jna) + implementation(libs.jnaplatform) + + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") +} + +tasks.test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/app/utils/src/main/java/processing/utils/Base.java b/app/utils/src/main/java/processing/utils/Base.java new file mode 100644 index 0000000000..a265bcab69 --- /dev/null +++ b/app/utils/src/main/java/processing/utils/Base.java @@ -0,0 +1,72 @@ +package processing.utils; + +import java.io.File; +import java.io.InputStream; + +public class Base { + static public boolean DEBUG = System.getenv().containsKey("DEBUG"); + + + static public boolean isCommandLine() { + return Boolean.getBoolean("processing.cli"); + } + + + /** + * Convenience method to get a File object for the specified filename inside + * the settings folder. Used to get preferences and recent sketch files. + * + * @param filename A file inside the settings folder. + * @return filename wrapped as a File object inside the settings folder + */ + static public File getSettingsFile(String filename) { + File settingsFolder = null; + + try { + settingsFolder = SettingsResolver.getSettingsFolder(); + + // create the folder if it doesn't exist already + if (!settingsFolder.exists()) { + if (!settingsFolder.mkdirs()) { + Messages.showError("Settings issues", + "Could not create the folder" + + settingsFolder, null); + } + } + } catch (Exception e) { + Messages.showError("An rare and unknowable thing happened", + "Could not get the settings folder.", e); + } + return new File(settingsFolder, filename); + } + + + /** + * Retrieves an InputStream to a resource file located within the JAR package. + * This method uses Java's resource loading system to fetch files bundled in the application, + * such as configuration files, data, or assets. + * + * @param resourceName The name or path of the resource file to be loaded. + * This should match the location in the JAR's structure. + * @return An InputStream that can be used to read the contents of the requested resource file, + * or null if the resource is not found. + * @throws IllegalArgumentException if the resource cannot be located. + */ + public static InputStream getLibStream(String resourceName) { + if (resourceName == null || resourceName.isEmpty()) { + throw new IllegalArgumentException("Resource name cannot be null or empty"); + } + // Ensure the resource name starts with exactly one "/" + if (!resourceName.startsWith("/")) { + resourceName = "/" + resourceName; // Prepend "/" if missing + } else { + resourceName = resourceName.replaceAll("^/+","/"); // Ensure only one "/" at start + } + + InputStream stream = Base.class.getResourceAsStream(resourceName); + if (stream == null) { + throw new IllegalArgumentException("Resource not found: " + resourceName); + } + return stream; + } +} diff --git a/app/utils/src/main/java/processing/utils/Main.java b/app/utils/src/main/java/processing/utils/Main.java new file mode 100644 index 0000000000..04449f5411 --- /dev/null +++ b/app/utils/src/main/java/processing/utils/Main.java @@ -0,0 +1,27 @@ +package processing.utils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +//TIP To Run code, press or +// click the icon in the gutter. +public class Main { + public static void main(String[] args) { + //TIP Press with your caret at the highlighted text + // to see how IntelliJ IDEA suggests fixing it. + InputStream inputStream = Base.getLibStream("/defaults.txt"); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { + String line; + while ((line = reader.readLine()) != null) { + // Print each line from the InputStream + System.out.println(line); + } + } catch (IOException e) { + // Handle error if an issue occurs while reading + System.err.println("Error reading from InputStream: " + e.getMessage()); + } + + } +} \ No newline at end of file diff --git a/app/utils/src/main/java/processing/utils/Messages.kt b/app/utils/src/main/java/processing/utils/Messages.kt new file mode 100644 index 0000000000..c6418e9df7 --- /dev/null +++ b/app/utils/src/main/java/processing/utils/Messages.kt @@ -0,0 +1,144 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* + Part of the Processing project - http://processing.org + + Copyright (c) 2015 The Processing Foundation + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2, as published by the Free Software Foundation. + + 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.utils + +import java.awt.Frame +import javax.swing.JOptionPane + +open class Messages { + companion object { + /** + * "No cookie for you" type messages. Nothing fatal or all that + * much of a bummer, but something to notify the user about. + */ + @JvmStatic + fun showMessage(title: String = "Message", message: String) { + if (Base.isCommandLine()) { + println("$title: $message") + } else { + JOptionPane.showMessageDialog( + Frame(), message, title, + JOptionPane.INFORMATION_MESSAGE + ) + } + } + + + /** + * Non-fatal error message with optional stack trace side dish. + */ + /** + * Non-fatal error message. + */ + @JvmStatic + @JvmOverloads + fun showWarning(title: String = "Warning", message: String, e: Throwable? = null) { + if (Base.isCommandLine()) { + println("$title: $message") + } else { + JOptionPane.showMessageDialog( + Frame(), message, title, + JOptionPane.WARNING_MESSAGE + ) + } + e?.printStackTrace() + } + + + /** + * Show an error message that's actually fatal to the program. + * This is an error that can't be recovered. Use showWarning() + * for errors that allow P5 to continue running. + */ + @JvmStatic + fun showError(title: String? = "Error", message: String, e: Throwable?) { + if (Base.isCommandLine()) { + System.err.println("$title: $message") + } else { + JOptionPane.showMessageDialog( + Frame(), message, title, + JOptionPane.ERROR_MESSAGE + ) + } + e?.printStackTrace() + System.exit(1) + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + @JvmStatic + @Deprecated("Use log() instead") + fun log(from: Any, message: String) { + if (Base.DEBUG) { + val callingClass = Throwable() + .stackTrace[2] + .className + .formatClassName() + println("$callingClass: $message") + } + } + + @JvmStatic + fun log(message: String?) { + if (Base.DEBUG) { + val callingClass = Throwable() + .stackTrace[2] + .className + .formatClassName() + println("$callingClass$message") + } + } + + @JvmStatic + fun logf(message: String?, vararg args: Any?) { + if (Base.DEBUG) { + val callingClass = Throwable() + .stackTrace[2] + .className + .formatClassName() + System.out.printf("$callingClass$message", *args) + } + } + + @JvmStatic + @JvmOverloads + fun err(message: String?, e: Throwable? = null) { + if (Base.DEBUG) { + if (message != null) { + val callingClass = Throwable() + .stackTrace[4] + .className + .formatClassName() + System.err.println("$callingClass$message") + } + e?.printStackTrace() + } + } + } +} + +// Helper functions to give the base classes a color +fun String.formatClassName() = this + .replace("processing.", "") + .replace(".", "/") + .padEnd(40) + .colorizePathParts() +fun String.colorizePathParts() = split("/").joinToString("/") { part -> + "\u001B[${31 + (part.hashCode() and 0x7).rem(6)}m$part\u001B[0m" +} \ No newline at end of file diff --git a/app/utils/src/main/java/processing/utils/Preferences.java b/app/utils/src/main/java/processing/utils/Preferences.java new file mode 100644 index 0000000000..faf153e75a --- /dev/null +++ b/app/utils/src/main/java/processing/utils/Preferences.java @@ -0,0 +1,308 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2014-19 The Processing Foundation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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.utils; + +import java.awt.Color; +import java.awt.SystemColor; +import java.io.*; +import java.util.*; + +import processing.core.*; + + +/** + * Storage class for user preferences and environment settings. + *

+ * This class does not use the Properties class because .properties files use + * ISO 8859-1 encoding, which is highly likely to be a problem when trying to + * save sketch folders and locations. Like the rest of Processing, we use UTF8. + *

+ * We don't use the Java Preferences API because it would entail writing to + * the registry (on Windows), or an obscure file location (on Mac OS X) and + * make it far more difficult (impossible) to remove the preferences.txt to + * reset them (when they become corrupt), or to find the the file to make + * edits for numerous obscure preferences that are not part of the preferences + * window. If we added a generic editor (e.g. about:config in Mozilla) for + * such things, we could start using the Java Preferences API. But wow, that + * sounds like a lot of work. Not unlike writing this paragraph. + */ +public class Preferences { + // had to rename the defaults file because people were editing it + static final String DEFAULTS_FILE = "defaults.txt"; //$NON-NLS-1$ + static final String PREFS_FILE = "preferences.txt"; //$NON-NLS-1$ + + static Map defaults; + static Map table = new HashMap<>(); + static File preferencesFile; + private static boolean initialized = false; + + +// /** @return true if the sketchbook file did not exist */ +// static public boolean init() { + static public void init() { + initialized = true; + // start by loading the defaults, in case something + // important was deleted from the user prefs + try { + // Name changed for 2.1b2 to avoid problems with users modifying or + // replacing the file after doing a search for "preferences.txt". + load(Base.getLibStream(DEFAULTS_FILE)); + } catch (Exception e) { + Messages.showError(null, "Could not read default settings.\n" + + "You'll need to reinstall Processing.", e); + } + + // Clone the defaults, then override any them with the user's preferences. + // This ensures that any new/added preference will be present. + defaults = new HashMap<>(table); + + // next load user preferences file + preferencesFile = Base.getSettingsFile(PREFS_FILE); + boolean firstRun = !preferencesFile.exists(); + if (!firstRun) { + try { + load(new FileInputStream(preferencesFile)); + + } catch (Exception ex) { + Messages.showError("Error reading preferences", + "Error reading the preferences file. " + + "Please delete (or move)\n" + + preferencesFile.getAbsolutePath() + + " and restart Processing.", ex); + } + } + + if (firstRun) { + // create a new preferences file if none exists + // saves the defaults out to the file + save(); + } + + } + + + /** + * For testing, pretend to load preferences without a real file. + */ + static public void skipInit() { + initialized = true; + } + + + static public String getPreferencesPath() { + return preferencesFile.getAbsolutePath(); + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + /** + * Load a set of key/value pairs from a UTF-8 encoded file into 'table'. + * For 3.0a6, this removes any platform-specific extensions from keys, so + * that we don't have platform-specific entries in a user's preferences.txt + * file, which would require all prefs to be changed twice, or risk being + * overwritten by the unchanged platform-specific version on reload. + */ + static public void load(InputStream input) throws IOException { + HashMap platformSpecific = new HashMap<>(); + + String[] lines = PApplet.loadStrings(input); // Reads as UTF-8 + for (String line : lines) { + if ((line.length() == 0) || + (line.charAt(0) == '#')) continue; + + // this won't properly handle = signs being in the text + int equals = line.indexOf('='); + if (equals != -1) { + String key = line.substring(0, equals).trim(); + String value = line.substring(equals + 1).trim(); + if (!isPlatformSpecific(key, value, platformSpecific)) { + table.put(key, value); + } + } + } + // Now override the keys with any platform-specific defaults we've found. + for (String key : platformSpecific.keySet()) { + table.put(key, platformSpecific.get(key)); + } + } + + + /** + * @param key original key (may include platform extension) + * @param value the value that goes with the key + * @param specific where to put the key/value pairs for *this* platform + * @return true if a platform-specific key + */ + static protected boolean isPlatformSpecific(String key, String value, + Map specific) { + for (String platform : PConstants.platformNames) { + String ext = "." + platform; + if (key.endsWith(ext)) { + String thisPlatform = PConstants.platformNames[PApplet.platform]; + if (platform.equals(thisPlatform)) { + key = key.substring(0, key.lastIndexOf(ext)); + // store this for later overrides + specific.put(key, value); + //} else { + // ignore platform-specific defaults for other platforms, + // but return 'true' because it needn't be added to the big list + } + return true; + } + } + return false; + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + static public void save() { + // On startup, this is null, but ignore it. It's trying to update the + // prefs for the open sketch before Preferences.init() has been called. + if (preferencesFile != null) { + try { + File dir = preferencesFile.getParentFile(); + File preferencesTemp = File.createTempFile("preferences", ".txt", dir); + if (!preferencesTemp.setWritable(true, false)) { + throw new IOException("Could not set " + preferencesTemp + " writable"); + } + + // Fix for 0163 to properly use Unicode when writing preferences.txt + PrintWriter writer = PApplet.createWriter(preferencesTemp); + + String[] keyList = table.keySet().toArray(new String[0]); + // Sorting is really helpful for debugging, diffing, and finding keys + keyList = PApplet.sort(keyList); + for (String key : keyList) { + writer.println(key + "=" + table.get(key)); //$NON-NLS-1$ + } + writer.flush(); + writer.close(); + + // Rename preferences.txt to preferences.old + File oldPreferences = new File(dir, "preferences.old"); + if (oldPreferences.exists()) { + if (!oldPreferences.delete()) { + throw new IOException("Could not delete preferences.old"); + } + } + if (preferencesFile.exists() && + !preferencesFile.renameTo(oldPreferences)) { + throw new IOException("Could not replace preferences.old"); + } + // Make the temporary file into the real preferences + if (!preferencesTemp.renameTo(preferencesFile)) { + throw new IOException("Could not move preferences file into place"); + } + + } catch (IOException e) { + Messages.showWarning("Preferences", + "Could not save the Preferences file.", e); + } + } + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + // all the information from preferences.txt + + static public String get(String attribute /*, String defaultValue */) { + if (!initialized) { + throw new RuntimeException( + "Tried reading preferences prior to initialization." + ); + } + return table.get(attribute); + } + + + static public String getDefault(String attribute) { + return defaults.get(attribute); + } + + + static public void set(String attribute, String value) { + table.put(attribute, value); + } + + + static public void unset(String attribute) { + table.remove(attribute); + } + + + static public boolean getBoolean(String attribute) { + String value = get(attribute); //, null); + return Boolean.parseBoolean(value); + + /* + supposedly not needed, because anything besides 'true' + (ignoring case) will just be false.. so if malformed -> false + if (value == null) return defaultValue; + + try { + return (new Boolean(value)).booleanValue(); + } catch (NumberFormatException e) { + System.err.println("expecting an integer: " + attribute + " = " + value); + } + return defaultValue; + */ + } + + + static public void setBoolean(String attribute, boolean value) { + set(attribute, value ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + + static public int getInteger(String attribute /*, int defaultValue*/) { + return Integer.parseInt(get(attribute)); + } + + + static public void setInteger(String key, int value) { + set(key, String.valueOf(value)); + } + + + static public Color getColor(String name) { + Color parsed = Color.GRAY; // set a default + String s = get(name); + if ((s != null) && (s.indexOf("#") == 0)) { //$NON-NLS-1$ + try { + parsed = new Color(Integer.parseInt(s.substring(1), 16)); + } catch (Exception ignored) { } + } + return parsed; + } + + + static public void setColor(String attr, Color what) { + set(attr, "#" + PApplet.hex(what.getRGB() & 0xffffff, 6)); //$NON-NLS-1$ + } + +} diff --git a/app/utils/src/main/java/processing/utils/SettingsResolver.java b/app/utils/src/main/java/processing/utils/SettingsResolver.java new file mode 100644 index 0000000000..b58f99ef4b --- /dev/null +++ b/app/utils/src/main/java/processing/utils/SettingsResolver.java @@ -0,0 +1,35 @@ +package processing.utils; + +import processing.utils.settingslocation.*; + +import java.io.File; + +public class SettingsResolver { + /** + * Get the directory that can store settings. (Library on OS X, App Data or + * something similar on Windows, a dot folder on Linux.) Removed this as a + * preference for 3.0a3 because we need this to be stable, but adding back + * for 4.0 beta 4 so that folks can do 'portable' versions again. + */ + static public File getSettingsFolder() throws Exception { + File settingsFolder = null; + + String os = System.getProperty("os.name"); + DefaultLocation loc = null; + + if (os.contains("Mac")) { + loc = new MacLocation(); + } else if (os.contains("Linux")) { + loc = new LinuxLocation(); + } else if (os.contains("Windows")) { + loc = new WindowsLocation(); + } else { + loc = new DefaultLocation(); + } + + settingsFolder = loc.getSettingsFolder(); + + return settingsFolder; + + } +} diff --git a/app/src/processing/app/Util.java b/app/utils/src/main/java/processing/utils/Util.java similarity index 99% rename from app/src/processing/app/Util.java rename to app/utils/src/main/java/processing/utils/Util.java index 4c94af5fe5..281de6fb6d 100644 --- a/app/src/processing/app/Util.java +++ b/app/utils/src/main/java/processing/utils/Util.java @@ -20,7 +20,7 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -package processing.app; +package processing.utils; import java.io.*; import java.net.HttpURLConnection; @@ -278,13 +278,14 @@ static public File createTempFolder(String prefix, String suffix, static public File getProcessingTemp() throws IOException { + String os = System.getProperty("os.name"); String tmpDir = System.getProperty("java.io.tmpdir"); File directory = new File(tmpDir, "processing"); if (!directory.exists()) { if (directory.mkdirs()) { // Set the parent directory writable for multi-user machines. // https://github.com/processing/processing4/issues/666 - if (Platform.isLinux()) { + if (os.contains("Linux")) { Path path = directory.toPath(); Files.setPosixFilePermissions(path, PosixFilePermissions.fromString("rwxrwxrwx")); @@ -347,8 +348,9 @@ static public void copyDir(File sourceDir, static public void copyDirNative(File sourceDir, File targetDir) throws IOException { + String os = System.getProperty("os.name"); Process process; - if (Platform.isMacOS() || Platform.isLinux()) { + if (os.contains("Mac") || os.contains("Linux")) { process = Runtime.getRuntime().exec(new String[] { "cp", "-a", sourceDir.getAbsolutePath(), targetDir.getAbsolutePath() }); diff --git a/app/utils/src/main/java/processing/utils/settingslocation/DefaultLocation.java b/app/utils/src/main/java/processing/utils/settingslocation/DefaultLocation.java new file mode 100644 index 0000000000..ecf8fbdd0e --- /dev/null +++ b/app/utils/src/main/java/processing/utils/settingslocation/DefaultLocation.java @@ -0,0 +1,17 @@ +package processing.utils.settingslocation; + +import java.io.File; + +public class DefaultLocation { + /** + * This function should throw an exception or return a value. + * Do not return null. + */ + public File getSettingsFolder() throws Exception { + // If no subclass has a behavior, default to making a + // ".processing" directory in the user's home directory. + File home = new File(System.getProperty("user.home")); + return new File(home, ".processing"); + } + +} diff --git a/app/utils/src/main/java/processing/utils/settingslocation/LinuxLocation.java b/app/utils/src/main/java/processing/utils/settingslocation/LinuxLocation.java new file mode 100644 index 0000000000..439664de19 --- /dev/null +++ b/app/utils/src/main/java/processing/utils/settingslocation/LinuxLocation.java @@ -0,0 +1,60 @@ +package processing.utils.settingslocation; + +import processing.core.PApplet; +import processing.utils.Messages; + +import java.io.File; + +public class LinuxLocation extends DefaultLocation{ + String homeDir; + + @Override + public File getSettingsFolder() throws Exception { + // https://github.com/processing/processing4/issues/203 + // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + + File configHome = null; + + // Check to see if the user has set a different location for their config + String configHomeEnv = System.getenv("XDG_CONFIG_HOME"); + if (configHomeEnv != null && !configHomeEnv.isBlank()) { + configHome = new File(configHomeEnv); + if (!configHome.exists()) { + Messages.err("XDG_CONFIG_HOME is set to " + configHomeEnv + " but does not exist."); + configHome = null; // don't use non-existent folder + } + } + String snapUserCommon = System.getenv("SNAP_USER_COMMON"); + if (snapUserCommon != null && !snapUserCommon.isBlank()) { + configHome = new File(snapUserCommon); + } + // If not set properly, use the default + if (configHome == null) { + configHome = new File(getHomeDir(), ".config"); + } + return new File(configHome, "processing"); + } + + + // Java sets user.home to be /root for execution with sudo. + // This method attempts to use the user's real home directory instead. + public String getHomeDir() { + if (homeDir == null) { + // get home directory of SUDO_USER if set, else use user.home + homeDir = System.getProperty("user.home"); + String sudoUser = System.getenv("SUDO_USER"); + if (sudoUser != null && sudoUser.length() != 0) { + try { + homeDir = getHomeDir(sudoUser); + } catch (Exception ignored) { } + } + } + return homeDir; + } + + + static public String getHomeDir(String user) throws Exception { + Process p = PApplet.exec("/bin/sh", "-c", "echo ~" + user); + return PApplet.createReader(p.getInputStream()).readLine(); + } +} diff --git a/app/utils/src/main/java/processing/utils/settingslocation/MacLocation.java b/app/utils/src/main/java/processing/utils/settingslocation/MacLocation.java new file mode 100644 index 0000000000..2029427870 --- /dev/null +++ b/app/utils/src/main/java/processing/utils/settingslocation/MacLocation.java @@ -0,0 +1,25 @@ +package processing.utils.settingslocation; + +import java.io.File; +import java.io.FileNotFoundException; + +public class MacLocation extends DefaultLocation{ + + public File getSettingsFolder() throws Exception { + return new File(getLibraryFolder(), "Processing"); + } + + + // TODO I suspect this won't work much longer, since access to the user's + // home directory seems verboten on more recent macOS versions [fry 191008] + // However, anecdotally it seems that just using the name works, + // and the localization is handled transparently. [fry 220116] + // https://github.com/processing/processing4/issues/9 + protected String getLibraryFolder() throws FileNotFoundException { + File folder = new File(System.getProperty("user.home"), "Library"); + if (!folder.exists()) { + throw new FileNotFoundException("Folder missing: " + folder); + } + return folder.getAbsolutePath(); + } +} diff --git a/app/utils/src/main/java/processing/utils/settingslocation/WindowsLocation.java b/app/utils/src/main/java/processing/utils/settingslocation/WindowsLocation.java new file mode 100644 index 0000000000..e0f6f0dc35 --- /dev/null +++ b/app/utils/src/main/java/processing/utils/settingslocation/WindowsLocation.java @@ -0,0 +1,69 @@ +package processing.utils.settingslocation; + +import com.sun.jna.platform.win32.Shell32Util; +import com.sun.jna.platform.win32.ShlObj; +import processing.utils.Messages; +import processing.utils.Util; + +import java.io.File; +import java.io.IOException; + +public class WindowsLocation extends DefaultLocation{ + static final String APP_NAME = "Processing"; + + // looking for Documents and Settings/blah/Application Data/Processing + public File getSettingsFolder() throws Exception { + + try { + String appDataRoaming = getAppDataPath(); + if (appDataRoaming != null) { + File settingsFolder = new File(appDataRoaming, APP_NAME); + if (settingsFolder.exists() || settingsFolder.mkdirs()) { + return settingsFolder; + } + } + + String appDataLocal = getLocalAppDataPath(); + if (appDataLocal != null) { + File settingsFolder = new File(appDataLocal, APP_NAME); + if (settingsFolder.exists() || settingsFolder.mkdirs()) { + return settingsFolder; + } + } + + if (appDataRoaming == null && appDataLocal == null) { + throw new IOException("Could not get the AppData folder"); + } + + // https://github.com/processing/processing/issues/3838 + throw new IOException("Permissions error: make sure that " + + appDataRoaming + " or " + appDataLocal + + " is writable."); + + } catch (UnsatisfiedLinkError ule) { + String path = new File("lib").getCanonicalPath(); + + String msg = Util.containsNonASCII(path) ? + """ + Please move Processing to a location with only + ASCII characters in the path and try again. + https://github.com/processing/processing/issues/3543 + """ : + "Could not find JNA support files, please reinstall Processing."; + Messages.showError("Windows JNA Problem", msg, ule); + return null; // unreachable + } + } + + + /** Get the Users\name\AppData\Roaming path to write settings files. */ + static private String getAppDataPath() { + return Shell32Util.getSpecialFolderPath(ShlObj.CSIDL_APPDATA, true); + } + + + /** Get the Users\name\AppData\Local path as a settings fallback. */ + static private String getLocalAppDataPath() { + return Shell32Util.getSpecialFolderPath(ShlObj.CSIDL_LOCAL_APPDATA, true); + } +} diff --git a/app/utils/src/main/resources/defaults.txt b/app/utils/src/main/resources/defaults.txt new file mode 100644 index 0000000000..6e3e00f0d6 --- /dev/null +++ b/app/utils/src/main/resources/defaults.txt @@ -0,0 +1,309 @@ +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + +# DO NOT MAKE CHANGES TO THIS FILE!!! + +# These are the default preferences. If you want to modify +# them directly, use the per-user local version of the file: + +# Users -> [username] -> AppData -> Roaming -> +# Processing -> preferences.txt (on Windows 10) + +# ~/Library -> Processing -> preferences.txt (on macOS) + +# ~/.config/processing -> preferences.txt (on Linux) + +# The exact location of your preferences file can be found at +# the bottom of the Preferences window inside Processing. + +# Because AppData and Application Data may be considered +# hidden or system folders on Windows, you'll have to ensure +# that they're visible in order to get at preferences.txt + +# You'll have problems running Processing if you incorrectly +# modify lines in this file. It will probably not start at all. + +# AGAIN, DO NOT ALTER THIS FILE! I'M ONLY YELLING BECAUSE I LOVE YOU! + + +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + +# If you don't want users to have their sketchbook default to +# "My Documents/Processing" on Windows and "Documents/Processing" on OS X, +# set this to another path that will be used by default. +# Note that this path must exist already otherwise it won't see +# the sketchbook folder, and will instead assume the sketchbook +# has gone missing, and that it should instead use the default. +# In 4.0, the location has changed. +#sketchbook.path.four= + +# Whether or not to show the Welcome screen for 4.0 +# (It's always available under Help → Welcome) +welcome.four.show = true +welcome.four.seen = false + +# Set 'true' for the default behavior before 4.0, where the +# main tab must have the same name as the sketch folder +editor.sync_folder_and_filename = true + +# By default, contributions are moved to backup folders when +# they are removed or replaced. The backups can be found at +# sketchbook/libraries/old, sketchbook/tools/old, and sketchbook/modes/old + +# true to backup contributions when "Remove" button is pressed +contribution.backup.on_remove = true +# true to backup contributions when installing a newer version +contribution.backup.on_install = true + +recent.count = 10 + +# Default to the native (AWT) file selector where possible +chooser.files.native = true +# We were shutting this off on macOS because it broke Copy/Paste: +# https://github.com/processing/processing/issues/1035 +# But removing again for 4.0 alpha 5, because the JFileChooser is awful, +# and worse on Big Sur, so a bigger problem than the Copy/Paste issue. +# https://github.com/processing/processing4/issues/77 +#chooser.files.native.macos = false + +# set to 'lab' to interpolate theme gradients using L*a*b* color space +theme.gradient.method = rgb + + +# by default, check the processing server for any updates +# (please avoid disabling, this also helps us know basic numbers +# on how many people are using Processing) +update.check = true + +# on windows, automatically associate .pde files with processing.exe +platform.auto_file_type_associations = true + + +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + +# default size for the main window +editor.window.width.default = 700 +editor.window.height.default = 600 + +editor.window.width.min = 400 +editor.window.height.min = 500 +# tested as approx 440 on OS X +editor.window.height.min.macos = 450 +# tested to be 515 on Windows XP, this leaves some room +editor.window.height.min.windows = 530 +# tested with Raspberry Pi display +editor.window.height.min.linux = 480 + +# scaling for the interface (to handle Windows and Linux HiDPI displays) +editor.zoom = 100% +# automatically set based on system dpi (only helps on Windows) +editor.zoom.auto = true + +# Use the default monospace font included in lib/fonts. +# (As of Processing 4 alpha 5, that's Source Code Pro) +editor.font.family = processing.mono +editor.font.size = 12 + +# To reset everyone's default, replaced editor.antialias with editor.smooth +# for 2.1. Fonts are unusably gross on OS X (and Linux) w/o smoothing and +# the Oracle JVM, and many longtime users have anti-aliasing turned off. +editor.smooth = true + +# blink the caret by default +editor.caret.blink = true +# change to true to use a block (instead of a bar) +editor.caret.block = false + +# enable ctrl-ins, shift-ins, shift-delete for cut/copy/paste +# on windows and linux, but disable on the mac +editor.keys.alternative_cut_copy_paste = true +editor.keys.alternative_cut_copy_paste.macos = false + +# true if shift-backspace sends the delete character, +# false if shift-backspace just means backspace +editor.keys.shift_backspace_is_delete = false + +# home and end keys should only travel to the start/end of the current line +editor.keys.home_and_end_travel_far = false +# home and end keys move to the first/last non-whitespace character, +# and move to the actual start/end when pressed a second time. +# Only works if editor.keys.home_and_end_travel_far is false. +editor.keys.home_and_end_travel_smart = true +# The OS X HI Guidelines say that home/end are relative to the document, +# but that drives some people nuts. This pref enables/disables it. +editor.keys.home_and_end_travel_far.macos = true + +# Enable/disable support for complex scripts. Used for Japanese and others, +# but disable when not needed, otherwise basic Western European chars break. +editor.input_method_support = false + +# convert tabs to spaces? how many spaces? +editor.tabs.expand = true +editor.tabs.size = 2 + +# Set to true to automatically close [ { ( " and ' +editor.completion.auto_close = false + +# automatically indent each line +editor.indent = true + +# Whether to check files to see if they've been modified externally +editor.watcher = true +# Set true to enable debugging, since this is quirky on others' machines +editor.watcher.debug = false +# The window of time (in milliseconds) in which a change won't be counted +editor.watcher.window = 1500 + +# Format and search engine to use for online queries +search.format = https://google.com/search?q=%s + +# font choice and size for the console +console.font.size = 12 + +# number of lines to show by default +console.lines = 4 + +# Number of blank lines to advance/clear console. +# Note that those lines are also printed in the terminal when +# Processing is executed there. +# Setting to 0 stops this behavior. +console.head_padding = 10 + +# Set to false to disable automatically clearing the console +# each time 'run' is hit +# If one sets it to false, one may also want to set 'console.head_padding' +# to a positive number to separate outputs from different runs. +console.auto_clear = true + +# number of days of history to keep around before cleaning +# setting to 0 will never clean files +console.temp.days = 7 + +# set the maximum number of lines remembered by the console +# the default is 500, lengthen at your own peril +console.scrollback.lines = 500 +console.scrollback.chars = 40000 + +# Any additional Java options when running. +# If you change this and can't run things, it's your own durn fault. +run.options = + +# settings for the -XmsNNNm and -XmxNNNm command line option +run.options.memory = false +run.options.memory.initial = 64 +run.options.memory.maximum = 512 + +# Index of the display to use for running sketches (starts at 1). +# Kept this 1-indexed because older vesions of Processing were setting +# the preference even before it was being used. +# -1 means the default display, 0 means all displays +run.display = -1 + +# set internally because it comes from the system +#run.window.bgcolor= + +# set to false to open a new untitled window when closing the last window +# (otherwise, the environment will quit) +# default to the relative norm for the different platforms, +# but the setting can be changed in the prefs dialog anyway +#sketchbook.closing_last_window_quits = true +#sketchbook.closing_last_window_quits.macos = false + +editor.untitled.prefix=sketch_ +# The old (pre-1.0, back for 2.0) style for default sketch name. +# If you change this, be careful that this will work with your language +# settings. For instance, MMMdd won't work on Korean-language systems +# because it'll insert non-ASCII characters and break the environment. +# https://github.com/processing/processing/issues/322 +editor.untitled.suffix=yyMMdd + +# replace underscores in .pde file names with spaces +sketch.name.replace_underscore = true + +# what to use for generating sketch names (change in the prefs window) +#sketch.name.approach = + +# number of days of build history and other temp files to keep around +# these are kept around for debugging purposes, and in case code is lost +temp.days = 7 + + +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + +# whether or not to export as full screen (present) mode +export.application.fullscreen = false + +# whether to show the stop button when exporting to application +export.application.stop = true + +# embed Java by default for lower likelihood of problems +export.application.embed_java = true + +# set to false to no longer delete application folders before export +# (removed from the Preferences windows in 4.0 beta 9) +export.delete_target_folder = true + +# may be useful when attempting to debug the preprocessor +preproc.save_build_files=false + +# allows various preprocessor features to be toggled +# in case they are causing problems + +# preprocessor: pde.g +preproc.color_datatype = true +preproc.web_colors = true +preproc.enhanced_casting = true + +# preprocessor: PdeEmitter.java +preproc.substitute_floats = true + +# PdePreproc.java +# writes out the parse tree as parseTree.xml, which can be usefully +# viewed in (at least) Mozilla or IE. useful when debugging the preprocessor. +preproc.output_parse_tree = false + +# set to the program to be used for opening HTML files, folders, etc. +#launcher.linux = xdg-open + +# FULL SCREEN (PRESENT MODE) +run.present.bgcolor = #666666 +run.present.stop.color = #cccccc + +# PROXIES +# Set a proxy server for folks that require it. This will allow the update +# checker and the contrib manager to run properly in those environments. +# This changed from proxy.host and proxy.port to proxy.http.host and +# proxy.http.port in 3.0a8. In addition, https and socks were added. +proxy.http.host= +proxy.http.port= +proxy.https.host= +proxy.https.port= +proxy.socks.host= +proxy.socks.port= +# Example of usage (replace 'http' with 'https' or 'socks' as needed) +#proxy.http.host=proxy.example.com +#proxy.http.port=8080 +# Whether to use the system proxy by default +proxy.system=true + +# PDE X +pdex.errorCheckEnabled = true +pdex.warningsEnabled = true +pdex.writeErrorLogs = false + +pdex.autoSave.autoSaveEnabled = false +pdex.autoSaveInterval = 5 +pdex.autoSave.promptDisplay = true +pdex.autoSave.autoSaveByDefault = true + +# Enable auto-completion when hitting ctrl-space +pdex.completion = false +# Setting this true will show completions whenever available, not just after ctrl-space +pdex.completion.trigger = false +# Suggest libraries to import when a class is undefined/unavailable +pdex.suggest.imports = true +# Set to false to disable ctrl/cmd-click jump to definition +pdex.inspectMode.hotkey = true diff --git a/java/build.gradle.kts b/java/build.gradle.kts index 0f8e052780..d2b74abc4f 100644 --- a/java/build.gradle.kts +++ b/java/build.gradle.kts @@ -24,6 +24,7 @@ sourceSets{ dependencies{ implementation(project(":app")) + implementation(project(":app:utils")) implementation(project(":core")) implementation(project(":java:preprocessor")) diff --git a/java/src/processing/mode/java/Commander.java b/java/src/processing/mode/java/Commander.java index 2a2ee9bc0c..d74e3a3041 100644 --- a/java/src/processing/mode/java/Commander.java +++ b/java/src/processing/mode/java/Commander.java @@ -30,17 +30,16 @@ import processing.app.Base; import processing.app.Platform; -import processing.app.Preferences; import processing.app.RunnerListener; import processing.app.Sketch; import processing.mode.java.preproc.SketchException; -import processing.app.Util; +import processing.utils.Util; import processing.app.contrib.ModeContribution; import processing.core.PApplet; import processing.data.StringDict; import processing.mode.java.runner.Runner; - +import processing.utils.Preferences; /** * Class to handle running Processing from the command line. */ diff --git a/java/src/processing/mode/java/Compiler.java b/java/src/processing/mode/java/Compiler.java index 3e97931699..a94e0982c6 100644 --- a/java/src/processing/mode/java/Compiler.java +++ b/java/src/processing/mode/java/Compiler.java @@ -23,10 +23,10 @@ package processing.mode.java; -import processing.app.*; import processing.app.ui.Editor; import processing.core.*; import processing.mode.java.preproc.SketchException; +import processing.utils.Util; import java.io.*; import java.lang.reflect.Method; diff --git a/java/src/processing/mode/java/CompletionPanel.java b/java/src/processing/mode/java/CompletionPanel.java index 2ba1eb29ef..e0780776d6 100644 --- a/java/src/processing/mode/java/CompletionPanel.java +++ b/java/src/processing/mode/java/CompletionPanel.java @@ -41,7 +41,6 @@ import javax.swing.ListSelectionModel; import javax.swing.border.EmptyBorder; -import processing.app.Base; import processing.app.Messages; import processing.app.Mode; import processing.app.syntax.JEditTextArea; diff --git a/java/src/processing/mode/java/JavaBuild.java b/java/src/processing/mode/java/JavaBuild.java index aed0bc1327..28601325eb 100644 --- a/java/src/processing/mode/java/JavaBuild.java +++ b/java/src/processing/mode/java/JavaBuild.java @@ -47,6 +47,7 @@ import processing.mode.java.preproc.PdePreprocessor; import processing.mode.java.preproc.PreprocessorResult; import processing.mode.java.preproc.SketchException; +import processing.utils.Util; public class JavaBuild { diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java index 815792955d..a53c6fd3cc 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -29,7 +29,6 @@ import java.net.HttpURLConnection; import java.net.URL; import java.nio.file.Files; -import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -63,6 +62,9 @@ import processing.mode.java.tweak.SketchParser; import processing.mode.java.tweak.TweakClient; +import processing.utils.Util; +import processing.utils.Preferences; + public class JavaEditor extends Editor { JavaMode jmode; diff --git a/java/src/processing/mode/java/JavaMode.java b/java/src/processing/mode/java/JavaMode.java index ca448368eb..5f3dce839b 100644 --- a/java/src/processing/mode/java/JavaMode.java +++ b/java/src/processing/mode/java/JavaMode.java @@ -37,6 +37,8 @@ import processing.mode.java.runner.Runner; import processing.mode.java.tweak.SketchParser; +import processing.utils.Preferences; + public class JavaMode extends Mode { diff --git a/java/src/processing/mode/java/PreprocService.java b/java/src/processing/mode/java/PreprocService.java index 4f91505613..1e3fde1789 100644 --- a/java/src/processing/mode/java/PreprocService.java +++ b/java/src/processing/mode/java/PreprocService.java @@ -56,7 +56,7 @@ import processing.app.Messages; import processing.app.Sketch; import processing.app.SketchCode; -import processing.app.Util; +import processing.utils.Util; import processing.mode.java.preproc.*; import processing.mode.java.preproc.TextTransform.OffsetMapper; import processing.data.IntList; diff --git a/java/src/processing/mode/java/RuntimePathBuilder.java b/java/src/processing/mode/java/RuntimePathBuilder.java index f60471e0af..715fa0a79e 100644 --- a/java/src/processing/mode/java/RuntimePathBuilder.java +++ b/java/src/processing/mode/java/RuntimePathBuilder.java @@ -38,6 +38,7 @@ import processing.app.*; import processing.mode.java.preproc.ImportStatement; +import processing.utils.Util; /** diff --git a/java/src/processing/mode/java/lsp/PdeAdapter.java b/java/src/processing/mode/java/lsp/PdeAdapter.java index f4f1f450d2..6b0fc2bd53 100644 --- a/java/src/processing/mode/java/lsp/PdeAdapter.java +++ b/java/src/processing/mode/java/lsp/PdeAdapter.java @@ -32,7 +32,6 @@ import processing.app.Base; import processing.app.contrib.ModeContribution; import processing.app.Platform; -import processing.app.Preferences; import processing.app.Problem; import processing.app.Sketch; import processing.app.SketchCode; @@ -45,6 +44,8 @@ import processing.mode.java.PreprocService; import processing.mode.java.PreprocSketch; +import processing.utils.Preferences; + import static java.util.Arrays.copyOfRange; class PdeAdapter { diff --git a/java/src/processing/mode/java/runner/Runner.java b/java/src/processing/mode/java/runner/Runner.java index b4dc517707..942a5f10a0 100644 --- a/java/src/processing/mode/java/runner/Runner.java +++ b/java/src/processing/mode/java/runner/Runner.java @@ -45,6 +45,8 @@ import com.sun.jdi.request.*; import processing.mode.java.preproc.SketchException; +import processing.utils.Preferences; + /** * Runs a compiled sketch. As of release 0136, all sketches are run externally diff --git a/settings.gradle.kts b/settings.gradle.kts index 4bdcd880e8..7eacb06877 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,6 +3,7 @@ include( "core", "core:examples", "app", + "app:utils", "java", "java:preprocessor", "java:libraries:dxf",