package bluej;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.time.LocalDate;
import java.util.*;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.ObservableList;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.DialogPane;
import javafx.scene.control.Label;
import javafx.scene.control.SplitPane;
import javafx.scene.input.KeyCharacterCombination;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Polygon;
import javafx.stage.Screen;
import javafx.stage.Window;

import javax.swing.ImageIcon;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;

import bluej.stride.generic.InteractionManager;
import bluej.utility.javafx.JavaFXUtil;
import javafx.stage.WindowEvent;
import threadchecker.OnThread;
import threadchecker.Tag;
import bluej.utility.Debug;
import bluej.utility.Utility;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.lang.reflect.Field;


| Class to handle application configuration for BlueJ. | The configuration information is spread over several files: <BR> | <BR> | <bluej_home>/lib/bluej.defs <BR> | <user_home>/.bluej/bluej.properties <BR> | command line arguments in form -D<prop>=<val> <BR> | <BR> | bluej.defs - contains system definitions which are not user specific<BR> | bluej.properties - contains user specific settings. | Settings here override settings in bluej.defs <BR> | command line arguments - contains per-launch specific settings. | Settings here override settings in bluej.properties <BR> | <BR> | There is also a set of language specific labels | in a directory named after the language | <bluej_home>/lib/<language>/labels | | @author Michael Cahill | @author Michael Kolling | @author Andrew Patterson | public final class Config { public static final String nl = System.getProperty("line.separator"); public static final String osname = System.getProperty("os.name", ""); public static final String DEFAULT_LANGUAGE = "english"; public static final String BLUEJ_OPENPACKAGE = "bluej.openPackage"; public static final String bluejDebugLogName = "bluej-debuglog.txt"; public static final String greenfootDebugLogName = "greenfoot-debuglog.txt"; public static final KeyCodeCombination GREENFOOT_SET_PLAYER_NAME_SHORTCUT = new KeyCodeCombination(KeyCode.P, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN);
| name of the icons file for the VM on Mac | private static final String BLUEJ_DEBUG_DOCK_ICON = "vm.icns"; private static final String GREENFOOT_DEBUG_DOCK_ICON = "greenfootvm.icns";
| name of the VM in the dock on Mac | private static final String BLUEJ_DEBUG_DOCK_NAME = "BlueJ Virtual Machine"; private static final String GREENFOOT_DEBUG_DOCK_NAME = "Greenfoot"; public static Properties moeSystemProps; public static Properties moeUserProps; private static final Map<String, BooleanProperty> booleanProperties = new HashMap<>(); public static String language; public static Rectangle screenBounds; public static String debugLogName = bluejDebugLogName; public static List<String> fontOptions = new ArrayList<>(); private static Border focusBorder; private static Border normalBorder; private static Properties systemProps; private static Properties userProps; private static Properties greenfootProps; private static Properties commandProps; private static Properties initialCommandLineProps; private static Properties langProps; private static Properties langVarProps; private static File bluejLibDir; private static File userPrefDir; private static File templateDir;
| The greenfoot subdirectory of the "lib"-directory*/ |private static File greenfootLibDir; private static boolean initialised = false; private static boolean isGreenfoot = false; private static List<String> debugVMArgs = new ArrayList<>(); | |/** whether this is the debug vm or not. private static boolean isDebugVm = true; public static final String EDITOR_COUNT_JAVA = "session.numeditors.java"; public static final String EDITOR_COUNT_STRIDE = "session.numeditors.stride"; public static final String MESSAGE_LATEST_SEEN = "bluej.latest.msg"; private static long MAX_DEBUG_LOG_SIZE = 1048576;
| Initialisation of BlueJ configuration. Must be called at startup. | This method finds and opens the configuration files. | | This is only called from the main/server VM | | See also initializeVMside(). | public static void initialise(File bluejLibDir, Properties tempCommandLineProps, boolean bootingGreenfoot) { initialise(bluejLibDir, tempCommandLineProps, bootingGreenfoot, true); initDebugVMArgs(); }
| Initialisation of BlueJ configuration. Must be called at startup. | This method finds and opens the configuration files.<p> | | See also initializeVMside(). | private static void initialise(File bluejLibDir, Properties tempCommandLineProps, boolean bootingGreenfoot, boolean createUserhome) { if (initialised) return; initialised = true; initialCommandLineProps = tempCommandLineProps; isGreenfoot = bootingGreenfoot; screenBounds = calculateScreenBounds(); Config.bluejLibDir = bluejLibDir; Config.greenfootLibDir = new File(bluejLibDir, "greenfoot"); if (systemProps == null) { isDebugVm = false; systemProps = loadDefs("bluej.defs", System.getProperties()); if (isGreenfoot()) { greenfootProps = loadDefs("greenfoot.defs", systemProps); userProps = new Properties(greenfootProps); } else { userProps = new Properties(systemProps); } } commandProps = new Properties(userProps); commandProps.putAll(tempCommandLineProps); commandProps.setProperty("bluej.libdir", bluejLibDir.getAbsolutePath()); if (createUserhome) { initUserHome(); loadProperties(getApplicationName().toLowerCase(), userProps); if (isGreenfoot) { debugLogName = greenfootDebugLogName; } checkDebug(userPrefDir); } initLanguage(); moeSystemProps = loadDefs("moe.defs", System.getProperties()); moeUserProps = new Properties(moeSystemProps); loadProperties("moe", moeUserProps); String macOSscreenMenuBar = Config.getPropString("bluej.macos.screenmenubar", "true"); System.setProperty("apple.laf.useScreenMenuBar", macOSscreenMenuBar); Config.setVMLocale(); commandProps.setProperty("bluej.version", Boot.BLUEJ_VERSION); }
| Determine the configured language, or detect the language from the locale. | Fall back to the DEFAULT_LANGUAGE if language cannot be determined. | Load language-specific labels for the determined language. | private static void initLanguage() { language = commandProps.getProperty("bluej.language", null); if (language == null) { language = DEFAULT_LANGUAGE; try { String iso3lang = Locale.getDefault().getISO3Language(); for (int i = 1; ; i++) { String langString = Config.getPropString("bluej.language" + i, null); if (langString == null) { break; } int colonIndex = langString.indexOf(':'); if (colonIndex == -1) { continue; } int secondColon = langString.indexOf(':', colonIndex + 1); if (secondColon == -1) { continue; } if (langString.substring(secondColon + 1).equals(iso3lang)) { language = langString.substring(0, colonIndex); break; } } Debug.log("Detected language \"" + language + "\" based on iso639-2 code \"" + iso3lang + "\""); } catch (MissingResourceException mre) { Debug.log("Using default language \"" + language + "\""); } } langProps = loadLanguageLabels(language); }
| Initialise the user home (try and create directories if necessary). | | We try the bluej.userHome property, or default to the system user.home | property if that is not set. If the result is not writable we try | bluej.userHome1, bluej.userHome2, etc. | private static void initUserHome() { File userHome; String homeDir = getPropString("bluej.userHome", "$user.home"); userHome = new File(homeDir); String prefDirName = getBlueJPrefDirName(); userPrefDir = new File(userHome, prefDirName); int nameCounter = 1; do { if (! userPrefDir.isDirectory()) { if (userPrefDir.mkdirs()) { break; } } else if (userPrefDir.canWrite()) { break; } nameCounter++; String propertyName = "bluej.userHome" + nameCounter; homeDir = getPropString(propertyName, null); if (homeDir == null) { break; } userHome = new File(homeDir); userPrefDir = new File(userHome, prefDirName); } while (true){; if (homeDir == null) { homeDir = System.getProperty("user.home"); } userHome = new File(homeDir); userPrefDir = new File(userHome, prefDirName); } }
| Alternative to "initialise", to be used in the debugee-VM by |* applications which require it (ie. greenfoot). */ public static void initializeVMside(File bluejLibDir, File userConfigDir, BlueJPropStringSource propSource) | |{ | |isDebugVm = true; | |Config.userPrefDir = userConfigDir; | |// Set up the properties so that they use the properties from the | |// BlueJVM | |systemProps = new Properties() { | |@Override | |public String getProperty(String key) | |{ | |return propSource.getBlueJPropertyString(key, null); | |} | |@Override | |public String getProperty(String key, String def) | |{ | |return propSource.getBlueJPropertyString(key, def); | |} | |}; | |userProps = new Properties(systemProps) { | |@Override | |public synchronized Object setProperty(String key, String val) | |{ | |// Debug VM should not be setting properties: | |Debug.printCallStack("Internal error: setting user property on debug VM"); return null; } @Override public String getProperty(String key) { return propSource.getBlueJPropertyString(key, null); | |} | |@Override | |public String getProperty(String key, String def) | |{ | |return propSource.getBlueJPropertyString(key, def); | |} | |}; | |initialise(bluejLibDir, new Properties(), true, false); | |} | |/** | Get the properties that were given on the command line and used | to initialise bluej.Config. | public static Properties getInitialCommandLineProperties() { return initialCommandLineProps; }
| Initializer for use in Greenfoot's standalone scenario viewer if you | export a scenario as an app or applet. | public static void initializeStandalone(BlueJPropStringSource propSource) { if (initialised) return; initialised = true; Config.isGreenfoot = true; langProps = new Properties() { @Override public String getProperty(String key) { return propSource.getLabel(key); } @Override public String getProperty(String key, String def) { return propSource.getLabel(key); } }; commandProps = langProps; } public static boolean isInitialised() { return initialised; }
| Get the name of icons file for the debug VM (Mac). | public static String getVMIconsName() { if (isGreenfoot()) { return GREENFOOT_DEBUG_DOCK_ICON; } return BLUEJ_DEBUG_DOCK_ICON; }
| Get the name of the debug VM to appear in the dock (Mac). | public static String getVMDockName() { if (isGreenfoot()) { return GREENFOOT_DEBUG_DOCK_NAME; } return BLUEJ_DEBUG_DOCK_NAME; }
| True if this is the debugVM or false if not. | public static boolean isDebugVM() { return isDebugVm; }
| Tell us whether we are running on MacOS | public static boolean isMacOS() { return osname.startsWith("Mac"); } private static boolean osVersionNumberAtLeast(int... target) { return versionAtLeast(System.getProperty("os.version"), target); } private static boolean javaVersionNumberAtLeast(int... target) { return versionAtLeast(System.getProperty("java.specification.version"), target); } private static boolean versionAtLeast(String version, int[] target) { String[] versionChunks = version.split("\\."); for (int i = 0; i < target.length; i++) { if (versionChunks.length <= i) return false; if (target[i] < Integer.parseInt(versionChunks[i])) return true; if (target[i] > Integer.parseInt(versionChunks[i])) return false; } return true; }
| Tell us whether we are running on MacOS 10.5 (Leopard) or later | public static boolean isMacOSLeopard() { return osname.startsWith("Mac") && osVersionNumberAtLeast(10, 5); }
| Tell use whether we are running on MacOS 10.6 (Snow Leopard) or later | public static boolean isMacOSSnowLeopard() { return osname.startsWith("Mac") && osVersionNumberAtLeast(10, 6); }
| Tell us whether we are running on MS Windows | public static boolean isWinOS() { return osname.startsWith("Windows"); }
| True if OS is Windows Vista or newer. | public static boolean isModernWinOS() { return isWinOS() && osVersionNumberAtLeast(6, 0); }
| Tell us whether we are running on Linux | public static boolean isLinux() { return osname.startsWith("Linux"); }
| Tell us whether we are running on Solaris | public static boolean isSolaris() { return osname.startsWith("Solaris"); }
| Tell us whether we are running on a Java VM that supports Java 7 features. | public static boolean isJava17() { return javaVersionNumberAtLeast(1, 7); }
| Tell us whether we are running OpenJDK. | public static boolean isOpenJDK() { return System.getProperty("java.runtime.name").startsWith("OpenJDK"); }
| Whether we need to make all dialogs resizable, due to bug: | https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8198761 | public static boolean makeDialogsResizable() { return Config.isLinux(); }
| Return the name of a directory within the user's home directory | that should be used for storing BlueJ user preferences. | | @return The path of the preference directory relative to the user's home | private static String getBlueJPrefDirName() { String programName = "bluej"; if (isGreenfoot) { programName = "greenfoot"; } if (isMacOS()) { return "Library/Preferences/org." + programName; } else if (isWinOS()) { return programName; } else { return "." + programName; } }
| Get the name of this application. | public static String getApplicationName() { if (isGreenfoot) { return "Greenfoot"; } return "BlueJ"; }
| Get the screen size information | private static Rectangle calculateScreenBounds() { Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); return new Rectangle(d); }
| Check whether we want to see debug information. If not, redirect it to | a file. | private static void checkDebug(File userdir) { if (!isDebugVM()) { if (!"true".equals(commandProps.getProperty("bluej.debug"))) { File debugLogFile = new File(userdir, debugLogName); try { boolean append = debugLogFile.exists() && debugLogFile.length() < MAX_DEBUG_LOG_SIZE; PrintStream outStream = new PrintStream(new FileOutputStream(debugLogFile, append)); System.setOut(outStream); System.setErr(outStream); Debug.setDebugStream(new OutputStreamWriter(outStream)); if (append) { Debug.message("====\n\n===="); } Debug.message(getApplicationName() + " run started: " + new Date()); if (isGreenfoot()) { Debug.message("Greenfoot version: " + Boot.GREENFOOT_VERSION); } else { Debug.message("BlueJ version " + Boot.BLUEJ_VERSION); } Debug.message("Java version " + System.getProperty("java.version")); Debug.message("JavaFX version " + System.getProperty("javafx.runtime.version")); Debug.message("Virtual machine: " + System.getProperty("java.vm.name") + " " + System.getProperty("java.vm.version") + " (" + System.getProperty("java.vm.vendor") + ")"); Debug.message("Running on: " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " (" + System.getProperty("os.arch") + ")"); Debug.message("Java Home: " + System.getProperty("java.home")); Debug.message("----"); return; } catch (IOException e) { Debug.reportError("Warning: Unable to create debug log file."); } } } Debug.setDebugStream(new OutputStreamWriter(System.out)); }
| Called on system exit. Do whatever there is to do before exiting. | public static void handleExit() { saveAppProperties(); saveProperties("moe", "properties.heading.moe", moeUserProps); } private static void saveAppProperties() { final String name = getApplicationName().toLowerCase(); saveProperties(name, "properties.heading." + name, userProps); }
| Increases the count of editors opened by one for the given source type, | and saves the user properties file. | public static void recordEditorOpen(SourceType sourceType) { switch (sourceType) { case Java: { int javaEditors = getPropInteger(EDITOR_COUNT_JAVA, 0, userProps); javaEditors += 1; userProps.setProperty(EDITOR_COUNT_JAVA, Integer.toString(javaEditors)); saveAppProperties(); } break; case Stride: { int strideEditors = getPropInteger(EDITOR_COUNT_STRIDE, 0, userProps); strideEditors += 1; userProps.setProperty(EDITOR_COUNT_STRIDE, Integer.toString(strideEditors)); saveAppProperties(); } break; default: break; } }
| Gets the editors count as stored in the properties file. You should usually | use this to get the count from the previous session, then call resetEditorsCount. | | @return The number of editors that have been opened for that source type | since the last call to resetEditorsCount. Returns -1 if the property | is not found | public static int getEditorCount(SourceType sourceType) { switch (sourceType) { case Java: return getPropInteger(EDITOR_COUNT_JAVA, -1, userProps); case Stride: return getPropInteger(EDITOR_COUNT_STRIDE, -1, userProps); default: return -1; } }
| Resets the editor count (as processed by getEditorCount/recordEditorOpen) | in the properties file. Also saves the properties file. | public static void resetEditorsCount() { userProps.setProperty(EDITOR_COUNT_JAVA, "0"); userProps.setProperty(EDITOR_COUNT_STRIDE, "0"); saveAppProperties(); }
| Records the date of the latest seen message from the server and saves the bluej.properties file. | @param latestSeen The id (start date) of the latest message the user has seen. | public static void recordLatestSeen(LocalDate latestSeen) { userProps.setProperty(MESSAGE_LATEST_SEEN, latestSeen.toString()); saveAppProperties(); }
| Load a BlueJ definition file. This creates a new properties object. | The new properties object can be returned directly, or an empty | properties object can be returned that has the named definitions as | defaults. | | @param filename the properties file | private static Properties loadDefs(String filename, Properties parentProperties) { File propsFile = new File(bluejLibDir, filename); Properties defs = new Properties(parentProperties); try { defs.load(new FileInputStream(propsFile)); } catch(IOException e) { Debug.reportError("Unable to load definitions file: " + propsFile); } return defs; }
| Load the label property file for the currently defined language. | Install the default language (English) as the default properties | as a fallback. | private static Properties loadLanguageLabels(String language) { Properties labels = loadDefs(DEFAULT_LANGUAGE + File.separator + "labels", System.getProperties()); String frameLabels = DEFAULT_LANGUAGE + File.separator + "frame-labels"; File frameLabelFile = new File(bluejLibDir, frameLabels); try{ labels.load(new FileInputStream(frameLabelFile)); } catch(Exception e){ Debug.reportError("Unable to load greenfoot labels file: " + frameLabelFile); } if (isGreenfoot()) { String greenfootLabels = DEFAULT_LANGUAGE + File.separator + "greenfoot/greenfoot-labels"; File greenfootLabelFile = new File(bluejLibDir, greenfootLabels); try{ labels.load(new FileInputStream(greenfootLabelFile)); } catch(IOException e){ Debug.reportError("Unable to load greenfoot labels file: " + greenfootLabelFile); } } if (!DEFAULT_LANGUAGE.equals(language)) { String languageFileName = language + File.separator + "labels"; File languageFile = new File(bluejLibDir, languageFileName); try{ labels.load(new FileInputStream(languageFile)); } catch(Exception e){ Debug.reportError("Unable to load definitions file: " + languageFile); } String languageFrameLabels = language + File.separator + "frame-labels"; File languageFrameLabelFile = new File(bluejLibDir, languageFrameLabels); try{ labels.load(new FileInputStream(languageFrameLabelFile)); } catch(Exception e){ Debug.reportError("Unable to load frame labels file: " + languageFrameLabelFile); } if (isGreenfoot()) { File greenfootLabels = new File(bluejLibDir, language + File.separator + "greenfoot/greenfoot-labels"); try{ labels.load(new FileInputStream(greenfootLabels)); } catch(Exception e){ Debug.reportError("Unable to load greenfoot labels file: " + greenfootLabels); } } } return labels; }
| Load local BlueJ properties. The properties definitions override | the defaults found in the definitions file. | private static void loadProperties(String filename, Properties props) { File propsFile = new File(userPrefDir, filename + ".properties"); try { props.load(new FileInputStream(propsFile)); } catch (IOException e) { } catch (Exception e) { Debug.reportError("Exception while loading properties", e); } }
| Save user specific (local) BlueJ properties. | private static void saveProperties(String filename, String comment, Properties props) { File propsFile = new File(userPrefDir, filename + ".properties"); try { props.store(new FileOutputStream(propsFile), getString(comment)); } catch(IOException e) { Debug.reportError("could not save properties file " + propsFile); } }
| Find and return the moe help definitions | public static Properties getMoeHelp() { return loadDefs(language + File.separator + "moe.help", System.getProperties()); }
| Get a string list from the language dependent definitions file | (eg. "english/labels"). If you pass "mydialog.error" then we look |* for "mydialog.error1", then "mydialog.error2". Every consecutive * found String is added to the list until we can't find one, at which * point we stop. */ public static List<String> getStringList(String stem) { List<String> r = new ArrayList<>(); | |for (int i = 1; ;i++) | |{ | |String s = getString(stem + Integer.toString(i), null); | |if (s != null) | |r.add(s); | |else{ return r; | |} | |} | |} | |/** | Get a string from the language dependent definitions file | (eg. "english/labels"). |*/ public static String getString(String strname) { return getString(strname, strname); } /** * Get a string from the language dependent definitions file * (eg. "english/labels"). If not found, return default. */ public static String getString(String strname, String def) { return getString(strname, def, null); } /** * Get a string from the language dependent definitions file | (eg. "english/labels"), replacing local variables. |* If not found, return default. */ public static String getString(String strname, String def, Properties variables) { if (langVarProps == null) { langVarProps = new Properties(); | |langVarProps.put("APPNAME", getApplicationName()); } int index; // langProps can be null during testing: String str = langProps == null ? def : langProps.getProperty(strname, def); if (str != null) | |{ | |// remove all underscores | |while ((index = str.indexOf('_')) != -1) | |{ | |str = str.substring(0, index) + str.substring(index + 1); | |} | |if ((index = str.indexOf('@')) != -1) | |{ | |//remove everything from @ | |str = str.substring(0, index); | |} | |if (variables == null) { | |variables = langVarProps; | |} | |else { | |variables.putAll(langVarProps); | |} | |str = PropParser.parsePropString(str, variables); | |} | |return str; | |} | |/** | Get the mnemonic key for a particular label by looking for an underscore | and using the character right after the underscore as the mnemonic key. | | @param strname The label name | @return Mnemonic or KeyEvent.VK_UNDEFINED if none found. | public static int getMnemonicKey(String strname) { int mnemonic; String str = langProps.getProperty(strname, strname); int index = str.indexOf('_'); if (index == -1 || (index + 1) >= str.length()) { mnemonic = KeyEvent.VK_UNDEFINED; } else { mnemonic = str.codePointAt(index + 1); } return mnemonic; }
| Check whether a particular label has an accelerator key defined. | @param strname The label name to check | @return True if an accelerator key is defined | public static boolean hasAcceleratorKey(String strname) { return langProps.getProperty(strname, strname).indexOf('@') != -1; } @OnThread(Tag.FX) public static KeyCombination getAcceleratorKeyFX(String strname) { if (!hasAcceleratorKey(strname)) return null; int index; List<KeyCombination.Modifier> modifiers = new ArrayList<>(); modifiers.add(KeyCombination.SHORTCUT_DOWN); String str = langProps.getProperty(strname, strname); String keyString; index = str.indexOf('@'); index++; if (str.charAt(index) == '^') { index++; modifiers.add(KeyCombination.SHIFT_DOWN); } keyString = str.substring(index).toUpperCase(); if (keyString.length() == 1) { return new KeyCharacterCombination(keyString, modifiers.toArray(new KeyCombination.Modifier[0])); } if (keyString.equals("BACK_SPACE")) keyString = "Backspace"; KeyCode keyCode = KeyCode.getKeyCode(keyString); if (keyCode != null) return new KeyCodeCombination(keyCode, modifiers.toArray(new KeyCombination.Modifier[0])); else { Debug.message("Unknown key: \"" + keyString + "\""); return null; } }
| Get a system-dependent string from the BlueJ properties | System-dependent strings are properties that can | start with an OS ID prefix (though it will default to | finding just the plain property name in the case where the | system id'ed version does not exist). | Returns null if the property does not exist | public static String getSystemPropString(String propName) { String sysID; if (osname != null && osname.startsWith("Windows 9")) { sysID = "win9x"; } else if (osname != null && osname.equals("Windows Me")) { sysID = "win9x"; } else if (osname != null && osname.startsWith("Windows")) { sysID = "win"; } else if (osname != null && osname.startsWith("Linux")) { sysID = "linux"; } else if (osname != null && osname.startsWith("SunOS")) { sysID = "solaris"; } else if (osname != null && osname.startsWith("Mac")) { sysID = "macos"; } else { sysID = ""; } String value = commandProps.getProperty(sysID + propName); if (value == null) { value = commandProps.getProperty(propName); } return value; }
| Get a non-language-dependent string from the BlueJ properties | ("bluej.defs" or "bluej.properties"). If not defined, the property |* name is returned unchanged. */ public static String getPropString(String strname) { // Don't pass strname as the second parameter to getPropString, or | |// variable substitution will be performed. | |String rval = getPropString(strname, null); | |if (rval == null) { | |rval = strname; | |} | |return rval; | |} | |/** | Get a non-language-dependent string from the BlueJ properties | ("bluej.defs" or "bluej.properties") with a default value. Variable |* substitution ($varname) is performed on the value (and will be * performed on the default value if that is used). */ public static String getPropString(String strname, String def) | |{ | |return getPropString(strname, def, commandProps); | |} | |/** | Get a property string from the given properties map, using variable substitution. | If the variable is not defined the given default value is used (and variable | substitution is performed on it). | | @param strname The name of the property thats value is to be retrieved | @param def The default value to use if the value is not defined | @param props The properties to retrieve the value from | @return The property value after variable substitution | public static String getPropString(String strname, String def, Properties props) { String propVal = props.getProperty(strname, def); if (propVal == null) { propVal = def; } if (propVal != null) { return PropParser.parsePropString(propVal, props); } return null; }
| Get a non-language-dependent string from the BlueJ properties | "bluej.defs" with a default value. No variable substitution is |* performed. */ public static String getDefaultPropString(String strname, String def) { return systemProps.getProperty(strname, def); | |} | |/** | Get a non-language dependant integer from the BlueJ properties | ("bluej.defs" or "bluej.properties") with a default value |*/ public static int getPropInteger(String intname, int def) { int value; try { value = Integer.parseInt(getPropString(intname, String.valueOf(def))); | |} | |catch(NumberFormatException nfe) { | |return def; | |} | |return value; | |} | |private static int getPropInteger(String intname, int def, Properties props) | |{ | |int value; | |try { | |value = Integer.parseInt(getPropString(intname, String.valueOf(def), props)); | |} | |catch(NumberFormatException nfe) { | |return def; | |} | |return value; | |} | |/** | Gets a boolean value from the BlueJ properties as JavaFX observable | property. Any changes to the property will be saved to the BlueJ properties | automatically. | | Only one instance is created per property name for the lifetime of | the program. This is generally good as you can call this method twice | and updates will be reflected in both returned properties (because it | will be the same property object!). But be careful if you bind | the property as you may be unbinding an earlier binding. | @OnThread(Tag.FXPlatform) public static BooleanProperty getPropBooleanProperty(String propname) { return booleanProperties.computeIfAbsent(propname, p -> { boolean initial = getPropBoolean(propname); SimpleBooleanProperty prop = new SimpleBooleanProperty(initial); JavaFXUtil.addChangeListener(prop, b -> putPropBoolean(propname, b)); return prop; }); }
| Get a boolean value from the BlueJ properties. The default value is false. | public static boolean getPropBoolean(String propname) { return parseBoolean(getPropString(propname, null)); }
| Get a boolean value from the BlueJ properties, with the specified default. | public static boolean getPropBoolean(String propname, boolean def) { String propval = getPropString(propname); if (propval == null) { return def; } return parseBoolean(propval); }
| Parses the string argument as a boolean. The <code>boolean</code> | returned represents the value <code>true</code> if the string argument | is not <code>null</code> and is equal, ignoring case, to the string | <code>"true"</code>. |* * @param s the <code>String</code> containing the boolean * representation to be parsed * @return the boolean represented by the string argument */ private static boolean parseBoolean(String s) { | |return ((s != null) && s.equalsIgnoreCase("true")); } /** * remove a property value from the BlueJ properties. */ public static String removeProperty(String propertyName) { return (String)(userProps.remove(propertyName)); | |} | |/** | Find and return the file name for an image. | private static File getImageFile(String propname) { String filename = getPropString(propname, null); if (filename != null) { return new File(bluejLibDir, "images" + File.separator + filename); } return null; }
| Find and return the icon for an image, using the definitions in the | properties files to find the actual image. | @OnThread(Tag.Swing) public static ImageIcon getImageAsIcon(String propname) { try { java.net.URL u = getImageFile(propname).toURI().toURL(); return new ImageIcon(u); } catch (java.net.MalformedURLException mue) { } catch (NullPointerException npe) { } return null; }
| Gets an image as an FX image. Note that you pass the property name | as the parameter, which gets resolved via getPropString. | If you don't want this resolution, use getFixedImageAsFXImage | @OnThread(Tag.FX) public static javafx.scene.image.Image getImageAsFXImage(String propname) { try { java.net.URL u = getImageFile(propname).toURI().toURL(); return new javafx.scene.image.Image(u.toString()); } catch (java.net.MalformedURLException mue) { } catch (NullPointerException npe) { } return null; }
| Return an icon for an image file name, without going through bluej.defs. | The parameter specifies the final image name, not a property. | @OnThread(Tag.Swing) public static ImageIcon getFixedImageAsIcon(String filename) { if (filename == null) throw new IllegalArgumentException("Cannot load null image"); File image = new File(bluejLibDir, "images" + File.separator + filename); try { return new ImageIcon(image.toURI().toURL()); } catch (java.net.MalformedURLException mue) { } return null; }
| Gets the given file name from the images directory as an image. | @param filename | @return | @OnThread(Tag.FX) public static javafx.scene.image.Image getFixedImageAsFXImage(String filename) { if (filename == null) throw new IllegalArgumentException("Cannot load null image"); File image = new File(bluejLibDir, "images" + File.separator + filename); try { return new javafx.scene.image.Image(image.toURI().toURL().toString()); } catch (java.net.MalformedURLException mue) { } return null; }
| Find and return an image. The image will have to be tracked. | public static Image getImage(String propname) { try { java.net.URL u = getImageFile(propname).toURI().toURL(); return Toolkit.getDefaultToolkit().createImage(u); } catch (java.net.MalformedURLException mue) { } catch (NullPointerException npe) { } return null; }
| Find the path to an executable command that may be located | in the JDK bin directory, and whose location may optionally | be explicitly specified in the properties. | | The logic goes like this: some tools such as javac, appletviewer | etc should be run from the same bin directory as the JDK that | launched bluej (rather than the first one in the path which may | be of a different version). So for all these properties, if the | property DOES NOT exist, we try to locate the executable in the | JDK directory and if we can't find it we use just the command | name. | | If the property DOES exist we return it and it will be resolved | by the Runtime.exec call (ie looked for in the current path if | the command name is not an absolute path). The specified property | may be null, in which case the property is assumed not to exist. | | This method never returns null (at the very least it returns the | executableName) | public static String getJDKExecutablePath(String propName, String executableName) { if (executableName == null) throw new IllegalArgumentException("must provide an executable name"); String p = propName == null ? null : getSystemPropString(propName); if (p == null) { String jdkPathName = System.getProperty("java.home"); if (jdkPathName != null) { File jdkPath = new File(jdkPathName); File binPath = new File(jdkPath, "bin"); File potentialExe = new File(binPath, executableName); if (potentialExe.exists()) return potentialExe.getAbsolutePath(); potentialExe = new File(binPath, executableName + ".exe"); if (potentialExe.exists()) return potentialExe.getAbsolutePath(); jdkPath = jdkPath.getParentFile(); if (jdkPath != null) { binPath = new File(jdkPath, "bin"); potentialExe = new File(binPath, executableName); if (potentialExe.exists()) return potentialExe.getAbsolutePath(); potentialExe = new File(binPath, executableName + ".exe"); if (potentialExe.exists()) return potentialExe.getAbsolutePath(); } } return executableName; } return p; }
| Return the template directory. | public static File getTemplateDir() { if (templateDir == null) { String path = getPropString("bluej.templatePath", ""); if (path.length() == 0) { templateDir = getLanguageFile("templates"); if (! templateDir.exists()) { templateDir = getDefaultLanguageFile("templates"); } return templateDir; } else { templateDir = new File(path); } } return templateDir; }
| Find and return the file name for a class template file | Format: <template-dir>/<base>.tmpl | public static File getTemplateFile(String base) { return new File(getTemplateDir(), base + ".tmpl"); }
| Return the template directory. | public static File getClassTemplateDir() { return new File(getTemplateDir(), "newclass"); }
| Find and return the file name for a class template file | Format: <template-dir>/<base>.tmpl | public static File getClassTemplateFile(String base) { return new File(getClassTemplateDir(), base + ".tmpl"); }
| Return the file with language specific text. | For example, | <CODE>bluej/lib/english/dialogs</CODE> if base is <CODE>dialogs</CODE> | and the current language is <CODE>english</CODE>. | public static File getLanguageFile(String base) { return new File(bluejLibDir, language + File.separator + base); }
| Return the default language version of a language resource file | public static File getDefaultLanguageFile(String base) { return new File(bluejLibDir, DEFAULT_LANGUAGE + File.separator + base); }
| Return the file name for a file in the user config directory | (<user_home>/.bluej/<base>) | public static File getUserConfigFile(String base) { return new File(userPrefDir, base); }
| Return the user config directory | (<user_home>/.bluej) | public static File getUserConfigDir() { return userPrefDir; }
| Return a color value from the bluej properties. | public static Color getItemColour(String itemname) { try { String rgbStr = getPropString(itemname, "255,0,255"); String rgbVal[] = Utility.split(rgbStr, ","); if (rgbVal.length < 3) Debug.reportError("Error reading colour ["+itemname+"]"); else { int r = Integer.parseInt(rgbVal[0].trim()); int g = Integer.parseInt(rgbVal[1].trim()); int b = Integer.parseInt(rgbVal[2].trim()); return new Color(r, g, b); } } catch(NumberFormatException e) { Debug.reportError("Could not get colour for " + itemname); } return null; }
| Return a color value from the bluej properties. | | If the value is not present, return null | public static Color getOptionalItemColour(String itemname) { try { String rgbStr = getPropString(itemname, null); if (rgbStr == null) { return null; } String rgbVal[] = Utility.split(rgbStr, ","); if (rgbVal.length < 3) { Debug.reportError("Error reading colour ["+itemname+"]"); } else { int r = Integer.parseInt(rgbVal[0].trim()); int g = Integer.parseInt(rgbVal[1].trim()); int b = Integer.parseInt(rgbVal[2].trim()); return new Color(r, g, b); } } catch(NumberFormatException e) { Debug.reportError("Could not get colour for " + itemname); } return null; }
| Get a font from a specified property, using the given default font name and | the given size. Font name can end with "-bold" to indicate bold style. |*/ public static Font getFont(String propertyName, String defaultFontName, int size) { String fontName = getPropString(propertyName, defaultFontName); | |int style; | |if (fontName.endsWith("-bold")) { style = Font.BOLD; fontName = fontName.substring(0, fontName.length()-5); } else { style = Font.PLAIN; } | |return new Font(fontName, style, size); | |} | |/** | Store a point in the config files. The config properties | are formed by adding ".x" and ".y" to the itemPrefix. |*/ private static void putLocation(String itemPrefix, int x, int y) { putPropInteger(itemPrefix + ".x", Math.max(0, x)); putPropInteger(itemPrefix + ".y", Math.max(0, y)); } /** * Return a point, read from the config files. The config properties * are formed by adding ".x" and ".y" to the itemPrefix. */ @OnThread(Tag.FX) private static Point2D getLocation(String itemPrefix) { int x = getPropInteger(itemPrefix + ".x", 16); int y = getPropInteger(itemPrefix + ".y", 16); return ensureOnScreen(x, y); } /** * Return a point, read from the config files. The config properties * are formed by adding ".width" and ".height" to the itemPrefix. * Unless both sizes can be found, null is returned. A minimum size of * 50 in both dimensions is enforced. */ private static Dimension getSize(String itemPrefix) { int w = getPropInteger(itemPrefix + ".width", -1); int h = getPropInteger(itemPrefix + ".height", -1); if (w == -1 || h == -1) return null; return new Dimension(Math.max(w, 50), Math.max(h, 50)); } /** * Store a size in the config files. The config properties | are formed by adding ".width" and ".height" to the itemPrefix. |*/ private static void putSize(String itemPrefix, int width, int height) { putPropInteger(itemPrefix + ".width", Math.max(width, 100)); putPropInteger(itemPrefix + ".height", Math.max(height, 100)); } /** * Set a non-language dependant integer for the BlueJ properties */ public static void putPropInteger(String intname, int value) { String defVal = systemProps.getProperty(intname); | |if (defVal != null) { | |try { | |if (value == Integer.valueOf(defVal).intValue()) { | |userProps.remove(intname); | |} | |} | |catch (NumberFormatException nfe) { | |} | |} | |userProps.setProperty(intname, Integer.toString(value)); | |} | |/** | Set a non-language dependant string for the BlueJ properties. | If the supplied value is null, the property is removed. | public static void putPropString(String strname, String value) { String defVal = systemProps.getProperty(strname); if (value != null && (defVal == null || ! defVal.equals(value))) { userProps.setProperty(strname, value); } else { userProps.remove(strname); } }
| Set a non-language dependent boolean value for the BlueJ properties | public static void putPropBoolean(String propname, boolean value) { String sysval = systemProps.getProperty(propname); if (Boolean.valueOf(sysval).booleanValue() == value) { userProps.remove(propname); } else { userProps.setProperty(propname, String.valueOf(value)); } }
| Returns the blueJLibDir | public static File getBlueJLibDir() { return bluejLibDir; }
| Returns the greenfoot directory in blueJLibDir | public static File getGreenfootLibDir() { return greenfootLibDir; }
| Returns the blueJLibDir | public static String getBlueJIconPath() { return bluejLibDir.getPath() + "/images"; }
| Checks for optional bluej.defs settings for vm language and | country. If either are specified, a Locale object is created | and it becomes the default locale for BlueJ. | private static void setVMLocale() { String lang = Config.getPropString("vm.language", null); String region = Config.getPropString("vm.country", null); if ((lang == null || "".equals(lang)) && (region == null || "".equals(region))) return; if (lang == null || lang.equals("")) lang = System.getProperty("user.language"); if (region == null || region.equals("")) region = System.getProperty("user.country"); debugVMArgs.add("-Duser.language=" + lang); debugVMArgs.add("-Duser.country=" + region); Locale loc = new Locale(lang, region); Locale.setDefault(loc); }
| Initialise debug VM args from bluej config file | Should only be called once in Config.initialise(...) | private static void initDebugVMArgs() { String args = getPropString("bluej.vm.args"); if (args != null && !args.equals("bluej.vm.args")) { List<String> splitArgs = Utility.dequoteCommandLine(args); debugVMArgs.addAll(splitArgs); } }
| Debug vm args used for launch of debug vm | public static List getDebugVMArgs() { return debugVMArgs; }
| Method to determine if BlueJ is running in greenfoot mode. Greenfoot mode | is used when we are launching BlueJ to be used for greenfoot. | | To switch on greenfoot mode the property greenfoot should be set to true. | This can be done by starting BlueJ with this command line argument | -greenfoot=true | public static final boolean isGreenfoot() { return isGreenfoot; }
| Determine whether a file is a ZIP File. | public static boolean isZipFile(File file) { try { if (file.isDirectory()) { return false; } if (!file.canRead()) { throw new IOException(); } if (file.length() < 4) { return false; } DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); int magicNumber = in.readInt(); in.close(); return magicNumber == 0x504b0304; } catch (IOException exc) { Debug.reportError("Could not read file: " + file.getAbsolutePath(), exc); } return false; } @OnThread(Tag.FX) public static void addEditorStylesheets(Scene scene) { String[] stylesheetStems = new String[] { "frame-style", "editor-banners", "editor-catalogue", "editor-error-bar", "editor-error-fix", "editor-expression", "editor-help", "editor-menu", "editor-selection", "editor-slot-choice", "editor-suggestions", "editor-tabs", "moe", "shared"}; for (String stem : stylesheetStems) { addStylesheet(scene.getStylesheets(), stem); } addJavaColorsStylesheet(scene.getStylesheets()); } @OnThread(Tag.FX) public static void addInspectorStylesheets(Scene scene) { addStylesheet(scene.getStylesheets(), "inspectors"); } @OnThread(Tag.FX) public static void addPopupStylesheets(Parent root) { addStylesheet(root.getStylesheets(), "popup"); } @OnThread(Tag.FX) public static void addTestsStylesheets(Scene scene) { addStylesheet(scene.getStylesheets(), "tests"); } @OnThread(Tag.FX) public static void addTerminalStylesheets(Scene scene) { addStylesheet(scene.getStylesheets(), "terminal"); } @OnThread(Tag.FX) public static void addDebuggerStylesheets(Scene scene) { addStylesheet(scene.getStylesheets(), "debugger"); addStylesheet(scene.getStylesheets(), "shared"); } @OnThread(Tag.FX) public static void addPMFStylesheets(Scene scene) { addStylesheet(scene.getStylesheets(), "pkgmgrframe"); } @OnThread(Tag.FX) public static void addGreenfootStylesheets(Scene scene) { addStylesheet(scene.getStylesheets(), "greenfoot"); } @OnThread(Tag.FX) private static void addStylesheet(ObservableList<String> sheetList, String stem) { try { sheetList.add(new File(bluejLibDir + "/stylesheets", stem + ".css").toURI().toURL().toString()); } catch (MalformedURLException e) { Debug.reportError(e); } } @OnThread(Tag.FX) public static void addDialogStylesheets(DialogPane dialogPane) { addStylesheet(dialogPane.getStylesheets(), "dialogs"); addJavaColorsStylesheet(dialogPane.getStylesheets()); } @OnThread(Tag.FX) private static void addJavaColorsStylesheet(ObservableList<String> stylesheets) { addStylesheet(stylesheets, "java-colors"); File userColorsFile = getUserConfigFile("java-colors.css"); if (userColorsFile.exists()) { try { stylesheets.add(userColorsFile.toURI().toURL().toString()); Debug.log("Using user-specified java-colors file."); } catch (MalformedURLException e) { Debug.reportError(e); } } } @OnThread(Tag.Swing) public static Border getFocusBorder() { if (focusBorder == null) { focusBorder = new CompoundBorder(new LineBorder(Color.BLACK), new BevelBorder(BevelBorder.LOWERED, new Color(195, 195, 195), new Color(240, 240, 240), new Color(195, 195, 195), new Color(124, 124, 124))); } return focusBorder; } @OnThread(Tag.Swing) public static Border getNormalBorder() { if (normalBorder == null) { normalBorder = new CompoundBorder(new EmptyBorder(1,1,1,1), new BevelBorder(BevelBorder.LOWERED, new Color(195, 195, 195), new Color(240, 240, 240), new Color(124, 124, 124), new Color(195, 195, 195))); } return normalBorder; } public static void loadFXFonts() { if (!fontOptions.isEmpty()) return; for (File file : new File(bluejLibDir + "/fonts").listFiles()) { if (file.getName().toLowerCase().endsWith(".ttf")) { try { FileInputStream fis = new FileInputStream(file); final javafx.scene.text.Font font = javafx.scene.text.Font.loadFont(fis, 10); fis.close(); if (font == null) { Debug.reportError("Unknown problem loading TTF JavaFX font: " + file.getAbsolutePath()); } if (font != null && !fontOptions.contains(font.getFamily())) { fontOptions.add(font.getFamily()); Collections.sort(fontOptions); } } catch (Throwable e) { Debug.reportError("Error loading font: " + file.getAbsolutePath(), e); } } } } @OnThread(Tag.FXPlatform) public static void loadAndTrackPosition(Window window, String locationPrefix) { JavaFXUtil.addChangeListener(window.xProperty(), x -> putLocation(locationPrefix, (int)window.getX(), (int)window.getY())); JavaFXUtil.addChangeListener(window.yProperty(), y -> putLocation(locationPrefix, (int)window.getX(), (int)window.getY())); Point2D location = getLocation(locationPrefix); window.setX(location.getX()); window.setY(location.getY()); } @OnThread(Tag.FXPlatform) public static void loadAndTrackPositionAndSize(Window window, String locationPrefix) { loadAndTrackPosition(window, locationPrefix); JavaFXUtil.addChangeListener(window.widthProperty(), x -> putSize(locationPrefix, (int)window.getWidth(), (int)window.getHeight())); JavaFXUtil.addChangeListener(window.heightProperty(), y -> putSize(locationPrefix, (int)window.getWidth(), (int)window.getHeight())); Dimension location = getSize(locationPrefix); if (location != null) { window.setWidth(location.width); window.setHeight(location.height); } }
| Remembers the position of the split pane's divider. Assumes there is only one divider (for now). | Only call after you've added the split pane items, as otherwise divider won't yet exist. | | @param window The window containing the split pane. We listen to window-showing for setting the split pane. | @OnThread(Tag.FXPlatform) public static void rememberDividerPosition(Window window, SplitPane splitPane, String locationName) { double SCALE = 1_000_000; double initialPos = (double)Config.getPropInteger(locationName, (int) (0.5 * SCALE)) / SCALE; JavaFXUtil.addChangeListener(splitPane.getDividers().get(0).positionProperty(), pos -> putPropInteger(locationName, (int)(pos.doubleValue() * SCALE))); if (window.isShowing()) { splitPane.setDividerPosition(0, initialPos); } else { window.addEventHandler(WindowEvent.WINDOW_SHOWN, e -> { splitPane.setDividerPosition(0, initialPos); }); } } public static KeyCode getKeyCodeForYesNo(InteractionManager.ShortcutKey keyPurpose) { switch (keyPurpose) { case YES_ANYWHERE: return KeyCode.F2; case NO_ANYWHERE: return KeyCode.F3; } return null; } public static boolean isGreenfootStartupProject(File projectDir) { return getGreenfootStartupProjectPath().equals(projectDir.getAbsolutePath()); } public static String getGreenfootStartupProjectPath() { return new File(getBlueJLibDir(), "greenfoot/startupProject").getAbsolutePath(); } @OnThread(Tag.FX) public static Point2D ensureOnScreen(int x, int y) { boolean onScreenAlready = Screen.getScreens().stream().anyMatch(screen -> { return screen.getVisualBounds().contains(x, y) && screen.getVisualBounds().contains(x + 80, y + 80); }); if (onScreenAlready) { return new Point2D(x, y); } else { return new Point2D(Screen.getPrimary().getBounds().getMinX() + 100, Screen.getPrimary().getBounds().getMinY() + 100); } } @OnThread(Tag.FX) public static Node makeStopIcon(boolean large) { Polygon octagon = new Polygon( 9, 0, 22, 0, 31, 9, 31, 22, 22, 31, 9, 31, 0, 22, 0, 9 ); if (!large) { JavaFXUtil.scalePolygonPoints(octagon, 0.5, false); } JavaFXUtil.addStyleClass(octagon, "octagon"); Label stop = new Label("STOP"); StackPane stackPane = new StackPane(octagon, stop); JavaFXUtil.setPseudoclass("bj-large", large, stackPane); JavaFXUtil.addStyleClass(stackPane, "stop-icon"); return stackPane; } @OnThread(Tag.FX) public static Polygon makeArrowShape(boolean shortTail) { return new Polygon( 10, 0, 18, 10, 10, 20, 10, 14, shortTail ? 4 : 0, 14, shortTail ? 4 : 0, 6, 10, 6 ); }
| This is almost equivalent to the SourceType in bluej.extensions, but we | don't want Config to depend on that class so we re-create the same idea here. | public enum SourceType { Java, Stride } }

.   - Config
.   initialise
.   initialise
.   initLanguage
.   initUserHome
.   getInitialCommandLineProperties
.   initializeStandalone
.   getProperty
.   getProperty
.   isInitialised
.   getVMIconsName
.   getVMDockName
.   isDebugVM
.   isMacOS
.   osVersionNumberAtLeast
.   javaVersionNumberAtLeast
.   versionAtLeast
.   isMacOSLeopard
.   isMacOSSnowLeopard
.   isWinOS
.   isModernWinOS
.   isLinux
.   isSolaris
.   isJava17
.   isOpenJDK
.   makeDialogsResizable
.   getBlueJPrefDirName
.   getApplicationName
.   calculateScreenBounds
.   checkDebug
.   handleExit
.   saveAppProperties
.   recordEditorOpen
.   getEditorCount
.   resetEditorsCount
.   recordLatestSeen
.   loadDefs
.   loadLanguageLabels
.   loadProperties
.   saveProperties
.   getMoeHelp
.   getMnemonicKey
.   hasAcceleratorKey
.   getAcceleratorKeyFX
.   getSystemPropString
.   getPropString
.   getPropBooleanProperty
.   getPropBoolean
.   getPropBoolean
.   getImageFile
.   getImageAsIcon
.   getImageAsFXImage
.   getFixedImageAsIcon
.   getFixedImageAsFXImage
.   getImage
.   getJDKExecutablePath
.   getTemplateDir
.   getTemplateFile
.   getClassTemplateDir
.   getClassTemplateFile
.   getLanguageFile
.   getDefaultLanguageFile
.   getUserConfigFile
.   getUserConfigDir
.   getItemColour
.   getOptionalItemColour
.   putPropString
.   putPropBoolean
.   getBlueJLibDir
.   getGreenfootLibDir
.   getBlueJIconPath
.   setVMLocale
.   initDebugVMArgs
.   getDebugVMArgs
.   isGreenfoot
.   isZipFile
.   addEditorStylesheets
.   addInspectorStylesheets
.   addPopupStylesheets
.   addTestsStylesheets
.   addTerminalStylesheets
.   addDebuggerStylesheets
.   addPMFStylesheets
.   addGreenfootStylesheets
.   addStylesheet
.   addDialogStylesheets
.   addJavaColorsStylesheet
.   getFocusBorder
.   getNormalBorder
.   loadFXFonts
.   loadAndTrackPosition
.   loadAndTrackPositionAndSize
.   rememberDividerPosition
.   getKeyCodeForYesNo
.   isGreenfootStartupProject
.   getGreenfootStartupProjectPath
.   ensureOnScreen
.   makeStopIcon
.   makeArrowShape




2058 neLoCode + 327 LoComm