2323
2424import processing .app .ui .Toolkit ;
2525import processing .core .PApplet ;
26- import processing .core .PConstants ;
2726
28- import java .awt .*;
29- import java .io .*;
30- import java .util .HashMap ;
31- import java .util .Map ;
27+ import java .awt .Color ;
28+ import java .awt .Font ;
29+ import java .awt .SystemColor ;
30+ import java .io .IOException ;
31+ import java .io .InputStream ;
3232
3333
3434/**
35- * Storage class for user preferences and environment settings.
36- * <P>
37- * This class does not use the Properties class because .properties files use
38- * ISO 8859-1 encoding, which is highly likely to be a problem when trying to
39- * save sketch folders and locations. Like the rest of Processing, we use UTF8.
40- * <p>
41- * We don't use the Java Preferences API because it would entail writing to
42- * the registry (on Windows), or an obscure file location (on Mac OS X) and
43- * make it far more difficult (impossible) to remove the preferences.txt to
44- * reset them (when they become corrupt), or to find the the file to make
45- * edits for numerous obscure preferences that are not part of the preferences
46- * window. If we added a generic editor (e.g. about:config in Mozilla) for
47- * such things, we could start using the Java Preferences API. But wow, that
48- * sounds like a lot of work. Not unlike writing this paragraph.
35+ * The app-facing side of the preferences. The actual storage (loading and
36+ * saving preferences.txt, the bundled defaults, platform-specific keys)
37+ * lives in the standalone {@link processing.utils.Preferences}; this class
38+ * delegates to it and keeps the pieces that only make sense inside the
39+ * full application: fonts and colors, error dialogs, and environment
40+ * side effects like proxies and the native file chooser.
4941 */
5042public class Preferences {
51- // had to rename the defaults file because people were editing it
52- static final String DEFAULTS_FILE = "defaults.txt" ; //$NON-NLS-1$
53- static final String PREFS_FILE = "preferences.txt" ; //$NON-NLS-1$
5443
55- static Map <String , String > defaults ;
56- static Map <String , String > table = new HashMap <>();
57- static File preferencesFile ;
58- private static boolean initialized = false ;
59-
60-
61- // /** @return true if the sketchbook file did not exist */
62- // static public boolean init() {
6344 static public void init () {
64- initialized = true ;
6545 // start by loading the defaults, in case something
6646 // important was deleted from the user prefs
6747 try {
68- var defaultsStream = Preferences
69- .class
70- .getClassLoader ()
71- .getResourceAsStream (DEFAULTS_FILE );
72- load (defaultsStream );
73- } catch (Exception e ) {
48+ processing .utils .Preferences .loadDefaults ();
49+ } catch (IOException e ) {
7450 Messages .showError (null , "Could not read default settings.\n " +
7551 "You'll need to reinstall Processing." , e );
7652 }
7753
78- // Clone the defaults, then override any them with the user's preferences.
79- // This ensures that any new/added preference will be present.
80- defaults = new HashMap <>(table );
81-
8254 // other things that have to be set explicitly for the defaults
8355 setColor ("run.window.bgcolor" , SystemColor .control ); //$NON-NLS-1$
8456
@@ -87,32 +59,15 @@ static public void init() {
8759 setBoolean ("editor.input_method_support" , true );
8860 }
8961
90-
91- // next load user preferences file
92- preferencesFile = Base .getSettingsFile (PREFS_FILE );
93- var preferencesFileOverride = System .getProperty ("processing.app.preferences.file" );
94- if (preferencesFileOverride != null && !preferencesFileOverride .isEmpty ()) {
95- preferencesFile = new File (preferencesFileOverride );
96- }
97- boolean firstRun = !preferencesFile .exists ();
98- if (!firstRun ) {
99- try {
100- load (new FileInputStream (preferencesFile ));
101-
102- } catch (Exception ex ) {
103- Messages .showError ("Error reading preferences" ,
104- "Error reading the preferences file. " +
105- "Please delete (or move)\n " +
106- preferencesFile .getAbsolutePath () +
107- " and restart Processing." , ex );
108- }
109- }
110-
111- if (checkSketchbookPref () || firstRun ) {
112- // if (firstRun) {
113- // create a new preferences file if none exists
114- // saves the defaults out to the file
115- save ();
62+ // next load user preferences file over the defaults
63+ try {
64+ processing .utils .Preferences .loadUserPrefs (Base .getSettingsFolder ());
65+ } catch (IOException ex ) {
66+ Messages .showError ("Error reading preferences" ,
67+ "Error reading the preferences file. " +
68+ "Please delete (or move)\n " +
69+ processing .utils .Preferences .getPreferencesPath () +
70+ " and restart Processing." , ex );
11671 }
11772
11873 PApplet .useNativeSelect =
@@ -140,15 +95,15 @@ static public void init() {
14095 * For testing, pretend to load preferences without a real file.
14196 */
14297 static public void skipInit () {
143- initialized = true ;
98+ processing . utils . Preferences . skipInit () ;
14499 }
145100
146101 /**
147102 * Check whether Preferences.init() has been called. If not, we are probably not running the full application.
148103 * @return true if Preferences has been initialized
149104 */
150105 static public boolean isInitialized () {
151- return initialized ;
106+ return processing . utils . Preferences . isInitialized () ;
152107 }
153108
154109
@@ -165,120 +120,24 @@ static void handleProxy(String protocol, String hostProp, String portProp) {
165120
166121
167122 static public String getPreferencesPath () {
168- return preferencesFile . getAbsolutePath ();
123+ return processing . utils . Preferences . getPreferencesPath ();
169124 }
170125
171126
172127 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
173128
174129
175- /**
176- * Load a set of key/value pairs from a UTF-8 encoded file into 'table'.
177- * For 3.0a6, this removes any platform-specific extensions from keys, so
178- * that we don't have platform-specific entries in a user's preferences.txt
179- * file, which would require all prefs to be changed twice, or risk being
180- * overwritten by the unchanged platform-specific version on reload.
181- */
182130 static public void load (InputStream input ) throws IOException {
183- HashMap <String , String > platformSpecific = new HashMap <>();
184-
185- String [] lines = PApplet .loadStrings (input ); // Reads as UTF-8
186- for (String line : lines ) {
187- if ((line .isEmpty ()) ||
188- (line .charAt (0 ) == '#' )) continue ;
189-
190- line = line .replace ("\\ " , "/" ); // normalize slashes in paths
191-
192- // this won't properly handle = signs being in the text
193- int equals = line .indexOf ('=' );
194- if (equals != -1 ) {
195- String key = line .substring (0 , equals ).trim ();
196- String value = line .substring (equals + 1 ).trim ();
197- if (!isPlatformSpecific (key , value , platformSpecific )) {
198- table .put (key , value );
199- }
200- }
201- }
202- // Now override the keys with any platform-specific defaults we've found.
203- for (String key : platformSpecific .keySet ()) {
204- table .put (key , platformSpecific .get (key ));
205- }
131+ processing .utils .Preferences .load (input );
206132 }
207133
208134
209- /**
210- * @param key original key (may include platform extension)
211- * @param value the value that goes with the key
212- * @param specific where to put the key/value pairs for *this* platform
213- * @return true if a platform-specific key
214- */
215- static protected boolean isPlatformSpecific (String key , String value ,
216- Map <String , String > specific ) {
217- for (String platform : PConstants .platformNames ) {
218- String ext = "." + platform ;
219- if (key .endsWith (ext )) {
220- String thisPlatform = PConstants .platformNames [PApplet .platform ];
221- if (platform .equals (thisPlatform )) {
222- key = key .substring (0 , key .lastIndexOf (ext ));
223- // store this for later overrides
224- specific .put (key , value );
225- //} else {
226- // ignore platform-specific defaults for other platforms,
227- // but return 'true' because it needn't be added to the big list
228- }
229- return true ;
230- }
231- }
232- return false ;
233- }
234-
235-
236- // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
237-
238-
239135 static public void save () {
240- // On startup, this is null, but ignore it. It's trying to update the
241- // prefs for the open sketch before Preferences.init() has been called.
242- if (preferencesFile != null ) {
243- try {
244- File dir = preferencesFile .getParentFile ();
245- File preferencesTemp = File .createTempFile ("preferences" , ".txt" , dir );
246- if (!preferencesTemp .setWritable (true , false )) {
247- throw new IOException ("Could not set " + preferencesTemp + " writable" );
248- }
249-
250- // Fix for 0163 to properly use Unicode when writing preferences.txt
251- PrintWriter writer = PApplet .createWriter (preferencesTemp );
252-
253- String [] keyList = table .keySet ().toArray (new String [0 ]);
254- // Sorting is really helpful for debugging, diffing, and finding keys
255- keyList = PApplet .sort (keyList );
256- for (String key : keyList ) {
257- writer .println (key + "=" + table .get (key )); //$NON-NLS-1$
258- }
259- writer .flush ();
260- writer .close ();
261-
262- // Rename preferences.txt to preferences.old
263- File oldPreferences = new File (dir , "preferences.old" );
264- if (oldPreferences .exists ()) {
265- if (!oldPreferences .delete ()) {
266- throw new IOException ("Could not delete preferences.old" );
267- }
268- }
269- if (preferencesFile .exists () &&
270- !preferencesFile .renameTo (oldPreferences )) {
271- throw new IOException ("Could not replace preferences.old" );
272- }
273- // Make the temporary file into the real preferences
274- if (!preferencesTemp .renameTo (preferencesFile )) {
275- throw new IOException ("Could not move preferences file into place" );
276- }
277-
278- } catch (IOException e ) {
279- Messages .showWarning ("Preferences" ,
280- "Could not save the Preferences file." , e );
281- }
136+ try {
137+ processing .utils .Preferences .save ();
138+ } catch (IOException e ) {
139+ Messages .showWarning ("Preferences" ,
140+ "Could not save the Preferences file." , e );
282141 }
283142 }
284143
@@ -289,44 +148,31 @@ static public void save() {
289148 // all the information from preferences.txt
290149
291150 static public String get (String attribute /*, String defaultValue */ ) {
292- if (!initialized ) {
151+ if (!isInitialized () ) {
293152 init ();
294153 }
295- return table .get (attribute );
154+ return processing . utils . Preferences .get (attribute );
296155 }
297156
298157
299158 static public String getDefault (String attribute ) {
300- return defaults . get (attribute );
159+ return processing . utils . Preferences . getDefault (attribute );
301160 }
302161
303162
304163 static public void set (String attribute , String value ) {
305- table . put (attribute , value );
164+ processing . utils . Preferences . set (attribute , value );
306165 }
307166
308167
309168 static public void unset (String attribute ) {
310- table . remove (attribute );
169+ processing . utils . Preferences . unset (attribute );
311170 }
312171
313172
314173 static public boolean getBoolean (String attribute ) {
315174 String value = get (attribute ); //, null);
316175 return Boolean .parseBoolean (value );
317-
318- /*
319- supposedly not needed, because anything besides 'true'
320- (ignoring case) will just be false.. so if malformed -> false
321- if (value == null) return defaultValue;
322-
323- try {
324- return (new Boolean(value)).booleanValue();
325- } catch (NumberFormatException e) {
326- System.err.println("expecting an integer: " + attribute + " = " + value);
327- }
328- return defaultValue;
329- */
330176 }
331177
332178
@@ -385,26 +231,6 @@ static public Font getFont(String familyAttr, String sizeAttr, int style) {
385231 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
386232
387233
388- /**
389- * Check for a 4.0 sketchbook location, and if none exists,
390- * try to grab it from the 3.0 sketchbook location.
391- * @return true if a location was found and the pref didn't exist
392- */
393- static protected boolean checkSketchbookPref () {
394- // If a 4.0 sketchbook location has never been inited
395- if (getSketchbookPath () == null ) {
396- String threePath = get ("sketchbook.path.three" ); //$NON-NLS-1$
397- // If they've run the 3.0 version, start with that location
398- if (threePath != null ) {
399- setSketchbookPath (threePath );
400- return true ; // save the sketchbook right away
401- }
402- // Otherwise it'll be null, and reset properly by Base
403- }
404- return false ;
405- }
406-
407-
408234 static public String getOldSketchbookPath () {
409235 return get ("sketchbook.path.three" ); //$NON-NLS-1$
410236 }
0 commit comments