Java tutorial
/******************************************************************************* * Copyright (C) 2011 Peter Brewer. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Peter Brewer ******************************************************************************/ // Copyright (c) 2001 Ken Harris <kbh7@cornell.edu> // Copyright (c) 2004-2005 Aaron Hamid // See license in COPYING.txt distributed with this file and available online at http://www.gnu.org/licenses/gpl.txt package org.tellervo.desktop.prefs; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import javax.swing.AbstractAction; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ColorUIResource; import javax.swing.plaf.FontUIResource; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tellervo.desktop.core.AbstractSubsystem; import org.tellervo.desktop.core.App; import org.tellervo.desktop.gui.BugDialog; import org.tellervo.desktop.platform.Platform; import org.tellervo.desktop.ui.I18n; import org.tellervo.desktop.util.BugReport; import org.tellervo.desktop.util.JDisclosureTriangle; import org.tellervo.desktop.util.WeakEventListenerList; import org.tellervo.schema.WSIWmsServer; /** * Storage and access of user preferences. * * <h2>Left to do</h2> * <ul> * <li>add getFont(), setColor(), etc. convenience methods * <li>get rid of JDisclosureTriangle usage (and then delete it) * <li>get rid of "system" preferences (and file Source/prefs.properties) - these should be inline, in each subsystem, with the prefs call * <li>extract prefs file location to platform? * <li>(goal: 300 lines for everything, maybe 350) * </ul> * * @author Ken Harris <kbh7 <i style="color: gray">at </i> cornell <i style="color: gray">dot </i> edu> * @version $Id$ */ public class Prefs extends AbstractSubsystem { // sample editor private final static Logger log = LoggerFactory.getLogger(Prefs.class); private static String ARRAY_DELIMITER = ";"; /** * Enum for all the keys identifying preference types in Tellervo * * @author pwb48 * */ public enum PrefKey { EDITOR_REMARKS_DIVIDER_LOCAITON("tellervo.editor.remarks.dividerloc"), LOCALE_LANGUAGE_CODE("tellervo.locale.language"), LOCALE_COUNTRY_CODE("tellervo.locale.country"), EDIT_FOREGROUND("tellervo.edit.foreground"), EDIT_BACKGROUND("tellervo.edit.background"), EDIT_FONT( "tellervo.edit.font"), EDIT_GRIDLINES("tellervo.edit.gridlines"), EDITOR_GRAPH_DIVIDER_LOCAITON("tellervo.editor.graph.dividerloc"), GRAPH_BACKGROUND( "tellervo.graph.background"), GRAPH_GRIDLINES("tellervo.graph.graphpaper"), GRAPH_GRIDLINES_COLOR( "tellervo.graph.graphpaper.color"), GRAPH_AXISCURSORCOLOR( "tellervo.graph.foreground"), GRID_HIGHLIGHT( "tellervo.grid.highlight"), GRID_HIGHLIGHTCOLOR( "tellervo.grid.hightlightcolor"), DISPLAY_UNITS( "tellervo.displayunits"), DISPLAY_DATING_SUFFIX( "tellervo.displaydatingsuffix"), BARCODES_DISABLED( "tellervo.barcodes.disable"), MEASUREMENT_VARIABLE( "tellervo.measurement.variable"), HIDE_PINNING_AND_RADIUS_SHIFT_ICONS( "tellervo.remarks.hidepinningandradiusshifticons"), DERIVED_REMARKS_THRESHOLD( "tellervo.remarks.thresholdfordisplay"), /** Boolean indicating whether OpenGL failed on last attempt*/ OPENGL_FAILED("opengl.failed"), OPENGL_LIBRARY_PRESENT("opengl.librarypresent"), SERIAL_DEVICE( "tellervo.serial.measuring.device"), SERIAL_PORT("tellervo.serialsampleio.port"), SERIAL_DATABITS( "tellervo.port.databits"), SERIAL_BAUD("tellervo.port.baudrate"), SERIAL_FLOWCONTROL( "tellervo.port.flowcontrol"), SERIAL_PARITY( "tellervo.port.parity"), SERIAL_LINEFEED( "tellervo.port.linefeed"), SERIAL_STOPBITS( "tellervo.port.stopbits"), SERIAL_MEASURE_CUMULATIVELY( "tellervo.serial.measurecumulatively"), SERIAL_MEASURE_IN_REVERSE( "tellervo.serial.measurereverse"), SERIAL_MULTIPLIER( "tellervo.serial.multiplier"), SERIAL_PORT_TIMEOUT_LENGTH( "tellervo.serial.timeout"), SERIAL_LIBRARY_PRESENT( "tellervo.serial.librarypresent"), MEASURE_BARK_TO_PITH( "tellervo.measure.pithtobark"), STATS_FORMAT_TSCORE("tellervo.cross.tscore.format"), STATS_FORMAT_RVALUE( "tellervo.cross.rvalue.format"), STATS_FORMAT_TREND( "tellervo.cross.trend.format"), STATS_FORMAT_DSCORE( "tellervo.cross.dscore.format"), STATS_FORMAT_WEISERJAHRE( "tellervo.cross.weiserjahre.format"), STATS_OVERLAP_REQUIRED( "tellervo.cross.overlap"), STATS_OVERLAP_REQUIRED_DSCORE( "tellervo.cross.d-overlap"), STATS_MODELINE( "tellervo.modeline.statistic"), STATS_SKELETON_PLOT_WINDOW_SIZE( "tellervo.skeletonplot.windowsize"), STATS_SKELETON_PLOT_ALGORITHM( "tellervo.skeletonplot.algorithm"), PERSONAL_DETAILS_EMAIL("tellervo.bugreport.fromemail"), PERSONAL_DETAILS_USERNAME( "tellervo.login.username"), PERSONAL_DETAILS_PASSWORD("tellervo.login.password"), REMEMBER_USERNAME( "tellervo.login.remember_username"), REMEMBER_PASSWORD( "tellervo.login.remember_password"), AUTO_LOGIN("tellervo.login.auto_login"), INDEX_POLY_DEGREES("tellervo.index.polydegs"), INDEX_LOWPASS("tellervo.index.lowpass"), INDEX_CUBIC_FACTOR( "tellervo.index.cubicfactor"), EXPORT_FORMAT("tellervo.export.format"), EXPORT_NAMING_CONVENTION( "tellervo.export.namingconvention"), EXPORT_FOLDER("tellervo.export.folder"), IMPORT_FORMAT( "tellervo.import.format"), EXPORT_GROUPING("tellervo.grouping"), /** URL for the Tellervo webservice */ WEBSERVICE_URL("tellervo.webservice.url"), WEBSERVICE_URL_OTHERS( "tellervo.webservice.url.others"), WEBSERVICE_DISABLED( "tellervo.webservice.disable"), WEBSERVICE_WARNED_OFFLINE_LIMITED( "tellervo.webservice.offlinewarning"), WEBSERVICE_USE_STRICT_SECURITY( "tellervo.webservice.usestrictsecurity"), PROXY_TYPE("tellervo.proxy.type"), PROXY_PORT_HTTP("tellervo.proxy.http_port"), PROXY_PORT_HTTPS( "tellervo.proxy.https_port"), PROXY_HTTPS( "tellervo.proxy.https"), PROXY_HTTP("tellervo.proxy.http"), WMS_PERSONAL_SERVERS("tellervo.wms.personalservers"), GRAPH_DEFAULT_AGENT("tellervo.graph.defaultagent"), FOLDER_DATA("tellervo.dir.data"), FOLDER_LAST_READ("tellervo.dir.lastimport"), FOLDER_ODK_LAST_READ( "tellervo.dir.odk.lastread"), FOLDER_LAST_SAVE( "tellervo.dir.lastsave"), FOLDER_LAST_GPS("tellervo.dir.lastgps"), SOUND_PLATFORM_INIT_FILE("tellervo.sound.platforminit"), SOUND_MEASURE_RING_FILE( "tellervo.sound.measure"), SOUND_MEASURE_DECADE_FILE( "tellervo.sound.decade"), SOUND_MEASURE_ERROR_FILE( "tellervo.sound.error"), SOUND_BARCODE_FILE( "tellervo.sound.barcodescan"), SOUND_USE_SYSTEM_DEFAULTS( "tellervo.sound.usedefaults"), SOUND_ENABLED( "tellervo.sound.enabled"), SOUND_LAST_FOLDER( "tellervo.sound.lastfolder"), CHECK_FOR_UPDATES("tellervo.checkforupdates"), OBJECT_FIELD_VISIBLE_OBJECT_CODE("tellervo.object.field.visible.objectcode"), OBJECT_FIELD_VISIBLE_TITLE( "tellervo.object.field.visible.title"), OBJECT_FIELD_VISIBLE_COMMENTS( "tellervo.object.field.visible.comments"), OBJECT_FIELD_VISIBLE_TYPE( "tellervo.object.field.visible.type"), OBJECT_FIELD_VISIBLE_DESCRIPTION( "tellervo.object.field.visible.description"), OBJECT_FIELD_VISIBLE_LATITUDE( "tellervo.object.field.visible.latitude"), OBJECT_FIELD_VISIBLE_LONGTITUDE( "tellervo.object.field.visible.longtitude"), OBJECT_FIELD_VISIBLE_WAYPOINT( "tellervo.object.field.visible.waypoint"), OBJECT_FIELD_VISIBLE_PARENT_OBJECT( "tellervo.object.field.visible.parentobject"), OBJECT_FIELD_VISIBLE_ADDRESSLINE1( "tellervo.object.field.visible.address1"), OBJECT_FIELD_VISIBLE_ADDRESSLINE2( "tellervo.object.field.visible.address2"), OBJECT_FIELD_VISIBLE_CITY_TOWN( "tellervo.object.field.visible.citytown"), OBJECT_FIELD_VISIBLE_STATE_PROVINCE_REGION( "tellervo.object.field.visible.stateprovinceregion"), OBJECT_FIELD_VISIBLE_POSTCODE( "tellervo.object.field.visible.postalcode"), OBJECT_FIELD_VISIBLE_COUNTRY( "tellervo.object.field.visible.country"), OBJECT_FIELD_VISIBLE_LOCATION_PRECISION( "tellervo.object.field.visible.locationprecision"), OBJECT_FIELD_VISIBLE_LOCATION_COMMENT( "tellervo.object.field.visible.locationcomment"), OBJECT_FIELD_VISIBLE_OWNER( "tellervo.object.field.visible.owner"), OBJECT_FIELD_VISIBLE_CREATOR( "tellervo.object.field.visible.creator"), MAP_CONTROLS_ENABLED("tellervo.map.controls"), MAP_COMPASS_ENABLED( "tellervo.map.compass"), MAP_COUNTRYBOUNDARY_ENABLED( "tellervo.map.countryboundary"), MAP_MGRSGRATICULE_ENABLED( "tellervo.map.mgrsgraticule"), MAP_NASAWFSPLACENAME_ENABLED( "tellervo.map.nasawfsplacename"), MAP_SCALEBAR_ENABLED( "tellervo.map.scalebar"), MAP_UTMGRATICULE_ENABLED( "tellervo.map.utmgraticule"), MAP_WORLDMAP_ENABLED( "tellervo.map.worldmap"), MAP_STEREOMODE_ENABLED( "tellervo.map.stereomode"), ODK_COPY_TO("tellervo.odk.copyto"), ODK_FINAL_PREFIX("tellervo.odk.finalprefix"), ODK_IMPORT_MEDIA( "tellervo.odk.importmedia"), ODK_RENAME_MEDIA("tellervo.odk.renamemedia"), ODK_CSV_FILENAME( "tellervo.odk.csvoutput"), ODK_CREATE_CSV("tellervo.odk.createcsv"), ; private String key; private PrefKey(String key) { this.key = key; } public String getValue() { return key; } } /** * if true, silently ignore if the prefs can't be saved. protected to avoid * synthetic accessor */ protected static boolean dontWarn = false; private String prefsFolder; private String userPrefsFilename; /** * Our internal Properties object in which to save preferences */ private Properties prefs; /** * A copy of the default UIDefaults object available at startup. We cache * these defaults to allow the user to "reset" any changes they may have * made through the Appearance Panel. */ private final Hashtable<Object, Object> UIDEFAULTS = new Hashtable<Object, Object>(); // (Hashtable) // UIManager.getDefaults().clone(); public String getName() { return "Preferences"; } // returns the directory for storing Tellervo-related files... public String getTellervoDir() { return prefsFolder; } /** * Migrate old Corina preferences to Tellervo */ private Boolean migrateCorinaToTellervo() { String home = System.getProperty("user.home"); String corinaUserPrefsFilename; String tellervoUserPrefsFilename; if (!home.endsWith(File.separator)) home = home + File.separator; if (App.platform.isWindows()) { corinaUserPrefsFilename = home + "Application Data" + File.separator + "Corina" + File.separator + "Corina.pref"; tellervoUserPrefsFilename = home + "Application Data" + File.separator + "Tellervo" + File.separator + "Tellervo.pref"; } else if (Platform.isMac()) { corinaUserPrefsFilename = home + "Library/Corina/Preferences"; tellervoUserPrefsFilename = home + "Library/Tellervo/Preferences"; } else { corinaUserPrefsFilename = home + ".corina/.preferences"; tellervoUserPrefsFilename = home + ".tellervo" + File.separator + ".preferences"; } // Do the copy if old exists and new doesn't File oldprefs = new File(corinaUserPrefsFilename); File newprefs = new File(tellervoUserPrefsFilename); if (oldprefs.exists() && !newprefs.exists()) { try { FileUtils.copyFile(oldprefs, newprefs); String oldprefcontents = FileUtils.readFileToString(oldprefs, "UTF-8"); String newprefcontents = oldprefcontents.replaceAll("corina.", "tellervo."); FileUtils.writeStringToFile(newprefs, newprefcontents, "UTF-8"); initPrefFolderLocations(); load(); } catch (IOException e) { log.error("Failed to migrate Corina preferences to Tellervo"); } } else { return false; } return true; } /** * Set the preferences folder and filename depending on OS */ private void initPrefFolderLocations() { // Get home folder String home = System.getProperty("user.home"); if (!home.endsWith(File.separator)) home = home + File.separator; if (App.platform.isWindows()) { String basedir = home; // if the Application Data directory exists, use it as our base. if (new File(home + "Application Data").isDirectory()) basedir = home + "Application Data" + File.separator; // ~/Application Data/Tellervo/ // OR ~/Tellervo/, if no Application Data basedir += "Tellervo" + File.separator; // if the Tellervo directory doesn't exist, make it. File tellervodir = new File(basedir); if (!tellervodir.exists()) tellervodir.mkdir(); prefsFolder = basedir; userPrefsFilename = basedir + "Tellervo.pref"; } else if (Platform.isMac()) { prefsFolder = home + "Library/Tellervo/"; userPrefsFilename = home + "Library/Tellervo/Preferences"; File basedir = new File(prefsFolder); if (!basedir.exists()) basedir.mkdirs(); } else { // good ol' Linux prefsFolder = home + ".tellervo/"; userPrefsFilename = home + ".tellervo/.preferences"; File basedir = new File(prefsFolder); if (!basedir.exists()) basedir.mkdirs(); } } /** * Initializes the preferences system. This should be called upon startup. */ @Override public void init() { super.init(); // Migrate Corina prefs first if necessary Boolean migrated = migrateCorinaToTellervo(); if (migrated) { setInitialized(true); return; } // Initialise the preferences file and folder initPrefFolderLocations(); // Initialise the default UI settings initUIDefaults(); // Proceed with loading the preferences try { load(); } catch (IOException ioe) { new BugDialog(ioe); } setInitialized(true); } @Override public void destroy() { // don't need to prevent double destroys, just resave if (!initialized) log.debug("being destroyed more than once!"); save(); } @Override public void finalize() throws Throwable { save(); super.finalize(); } public Hashtable<Object, Object> getUIDefaults() { return UIDEFAULTS; } private void initUIDefaults() { // UIDEFAULTS.putAll(UIManager.getDefaults()); UIDefaults defaults = UIManager.getDefaults(); // XXX: Even though Hashtable implements Map since // Java 1.2, UIDefaults is "special"... the Iterator // returned by UIDefaults keySet object does not return any // keys, and therefore Enumeration must be used instead - aaron Enumeration<?> e = defaults.keys(); while (e.hasMoreElements()) { Object key = e.nextElement(); Object value = defaults.get(key); // for some reason, java 1.6 seems to have some null values. if (value == null) continue; // log.debug("Saving UIDefault: " + key); UIDEFAULTS.put(key, value); } } /** * Load system and user properties (preferences). This runs 3 steps: * <ol> * <li>Load system properties * <li>Load default properties from class loader resource prefs.properties * <li>Override with user's properties file * </ol> * */ private synchronized void load() throws IOException { // get existing properties Properties systemprops = System.getProperties(); log.debug("Loading preferences"); // a place to record errors that may occur, because we don't // want to crap out before we've tried everything. StringBuffer errors = new StringBuffer(); Properties defaults = new Properties(systemprops); // load system properties as a resource from this jar ClassLoader cl = Prefs.class.getClassLoader(); try { java.io.InputStream is = cl.getResourceAsStream("prefs.properties"); if (is != null) { try { defaults.load(is); } finally { is.close(); } } } catch (IOException ioe) { errors.append("Error loading Tellervo's default preferences.\n"); } // instantiate our properties using the system properties // and tellervo properties as default values prefs = new Properties(defaults); try { prefs.load(new FileInputStream(userPrefsFilename)); } catch (FileNotFoundException fnfe) { // user doesn't have a properties file, so we'll give her one! try { // this is the guts of save(), but without the nice error // handling. prefs.store(new FileOutputStream(userPrefsFilename), "Tellervo user preferences"); App.isFirstRun = true; } catch (IOException ioe) { errors.append("Error copying preferences file to your home directory: " + ioe.getMessage() + "\n"); } } catch (IOException ioe) { errors.append("Error loading user preferences file: " + ioe.getMessage() + "\n"); } // install any UIDefaults preferences the user may have installUIDefaultsPrefs(); // if there was an exception thrown-and-caught, re-throw it now if (errors.length() != 0) throw new IOException(errors.toString()); } /** * Loads any saved uidefaults preferences and installs them */ private synchronized void installUIDefaultsPrefs() { Iterator<?> it = prefs.entrySet().iterator(); UIDefaults uidefaults = UIManager.getDefaults(); log.debug("iterating prefs"); while (it.hasNext()) { @SuppressWarnings("rawtypes") Map.Entry entry = (Map.Entry) it.next(); String prefskey = entry.getKey().toString(); if (!prefskey.startsWith("uidefaults.") || prefskey.length() <= "uidefaults.".length()) continue; String uikey = prefskey.substring("uidefaults.".length()); Object object = uidefaults.get(uikey); log.debug("prefs property " + uikey + " " + object); installUIDefault(object.getClass(), prefskey, uikey); } } private void installUIDefault(Class<? extends Object> type, String prefskey, String uikey) { Object decoded = null; String pref = prefs.getProperty(prefskey); if (pref == null) { log.warn("Preference '" + prefskey + "' held null value."); return; } if (Color.class.isAssignableFrom(type)) { decoded = Color.decode(pref); } else if (Font.class.isAssignableFrom(type)) { decoded = Font.decode(pref); } else { log.warn("Unsupported UIDefault preference type: " + type); return; } if (decoded == null) { log.warn("UIDefaults color preference '" + prefskey + "' was not decodable."); return; } UIDefaults uidefaults = UIManager.getDefaults(); // if (uidefaults.contains(property)) { // NOTE: ok, UIDefaults object is strange. Not only does // it not implement the Map interface correctly, but entries // will not "stick". The entries must be first explicitly // removed, and then re-added - aaron log.debug("Removing UIDefaults key before overwriting: " + uikey); uidefaults.remove(uikey); // } if (Color.class.isAssignableFrom(type)) { uidefaults.put(uikey, new ColorUIResource((Color) decoded)); } else { uidefaults.put(uikey, new FontUIResource((Font) decoded)); } } /* * XXX: maybe expose this later if/when we have a component/module/subsystem * model public static void destroy() { save(); } */ /** * Save current properties to the user's system-writable properties * (preferences) file, <code>FILENAME</code>. */ public synchronized void save() { log.debug("Saving preferences..."); // try to save prefs, and on failure present "try again?" dialog. while (true) { try { // -- p.store() buffers internally, so if i used a buffered // stream // here it would only hurt performance. // -- the second string passed to store() is a comment line // which // is added to the top of the file. prefs.store(new FileOutputStream(userPrefsFilename), "Tellervo user preferences"); return; } catch (IOException ioe) { if (dontWarn) return; boolean tryAgain = cantSave(ioe); if (!tryAgain) return; } } } /** * Deletes the preferences file so on reboot Tellervo acts as a fresh install * @return */ public Boolean resetPreferences() { try { File file = new File(userPrefsFilename); if (file.delete()) { log.debug("Preferences file deleted"); return true; } else { log.error("Unable to delete preferences file"); } } catch (Exception e) { e.printStackTrace(); } return false; } // exception |e| thrown while saving; tell user. // return value: true => "try again". // TODO: use jlinedlabel to be more explicit about the problem // TODO: (need left-alignment option on that class, first) private static boolean cantSave(Exception e) { JPanel message = new JPanel(new BorderLayout(0, 8)); // (hgap,vgap) message.add(new JLabel(I18n.getText("error.prefs_cant_save")), BorderLayout.NORTH); // -- dialog with optionpane (warning?) JOptionPane optionPane = new JOptionPane(message, JOptionPane.ERROR_MESSAGE); JDialog dialog = optionPane.createDialog(null /* ? */, I18n.getText("error.prefs_cant_save_title")); // -- buttons: cancel, try again. optionPane.setOptions(new String[] { I18n.getText("question.try_again"), I18n.getText("general.cancel") }); // -- disclosure triangle with scrollable text area: click for // details... (stacktrace) JComponent stackTrace = new JScrollPane(new JTextArea(BugReport.getStackTrace(e), 10, 60)); JDisclosureTriangle v = new JDisclosureTriangle(I18n.getText("bug.click_for_details"), stackTrace, false); message.add(v, BorderLayout.CENTER); // -- checkbox: don't warn me again JCheckBox dontWarnCheckbox = new JCheckBox(I18n.getText("bug.dont_warn_again"), false); dontWarnCheckbox.addActionListener(new AbstractAction() { private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent e) { dontWarn = !dontWarn; } }); // FIXME: consolidate |message| panel construction with Layout methods message.add(dontWarnCheckbox, BorderLayout.SOUTH); // show dialog dialog.pack(); dialog.setResizable(false); dialog.setVisible(true); // return true if "try again" is clicked return optionPane.getValue().equals(I18n.getText("try_again")); } // -------------------------------------------------- // new prefs api below here /* * TODO: -- set/get any data type -- automatically save -- (but not right * away, which would be slow) -- defaults -- required? -- convenience * functions for non-atomic things like window geometries? -- also, lists of * data -- prefs event -- prefs listener -- on prefs.set, fire events -- get * rid of all refreshFromPrefs() methods, HasPreferences interface -- * (register listener with type, like jrendezvous?) */ public Properties getPrefs() { return prefs; } /** * Set the value of a preference * * @param key * @param value */ public void setPref(PrefKey key, String value) { String pref = key.getValue(); // support removing via set(null) if (value == null) { prefs.remove(pref); } // Trim spaces from webservice URL else if (pref.equals("tellervo.webservice.url")) { prefs.setProperty(pref, value.trim()); } else { prefs.setProperty(pref, value); } save(); firePrefChanged(pref); } /** * Set the value of a preference to the specified boolean * @param key * @param value */ public void setBooleanPref(PrefKey key, boolean value) { setPref(key, Boolean.toString(value)); } public void removeWSIWmsServerFromArray(PrefKey key, WSIWmsServer server) { ArrayList<WSIWmsServer> servers = getWSIWmsServerArrayPref(key); servers.remove(server); setWSIWmsServerArrayPref(key, servers); } public void addWSIWmsServerToArrayPref(PrefKey key, WSIWmsServer server) { ArrayList<WSIWmsServer> servers = getWSIWmsServerArrayPref(key); if (servers == null) servers = new ArrayList<WSIWmsServer>(); servers.add(server); setWSIWmsServerArrayPref(key, servers); } public void setWSIWmsServerArrayPref(PrefKey key, ArrayList<WSIWmsServer> arr) { String str = ""; for (WSIWmsServer server : arr) { str += server.getName() + "=" + server.getUrl() + ":::"; } setPref(key, str); } public ArrayList<WSIWmsServer> getWSIWmsServerArrayPref(PrefKey key) { ArrayList<WSIWmsServer> servers = new ArrayList<WSIWmsServer>(); String str = getPref(key, ""); String[] pairs = str.split(":::"); for (String pair : pairs) { String[] pair2 = pair.split("="); if (pair2.length != 2) continue; WSIWmsServer server = new WSIWmsServer(); server.setName(pair2[0]); server.setUrl(pair2[1]); servers.add(server); } return servers; } /** * Set the value of a preference to the specified color * @param pref * @param value */ public void setColorPref(PrefKey pref, Color value) { String encoded = "#" + Integer.toHexString(value.getRGB() & 0x00ffffff); setPref(pref, encoded); } /** * Set the value of a preference to the specified int * @param pref * @param value */ public void setIntPref(PrefKey pref, int value) { setPref(pref, Integer.toString(value)); } /** * Set the value of a preference to the specified double * @param pref * @param value */ public void setDoublePref(PrefKey pref, Double value) { setPref(pref, Double.toString(value)); } /** * Set the value of a preference to the specified font * @param pref * @param value */ public void setFontPref(PrefKey pref, Font value) { setPref(pref, stringifyFont(value)); } /** * Set the value of a preference to the specified dimension * @param pref * @param value */ public void setDimensionPref(PrefKey pref, Dimension value) { setPref(pref, value.width + "," + value.height); } /** * Method for getting the string value of a preference * * @param key * @param deflt * @return */ public String getPref(PrefKey key, String deflt) { String value = prefs.getProperty(key.getValue()); if (value == null) value = deflt; return value; } public Double getDoublePref(PrefKey key, Double deflt) { String value = prefs.getProperty(key.getValue()); if (value == null) return deflt; try { return Double.parseDouble(value); } catch (NumberFormatException nfe) { log.warn("Invalid double for preference '" + key.getValue() + "': " + value); return deflt; } } /** * Get a color for the specified preference key * * @param key * @param deflt * @return */ public Color getColorPref(PrefKey key, Color deflt) { String value = prefs.getProperty(key.getValue()); if (value == null) return deflt; try { return Color.decode(value); } catch (NumberFormatException nfe) { log.warn("Invalid color for preference '" + key.getValue() + "': " + value); return deflt; } } /** * Get an enum for the specified preference key * * @param <T> * @param pref * @param deflt * @param enumType * @return */ public <T extends Enum<T>> T getEnumPref(PrefKey pref, T deflt, Class<T> enumType) { String val = getPref(pref, null); if (val == null) return deflt; try { return Enum.valueOf(enumType, val); } catch (Exception e) { return deflt; } } public ArrayList<String> getArrayListPref(PrefKey key, ArrayList<String> deflt) { String value = prefs.getProperty(key.getValue()); if (value == null) { return deflt; } String[] values = value.split(ARRAY_DELIMITER); ArrayList<String> arrlist = new ArrayList<String>(); for (String v : values) { arrlist.add(v); } if (values.length > 0) { return arrlist; } return deflt; } public void addToArrayListPref(PrefKey selectedKey, PrefKey optionsKey, String str) { this.setPref(selectedKey, str); ArrayList<String> arr = getArrayListPref(optionsKey, null); if (arr == null) arr = new ArrayList<String>(); if (arr.contains(str)) return; arr.add(str); setArrayListPref(optionsKey, arr); } /** * Set the value of a preference * * @param key * @param value */ public void setArrayListPref(PrefKey key, ArrayList<String> values) { String pref = key.getValue(); String arrAsStr = ""; // support removing via set(null) if (values == null) { prefs.remove(pref); } else { for (String value : values) { arrAsStr += value + ARRAY_DELIMITER; } prefs.setProperty(pref, arrAsStr); } save(); firePrefChanged(pref); } /** * Method for getting the boolean value of a preference * @param key * @param deflt * @return */ public Boolean getBooleanPref(PrefKey key, Boolean deflt) { String value = prefs.getProperty(key.getValue()); if (value == null) return deflt; return Boolean.parseBoolean(value); } /** * Get the specified preference as an int * @param key * @param deflt * @return */ public int getIntPref(PrefKey key, int deflt) { String value = prefs.getProperty(key.getValue()); if (value == null) return deflt; try { return Integer.parseInt(value); } catch (NumberFormatException nfe) { log.warn("Invalid integer for preference '" + key.getValue() + "': " + value); return deflt; } } /** * Get the requested preference as a font * * @param key * @param deflt * @return */ public Font getFontPref(PrefKey key, Font deflt) { String value = prefs.getProperty(key.getValue()); if (value == null) return deflt; return Font.decode(value); } /** * Get the requested preference as a Dimension * * @param key * @param deflt * @return */ public Dimension getDimensionPref(PrefKey key, Dimension deflt) { String value = prefs.getProperty(key.getValue()); if (value == null) return deflt; StringTokenizer st = new StringTokenizer(value, ","); Dimension d = new Dimension(deflt); if (st.hasMoreTokens()) { String s = st.nextToken(); try { int i = Integer.parseInt(s); if (i > 0) d.width = i; } catch (NumberFormatException nfe) { log.warn("Invalid dimension width: " + s); } } if (st.hasMoreTokens()) { String s = st.nextToken(); try { int i = Integer.parseInt(s); if (i > 0) d.height = i; } catch (NumberFormatException nfe) { log.warn("Invalid dimension height: " + s); } } return d; } /** * Remove a preference * * @param key */ public void removePref(PrefKey key) { prefs.remove(key.getValue()); save(); firePrefChanged(key.getValue()); } /** * Remove a preference * * @param pref */ @Deprecated public void removePref(String pref) { prefs.remove(pref); save(); firePrefChanged(pref); } private WeakEventListenerList listeners = new WeakEventListenerList(); public void addPrefsListener(PrefsListener l) { listeners.add(PrefsListener.class, l); } public void removePrefsListener(PrefsListener l) { listeners.remove(PrefsListener.class, l); } public void firePrefChanged(String pref) { // alert all listeners Object[] l = listeners.getListenerList(); PrefsEvent e = new PrefsEvent(Prefs.class, pref); for (int i = 0; i < l.length; i += 2) { if (l[i] == PrefsListener.class) ((PrefsListener) l[i + 1]).prefChanged(e); } } /** * Code a font into a string * * @param f * @return */ public static final String stringifyFont(Font f) { StringBuffer sb = new StringBuffer(); sb.append(f.getFontName()); sb.append('-'); int s = sb.length(); if ((f.getStyle() & Font.BOLD) != 0) { sb.append("BOLD"); } if ((f.getStyle() & Font.ITALIC) != 0) { sb.append("ITALIC"); } if (sb.length() > s) { sb.append('-'); } sb.append(f.getSize()); return sb.toString(); } /** * DEPRECATED METHODS */ /** * Use setPref(PrefKey, ...) instead * @param pref * @param value */ @Deprecated public void setBooleanPref(String pref, boolean value) { setPref(pref, Boolean.toString(value)); } /** * Set the value of a preference to the specified double * @param pref * @param value */ @Deprecated public void setDoublePref(String pref, Double value) { setPref(pref, Double.toString(value)); } /** * Use getPref(PrefKey, ...) instead * * @param pref * @param deflt * @return */ @Deprecated public Double getDoublePref(String pref, Double deflt) { String value = prefs.getProperty(pref); if (value == null) return deflt; return Double.parseDouble(value); } /** * Use getPref(PrefKey, ...) instead * * @param pref * @param deflt * @return */ @Deprecated public boolean getBooleanPref(String pref, boolean deflt) { String value = prefs.getProperty(pref); if (value == null) return deflt; return Boolean.parseBoolean(value); } /** * Use setPref(PrefKey, ...) instead * @param pref * @param value */ @Deprecated public void setDimensionPref(String pref, Dimension value) { setPref(pref, value.width + "," + value.height); } /** * Use getPref(PrefKey, ...) instead * * @param pref * @param deflt * @return */ @Deprecated public Dimension getDimensionPref(String pref, Dimension deflt) { String value = prefs.getProperty(pref); if (value == null) return deflt; StringTokenizer st = new StringTokenizer(value, ","); Dimension d = new Dimension(deflt); if (st.hasMoreTokens()) { String s = st.nextToken(); try { int i = Integer.parseInt(s); if (i > 0) d.width = i; } catch (NumberFormatException nfe) { log.warn("Invalid dimension width: " + s); } } if (st.hasMoreTokens()) { String s = st.nextToken(); try { int i = Integer.parseInt(s); if (i > 0) d.height = i; } catch (NumberFormatException nfe) { log.warn("Invalid dimension height: " + s); } } return d; } /** * Use setPref(PrefKey, ...) instead * @param pref * @param value */ @Deprecated public void setIntPref(String pref, int value) { setPref(pref, Integer.toString(value)); } /** * Use getPref(PrefKey, ...) instead * * @param pref * @param deflt * @return */ @Deprecated public int getIntPref(String pref, int deflt) { String value = prefs.getProperty(pref); if (value == null) return deflt; try { return Integer.parseInt(value); } catch (NumberFormatException nfe) { log.warn("Invalid integer for preference '" + pref + "': " + value); return deflt; } } /** * Use setPref(PrefKey, ...) instead * @param pref * @param value */ @Deprecated public void setColorPref(String pref, Color value) { String encoded = "#" + Integer.toHexString(value.getRGB() & 0x00ffffff); setPref(pref, encoded); } /** * Use getPref(PrefKey, ...) instead * * @param pref * @param deflt * @return */ @Deprecated public Color getColorPref(String pref, Color deflt) { String value = prefs.getProperty(pref); if (value == null) return deflt; try { return Color.decode(value); } catch (NumberFormatException nfe) { log.warn("Invalid color for preference '" + pref + "': " + value); return deflt; } } /** * Use setPref(PrefKey, ...) instead * @param pref * @param value */ @Deprecated public void setFontPref(String pref, Font value) { setPref(pref, stringifyFont(value)); } /** * Use getPref(PrefKey, ...) instead * * @param pref * @param deflt * @return */ @Deprecated public Font getFontPref(String pref, Font deflt) { String value = prefs.getProperty(pref); if (value == null) return deflt; return Font.decode(value); } /** * Use getPref(PrefKey, ...) instead * @param pref * @return */ @Deprecated public String getPref(String pref) { return prefs.getProperty(pref); } /** * Get a preference result as an enum * * @param pref * @param deflt * @param enumType * @return */ @Deprecated public <T extends Enum<T>> T getEnumPref(String pref, T deflt, Class<T> enumType) { String val = getPref(pref, null); if (val == null) return deflt; try { return Enum.valueOf(enumType, val); } catch (Exception e) { return deflt; } } /** * Use getPref(PrefKey, String) instead * * @param pref * @param deflt * @return */ @Deprecated public String getPref(String pref, String deflt) { String value = prefs.getProperty(pref); if (value == null) value = deflt; return value; } @Deprecated public void setPref(String pref, String value) { // support removing via set(null) if (value == null) { prefs.remove(pref); } // Trim spaces from webservice URL else if (pref.equals("tellervo.webservice.url")) { prefs.setProperty(pref, value.trim()); } else { prefs.setProperty(pref, value); } save(); firePrefChanged(pref); } }