/* GeoGebra - Dynamic Mathematics for Everyone http://www.geogebra.org This file is part of GeoGebra. 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. */ package org.geogebra.desktop.main; import java.io.ByteArrayOutputStream; import java.io.File; import java.util.Locale; import java.util.prefs.Preferences; import org.geogebra.common.GeoGebraConstants; import org.geogebra.common.euclidian3D.Input3DConstants; import org.geogebra.common.main.App; import org.geogebra.common.main.Feature; import org.geogebra.common.main.GeoGebraPreferences; import org.geogebra.common.main.GeoGebraPreferencesXML; import org.geogebra.common.util.debug.Log; import org.geogebra.desktop.util.UtilD; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * Stores user settings and options as preferences. * * @author Markus Hohenwarter * @date May 16, 2007 */ /* * Additions by Hans-Petter Ulven 6 Mars, 2010 Subclass * GeoGebraPortablePreferences, for saving prefs to propertyfile Added some * constants Small rewrite of getPref() to return subclass singleton instead of * this.singleton 7mars: Addition of setPropertyFile(filename) to fascilitate * cmdline option --settingsFile (Set in line 263 geogebra.gui.app.GeoGebraFrame * before getPref() is called first time.) */ public class GeoGebraPreferencesD extends GeoGebraPreferences { // Windows -> APPDATA, space in "GeoGebra 5.0" // Mac / Linux -> user.home, hidden folder, no space in ".GeoGebra5.0" public static final String PREFS_PATH = AppD.WINDOWS ? (System.getenv("APPDATA") + "/GeoGebra " + GeoGebraConstants.SHORT_VERSION_STRING + "/prefs/") : (System.getProperty("user.home") + "/.GeoGebra" + GeoGebraConstants.SHORT_VERSION_STRING + "/prefs/"); public static final String WINDOWS_USERS_PREFS = PREFS_PATH + "prefs.xml"; public static final String WINDOWS_OBJECTS_PREFS = PREFS_PATH + "defaults.xml"; public static final String WINDOWS_MACROS_PREFS = PREFS_PATH + "macros.ggt"; public static final String AUTHOR = "author"; public static final String VERSION = "version"; public static final String VERSION_LAST_CHECK = "version_last_check"; /** * Allow checking of availability of a newer version */ public static final String VERSION_CHECK_ALLOW = "version_check_allow"; /** * save what kind of 3D input we use (if one) */ public static final String INPUT_3D = "input_3d"; // worksheet export dialog public static final String EXPORT_WS_RIGHT_CLICK = "export_ws_right_click"; public static final String EXPORT_WS_LABEL_DRAGS = "export_ws_label_drags"; public static final String EXPORT_WS_RESET_ICON = "export_ws_reset_icon"; // public static final String EXPORT_WS_FRAME_POSSIBLE = // "export_ws_frame_possible"; public static final String EXPORT_WS_SHOW_MENUBAR = "export_ws_show_menubar"; public static final String EXPORT_WS_SHOW_TOOLBAR = "export_ws_show_toolbar"; public static final String EXPORT_WS_SHOW_TOOLBAR_HELP = "export_ws_show_toolbar_help"; public static final String EXPORT_WS_SHOW_INPUT_FIELD = "export_ws_show_input_field"; public static final String EXPORT_WS_OFFLINE_ARCHIVE = "export_ws_offline_archive"; // public static final String EXPORT_WS_GGB_FILE = "export_ws_ggb_file"; public static final String EXPORT_WS_SAVE_PRINT = "export_ws_save_print"; public static final String EXPORT_WS_USE_BROWSER_FOR_JAVASCRIPT = "export_ws_browser_for_js"; public static final String EXPORT_WS_INCLUDE_HTML5 = "export_ws_include_html5"; public static final String EXPORT_WS_ALLOW_RESCALING = "export_ws_allow_rescaling"; public static final String EXPORT_WS_REMOVE_LINEBREAKS = "export_ws_remove_linebreaks"; // public static final String EXPORT_WS_BUTTON_TO_OPEN = // "export_ws_button_to_open"; // picture export dialog public static final String EXPORT_PIC_FORMAT = "export_pic_format"; public static final String EXPORT_PIC_DPI = "export_pic_dpi"; // public final String EXPORT_PIC_SCALE = "export_pic_scale"; // print preview dialog public static final String PRINT_ORIENTATION = "print_orientation"; public static final String PRINT_SHOW_SCALE = "print_show_scale"; public static final String PRINT_SHOW_SCALE2 = "print_show_scale_2"; // misc public static final String MISC_REVERSE_MOUSE_WHEEL = "misc_reverse_mouse_wheel"; // user data public static final String USER_LOGIN_TOKEN = "user_login_token"; public static final String USER_LOGINNAME = "user_login_name"; public static final String USER_LOGIN_SKIP = "user_login_skip"; // preferences node name for GeoGebra private Preferences ggbPrefs, ggbPrefsSystem; protected GeoGebraPreferencesD() { try { if (PROPERTY_FILEPATH == null) { ggbPrefs = Preferences.userRoot() .node(GeoGebraConstants.PREFERENCES_ROOT); } } catch (Exception e) { // thrown when running unsigned JAR ggbPrefs = null; } try { if (PROPERTY_FILEPATH == null && Preferences.systemRoot() .nodeExists(GeoGebraConstants.PREFERENCES_ROOT_GLOBAL)) { ggbPrefsSystem = Preferences.systemRoot() .node(GeoGebraConstants.PREFERENCES_ROOT_GLOBAL); // System.out.println("system preference // "+GeoGebraConstants.PREFERENCES_ROOT_GLOBAL+ // " exists"); } else { ggbPrefsSystem = null; // System.out.println("system preference // "+GeoGebraConstants.PREFERENCES_ROOT_GLOBAL+ // " does not exist"); } } catch (Exception e) { // thrown when running unsigned JAR ggbPrefsSystem = null; // System.out.println("Error : system preference // "+GeoGebraConstants.PREFERENCES_ROOT_GLOBAL); } } // Ulven: changed to make available to subclass GeoGebraPortablePreferences protected String factoryDefaultXml; // see loadPreferences() protected static final String XML_FACTORY_DEFAULT = "xml_factory_default"; protected static final String TOOLS_FILE_GGT = "tools_file_ggt"; protected static final String APP_LOCALE = "app_locale"; protected static final String APP_CURRENT_IMAGE_PATH = "app_current_image_path"; protected static final String APP_FILE_ = "app_file_"; /* Ulven 06.03.10 */ private static String PROPERTY_FILEPATH = null; // full path, null: no // property file set private static GeoGebraPreferencesD singleton; /* Set in geogebra.gui.app.GeoGebraFrame before first call to getPref() */ public static void setPropertyFileName(String pfname) { PROPERTY_FILEPATH = pfname; Log.debug("Prferences in: " + PROPERTY_FILEPATH); }// setPropertyFileName(String) public synchronized static GeoGebraPreferencesD getPref() { /* * --- New code 06.03.10 - Ulven Singleton getInstance() method Checks * if PROPERTY_FILENAME is given (by commandline) and returns subclass * GeoGebraPortablePrefrences if it is, otherwise as original * * @author H-P Ulven * * @version 2010-03-07 */ if (singleton == null) { if (PROPERTY_FILEPATH != null) { // Application.debug(PROPERTY_FILENAME); singleton = GeoGebraPortablePreferences.getPref(); } // if (else leave it to original) } // if // --- New code end if (singleton == null) { singleton = new GeoGebraPreferencesD(); } return singleton; }// getPref(); public String loadPreference(String key, String defaultValue) { return ggbPrefs.get(key, defaultValue); } public void savePreference(String key, String value) { if (key != null && value != null) { ggbPrefs.put(key, value); } } /** * Check if system (local machine), then user, allows check version * * @param defaultValue * default value (if key doesn't exist) * @return true if system and user allows check version */ public boolean loadVersionCheckAllow(String defaultValue) { // check if system (local machine) allows check version boolean systemAllows; if (ggbPrefsSystem == null) { systemAllows = true; Log.info("No system preferences"); } else { systemAllows = Boolean.valueOf(ggbPrefsSystem.get( GeoGebraPreferencesD.VERSION_CHECK_ALLOW, defaultValue)); } // then check if user allows if (systemAllows && ggbPrefs != null) { return Boolean.valueOf(getPref().loadPreference( GeoGebraPreferencesD.VERSION_CHECK_ALLOW, defaultValue)); } // else don't allow return false; } /** * save "versionCheckAllow" value to users preferences * * @param value * value */ public void saveVersionCheckAllow(String value) { getPref().savePreference(GeoGebraPreferencesD.VERSION_CHECK_ALLOW, value); } /** * set 3D input used * * @param type * type */ public void setInput3DType(String type) { getPref().savePreference(GeoGebraPreferencesD.INPUT_3D, type); } /** * * @return 3D input type currently used, "none" if none */ public String getInput3DType() { return getPref().loadPreference(GeoGebraPreferencesD.INPUT_3D, Input3DConstants.PREFS_NONE); } /** * Returns the path of the first file in the file list */ public File getDefaultFilePath() { File file = new File(getPref().loadPreference(APP_FILE_ + "1", "")); if (file.exists()) { return file.getParentFile(); } return null; } /** * Returns the default image path * * @return the image path */ public File getDefaultImagePath() { // image path String pathName = getPref().loadPreference(APP_CURRENT_IMAGE_PATH, null); if (pathName != null) { return new File(pathName); } return null; } /** * Saves the currently set locale. */ public void saveDefaultImagePath(File imgPath) { try { if (imgPath != null) { getPref().savePreference(APP_CURRENT_IMAGE_PATH, imgPath.getCanonicalPath()); } } catch (Exception e) { e.printStackTrace(); } } /** * Returns the default locale * * @return the locale */ public Locale getDefaultLocale() { // language String strLocale = getPref().loadPreference(APP_LOCALE, null); if (strLocale != null) { return AppD.getLocale(strLocale); } return null; } /** * Saves the currently set locale. */ public void saveDefaultLocale(Locale locale) { // save locale (language) getPref().savePreference(APP_LOCALE, locale.toString()); } /** * Loads the names of the eight last used files from the preferences backing * store. */ public void loadFileList() { // load last eight files for (int i = AppD.MAX_RECENT_FILES; i >= 1; i--) { File file = new File(getPref().loadPreference(APP_FILE_ + i, "")); AppD.addToFileList(file); } } /** * Saves the names of the eight last used files. */ public void saveFileList() { try { // save last four files for (int i = 1; i <= AppD.MAX_RECENT_FILES; i++) { File file = AppD.getFromFileList(i - 1); if (file != null) { getPref().savePreference(APP_FILE_ + i, file.getCanonicalPath()); } else { getPref().savePreference(APP_FILE_ + i, ""); } } } catch (Exception e) { e.printStackTrace(); } } /** * Inits factory default XML if there are no old preferences or if the * version number changed. The default XML is the preferences XML of this * virgin application. */ public void initDefaultXML(AppD app) { // already initialized? if (factoryDefaultXml != null) { return; } // when applet unsigned this may be null if (ggbPrefs != null) { // get the GeoGebra version with which the preferences were saved // (the version number is stored since version 3.9.41) String oldVersion = getPref().loadPreference(VERSION, null); // current factory defaults possibly available? if (oldVersion != null && oldVersion.equals(GeoGebraConstants.VERSION_STRING)) { factoryDefaultXml = getPref() .loadPreference(XML_FACTORY_DEFAULT, null); } } // if this is an old version or the factory defaults were not saved in // the // preferences for some reasons, create and store them now (plus: store // version string) if (factoryDefaultXml == null) { factoryDefaultXml = getDefaultPreferences(app); if (ggbPrefs != null) { ggbPrefs.put(XML_FACTORY_DEFAULT, factoryDefaultXml); ggbPrefs.put(VERSION, GeoGebraConstants.VERSION_STRING); } } } /** * Saves preferences by taking the application's current values. */ @SuppressFBWarnings({ "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", "don't need to check return value of mkdirs()" }) public void saveXMLPreferences(AppD app) { String userPrefsXML = app.getPreferencesXML(); StringBuilder sb2d = new StringBuilder(); StringBuilder sb3d = new StringBuilder(); app.getKernel().getConstruction().getConstructionDefaults() .getDefaultsXML(sb2d, sb3d); String objectPrefsXML = sb2d.toString(); byte[] macros = app.getMacroFileAsByteArray(); if (app.has(Feature.SAVE_SETTINGS_TO_FILE)) { // make sure folder exists new File(PREFS_PATH).mkdirs(); UtilD.writeStringToFile(userPrefsXML, WINDOWS_USERS_PREFS); UtilD.writeStringToFile(objectPrefsXML, WINDOWS_OBJECTS_PREFS); UtilD.writeByteArrayToFile(macros, WINDOWS_MACROS_PREFS); return; } ggbPrefs.put(XML_USER_PREFERENCES, userPrefsXML); try { getPref().savePreference(XML_DEFAULT_OBJECT_PREFERENCES, objectPrefsXML); } catch (Exception e) { e.printStackTrace(); Log.error("object defaults too long"); } // store current tools including icon images as ggt file (byte array) putByteArray(TOOLS_FILE_GGT, app.getMacroFileAsByteArray()); try { ggbPrefs.flush(); } catch (Exception e) { Log.debug(e + ""); } } /** * Breaks up byte array value into pieces and calls * prefs.putByteArray(prefs, key+k, piece_k) for every piece. */ private void putByteArray(String key, byte[] value) { // byte array must not be longer than 3/4 of max value length int max_length = (int) Math.floor(Preferences.MAX_VALUE_LENGTH * 0.75); // value array is small enough if (value == null || value.length < max_length) { ggbPrefs.putByteArray(key, value); // remove possible old part keys int partCount = 0; while (true) { byte[] temp = ggbPrefs.getByteArray(key + partCount, null); if (temp != null) { ggbPrefs.remove(key + partCount); partCount++; } else { break; } } } // break value array up into smaller pieces else { // delete key value ggbPrefs.remove(key); byte[] bytePart = new byte[max_length]; int pos = 0; int partCount = 0; while (pos + max_length <= value.length) { for (int k = 0; k < max_length; k++, pos++) { bytePart[k] = value[pos]; } // put piece key + partCount partCount++; ggbPrefs.putByteArray(key + partCount, bytePart); } // write last part if (pos < value.length) { bytePart = new byte[value.length - pos]; for (int k = 0; pos < value.length; k++, pos++) { bytePart[k] = value[pos]; } // put piece key + partCount partCount++; ggbPrefs.putByteArray(key + partCount, bytePart); } } try { ggbPrefs.flush(); } catch (Exception e) { e.printStackTrace(); } } /** * Breaks up byte array value into pieces and calls * prefs.putByteArray(prefs, key+k, piece_k) for every piece. */ private byte[] getByteArray(String key, byte[] def) { byte[] ret = ggbPrefs.getByteArray(key, null); if (ret != null) { // no parts: return byte array return ret; } try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); int partCount = 1; while (true) { ret = ggbPrefs.getByteArray(key + partCount, null); if (ret != null) { bos.write(ret); partCount++; } else { break; } } bos.flush(); if (bos.size() > 0) { ret = bos.toByteArray(); } } catch (Exception e) { e.printStackTrace(); ret = null; } if (ret != null) { return ret; } return def; } public String getXMLPreferences() { return getPref().loadPreference(XML_USER_PREFERENCES, factoryDefaultXml); } /** * Loads XML preferences (empty construction with GUI and kernel settings) * and sets application accordingly. This method clears the current * construction in the application. Note: the XML string used is the same as * for ggb files. */ public void loadXMLPreferences(AppD app) { app.setWaitCursor(); if (app.has(Feature.SAVE_SETTINGS_TO_FILE)) { Log.debug("Preferences loaded from " + WINDOWS_USERS_PREFS); String userPrefsXML = UtilD.loadFileIntoString(WINDOWS_USERS_PREFS); String objectPrefsXML = UtilD .loadFileIntoString(WINDOWS_OBJECTS_PREFS); byte[] ggtFile = UtilD.loadFileIntoByteArray(WINDOWS_MACROS_PREFS); if (ggtFile != null) { app.loadMacroFileFromByteArray(ggtFile, true); } if (userPrefsXML != null) { app.setXML(userPrefsXML, false); } else { app.setXML(factoryDefaultXml, false); } if (objectPrefsXML != null && !objectPrefsXML.equals(factoryDefaultXml)) { boolean eda = app.getKernel().getElementDefaultAllowed(); app.getKernel().setElementDefaultAllowed(true); app.getKernel().getConstruction().setIgnoringNewTypes(true); app.setXML(objectPrefsXML, false); app.getKernel().getConstruction().setIgnoringNewTypes(false); app.getKernel().setElementDefaultAllowed(eda); } app.updateToolBar(); app.setDefaultCursor(); return; } // load this preferences xml file in application try { // load tools from ggt file (byte array) byte[] ggtFile = getByteArray(TOOLS_FILE_GGT, null); app.loadMacroFileFromByteArray(ggtFile, true); // load preferences xml String xml = getPref().loadPreference(XML_USER_PREFERENCES, factoryDefaultXml); app.setXML(xml, false); // if (!(app instanceof Application3D)) // TODO: implement it in // Application3D! { String xmlDef = getPref().loadPreference( XML_DEFAULT_OBJECT_PREFERENCES, factoryDefaultXml); if (!xmlDef.equals(factoryDefaultXml)) { boolean eda = app.getKernel().getElementDefaultAllowed(); app.getKernel().setElementDefaultAllowed(true); app.setXML(xmlDef, false); app.getKernel().setElementDefaultAllowed(eda); } } // String xml = getPref().loadPreference(XML_USER_PREFERENCES, ""); // if("".equals(xml)) { // initDefaultXML(app); // xml = XML_GGB_FACTORY_DEFAULT; // } // app.setXML(xml, true); // app.setUndoActive(app.isUndoActive()); // eg. 3D macros may cause MyError app.updateToolBar(); } catch (Throwable e) { e.printStackTrace(); } app.setDefaultCursor(); } /** * Clears all user preferences. */ @SuppressFBWarnings({ "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", "don't need to check return value of delete()" }) public void clearPreferences(App app) { if (app.has(Feature.SAVE_SETTINGS_TO_FILE)) { try { new File(WINDOWS_OBJECTS_PREFS).delete(); new File(WINDOWS_USERS_PREFS).delete(); new File(WINDOWS_MACROS_PREFS).delete(); } catch (Exception e) { e.printStackTrace(); } return; } try { ggbPrefs.clear(); ggbPrefs.flush(); } catch (Exception e) { Log.debug(e + ""); } } /** * @return Default preferences */ private static String getDefaultPreferences(App app) { return GeoGebraPreferencesXML.getXML(app); } public static File getFile() { return new File(GeoGebraPreferencesD.PROPERTY_FILEPATH); } }