/* * jMemorize - Learning made easy (and fun) - A Leitner flashcards tool * Copyright(C) 2004-2008 Riad Djemili and contributors * * 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 1, 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package jmemorize.core; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; import java.awt.Point; import java.io.File; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.prefs.Preferences; import javax.swing.JFrame; import jmemorize.core.learn.LearnSettings; import jmemorize.core.learn.LearnSettings.SchedulePreset; import jmemorize.gui.Localization; import jmemorize.gui.swing.CardFont; import jmemorize.gui.swing.CardFont.FontAlignment; import jmemorize.gui.swing.CardFont.FontType; import jmemorize.gui.swing.frames.MainFrame; import jmemorize.util.PreferencesTool; /** * Stores all jMemorize settings by using a preferences node. Use storeXXX to * save settings and loadXXX to load them again. * * @author djemili */ public class Settings { /** * Listens for changes to the currently set card font which can be set by * using the preferences window. */ public interface CardFontObserver { public void fontChanged(FontType type, CardFont font); } // TODO move all user prefs here public final static Preferences PREFS = Main.USER_PREFS; // observers private static List<CardFontObserver> m_cardFontObservers = new LinkedList<CardFontObserver>(); // locale keys private final static String LOCALE_LANG = "locale-lang"; //$NON-NLS-1$ private final static String LOCALE_COUNTRY = "locale-country"; //$NON-NLS-1$ // font keys private final static String FONT_FRONT_KEY = "front"; //$NON-NLS-1$ private final static String FONT_FLIP_KEY = "flip"; //$NON-NLS-1$ private final static String FONT_LEARN_FRONT_KEY = "learn-front"; //$NON-NLS-1$ private final static String FONT_LEARN_FLIP_KEY = "learn-flip"; //$NON-NLS-1$ private final static String FONT_TABLE_FRONT_KEY = "table-front"; //$NON-NLS-1$ private final static String FONT_TABLE_FLIP_KEY = "table-flip"; //$NON-NLS-1$ // strategy keys private final static String LIMIT_CARDS_ENABLED = "limit.cards.enabled"; //$NON-NLS-1$ private final static String LIMIT_TIME_ENABLED = "limit.time.enabled"; //$NON-NLS-1$ private final static String LIMIT_CARDS = "limit.cards"; //$NON-NLS-1$ private final static String LIMIT_TIME = "limit.time"; //$NON-NLS-1$ private final static String RETEST_FAILED_CARDS = "retest-failed-cards"; //$NON-NLS-1$ private final static String SCHEDULE_PRESET = "schedule.preset"; //$NON-NLS-1$ private final static String SCHEDULE = "schedule.values"; //$NON-NLS-1$ private final static String SCHEDULE_FIXED_EXPIRATION_ENABLED = "schedule.fixed-expiration.enabled"; //$NON-NLS-1$ private final static String SCHEDULE_FIXED_EXPIRATION_HOUR = "schedule.fixed-expiration.hour"; //$NON-NLS-1$ private final static String SCHEDULE_FIXED_EXPIRATION_MINUTE = "schedule.fixed-expiration.minute"; //$NON-NLS-1$ private final static String SIDES = "sides"; //$NON-NLS-1$ private final static String SIDES_FRONT_AMOUNT = "sides-front-amount"; //$NON-NLS-1$ private final static String SIDES_FLIP_AMOUNT = "sides-flip-amount"; //$NON-NLS-1$ private final static String GROUP_BY_CATEGORY = "card-order.group-by-category"; //$NON-NLS-1$ private final static String CATEGORY_ORDER = "card-order.group-by-category.order"; //$NON-NLS-1$ private final static String SHUFFLE_CARDS = "card-order.shuffle"; //$NON-NLS-1$ // gui private final static String FRAME_MAXIMIZED = "frame.maximized"; //$NON-NLS-1$ private final static String FRAME_POSITION = "frame.position"; //$NON-NLS-1$ private final static String FRAME_SIZE = "frame.size"; //$NON-NLS-1$ // etc keys private final static String LAST_DIRECTORY = "last-directory"; //$NON-NLS-1$ private final static String SAVE_COMPRESSED = "gzip"; //$NON-NLS-1$ private final static String CATEGORY_TREE_WIDTH = "category-tree.width"; //$NON-NLS-1$ private final static String CATEGORY_TREE_VISIBLE = "category-tree.visible"; //$NON-NLS-1$ private final static String MAIN_DIVIDER_LOCATION = "main-divider.location"; //$NON-NLS-1$ public static void storeLocale(Locale locale) { PREFS.put(LOCALE_LANG, locale.getLanguage()); PREFS.put(LOCALE_COUNTRY, locale.getCountry()); } /** * @return The locale stored in preferences or the default locale if no * locale was stored. */ public static Locale loadLocale() { String lang = PREFS.get(LOCALE_LANG, null); if (lang == null) { return Localization.getDefaultLocale(); } String country = PREFS.get(LOCALE_COUNTRY, null); return country != null ? new Locale(lang, country) : new Locale(lang); } public static void storeFont(FontType type, CardFont font) { String key = toFontString(type); PREFS.put(toNameKey(key), font.getFont().getFamily()); PREFS.putInt(toSizeKey(key), font.getFont().getSize()); PREFS.put(toAlignmentKey(key), font.getAlignment().toString()); PREFS.putBoolean(toVerticalAlignmentKey(key), font.isVerticallyCentered()); for (CardFontObserver observer : m_cardFontObservers) { observer.fontChanged(type, font); } } public static CardFont loadFont(FontType type) { String key = toFontString(type); String name = PREFS.get(toNameKey(key), null); int size = PREFS.getInt(toSizeKey(key), 12); String align = PREFS.get(toAlignmentKey(key), null); boolean valign = PREFS.getBoolean(toVerticalAlignmentKey(key), false); Font font = new Font(name, Font.PLAIN, size); FontAlignment alignment = FontAlignment.LEFT; try { alignment = FontAlignment.valueOf(align); } catch (Exception e) { // Main.logThrowable("failed to load font alignment.", e); } return new CardFont(font, alignment, valign); } public static void setCardFont(CardFontObserver observer, boolean flipped, FontType type1, FontType type2) { CardFont frontFont = loadFont(type1); CardFont flipFont = loadFont(type2); observer.fontChanged(type1, !flipped ? frontFont : flipFont); observer.fontChanged(type2, !flipped ? flipFont : frontFont); } public static void addCardFontObserver(CardFontObserver observer) { m_cardFontObservers.add(observer); } public static void setCardFont(CardFontObserver observer, FontType type1, FontType type2) { setCardFont(observer, false, type1, type2); } // TODO rename public static void removedCardFontObserver(CardFontObserver observer) { m_cardFontObservers.remove(observer); } public static void storeStrategy(LearnSettings strategy) { PREFS.putBoolean(LIMIT_CARDS_ENABLED, strategy.isCardLimitEnabled()); PREFS.putInt(LIMIT_CARDS, strategy.getCardLimit()); PREFS.putBoolean(LIMIT_TIME_ENABLED, strategy.isTimeLimitEnabled()); PREFS.putInt(LIMIT_TIME, strategy.getTimeLimit()); PREFS.putBoolean(RETEST_FAILED_CARDS, strategy.isRetestFailedCards()); PREFS.putFloat(SHUFFLE_CARDS, strategy.getShuffleRatio()); PREFS.putInt(SIDES, strategy.getSidesMode()); PREFS.putInt(SIDES_FRONT_AMOUNT, strategy.getAmountToTest(true)); PREFS.putInt(SIDES_FLIP_AMOUNT, strategy.getAmountToTest(false)); PREFS.putInt(SCHEDULE_PRESET, strategy.getSchedulePreset().ordinal()); PreferencesTool.putIntArray(PREFS, SCHEDULE, strategy.getSchedule()); PREFS.putBoolean(SCHEDULE_FIXED_EXPIRATION_ENABLED, strategy.isFixedExpirationTimeEnabled()); PREFS.putInt(SCHEDULE_FIXED_EXPIRATION_HOUR, strategy.getFixedExpirationHour()); PREFS.putInt(SCHEDULE_FIXED_EXPIRATION_MINUTE, strategy.getFixedExpirationMinute()); PREFS.putBoolean(GROUP_BY_CATEGORY, strategy.isGroupByCategory()); PREFS.putInt(CATEGORY_ORDER, strategy.getCategoryOrder()); } /** * @return A newly created strategy that got instantiated with all the * stored strategy data or with default values. */ public static LearnSettings loadStrategy(MainFrame frame) { LearnSettings settings = new LearnSettings(); settings.setCardLimitEnabled(PREFS.getBoolean(LIMIT_CARDS_ENABLED, false)); settings.setCardLimit(PREFS.getInt(LIMIT_CARDS, 20)); settings.setTimeLimitEnabled(PREFS.getBoolean(LIMIT_TIME_ENABLED, true)); settings.setTimeLimit(PREFS.getInt(LIMIT_TIME, 20)); settings.setRetestFailedCards(PREFS.getBoolean(RETEST_FAILED_CARDS, true)); settings.setSidesMode(PREFS.getInt(SIDES, LearnSettings.SIDES_NORMAL)); settings.setAmountToTest(true, PREFS.getInt(SIDES_FRONT_AMOUNT, 1)); settings.setAmountToTest(false, PREFS.getInt(SIDES_FLIP_AMOUNT, 1)); int preset = PREFS.getInt(SCHEDULE_PRESET, 1); //linear as default // if preconfigured schedule if (preset > -1 && preset < SchedulePreset.values().length - 1) { settings.setSchedulePreset(SchedulePreset.values()[preset]); } else // if custom { int[] schedule = PreferencesTool.getIntArray(PREFS, SCHEDULE); if (schedule != null) settings.setCustomSchedule(schedule); } settings.setFixedExpirationTimeEnabled(PREFS.getBoolean( SCHEDULE_FIXED_EXPIRATION_ENABLED, false)); int hour = PREFS.getInt(SCHEDULE_FIXED_EXPIRATION_HOUR, 3); int minute = PREFS.getInt(SCHEDULE_FIXED_EXPIRATION_MINUTE, 0); settings.setFixedExpirationTime(hour, minute); settings.setGroupByCategory(PREFS.getBoolean(GROUP_BY_CATEGORY, true)); settings.setCategoryOrder(PREFS.getInt(CATEGORY_ORDER, LearnSettings.CATEGORY_ORDER_FIXED)); settings.setShuffleRatio(PREFS.getFloat(SHUFFLE_CARDS, 0.3f)); return settings; } public static void storeSaveCompressed(boolean saveCompressed) { PREFS.putBoolean(SAVE_COMPRESSED, saveCompressed); } /** * @return <code>true</code> if lesson files should be compressed with * GZIP when saving. */ public static boolean loadIsSaveCompressed() { return PREFS.getBoolean(SAVE_COMPRESSED, true); } // TODO merge storeCategoryTreeWidth and storeCategoryTreeVisible public static void storeCategoryTreeWidth(int width) { PREFS.putInt(CATEGORY_TREE_WIDTH, width); } public static int loadCategoryTreeWidth() { return PREFS.getInt(CATEGORY_TREE_WIDTH, 150); } public static void storeCategoryTreeVisible(boolean visible) { PREFS.putBoolean(CATEGORY_TREE_VISIBLE, visible); } public static boolean loadCategoryTreeVisible() { return PREFS.getBoolean(CATEGORY_TREE_VISIBLE, false); } public static void storeMainDividerLocation(int size) { PREFS.putInt(MAIN_DIVIDER_LOCATION, size); } public static int loadMainDividerLocation() { return PREFS.getInt(MAIN_DIVIDER_LOCATION, 250); } public static void storeLastDirectory(File directory) { PREFS.put(LAST_DIRECTORY, directory.getAbsolutePath()); } public static File loadLastDirectory() { String directory = PREFS.get(LAST_DIRECTORY, null); if (directory != null) { File file = new File(directory); if (file.exists()) return file; } return null; } public static void loadFrameState(JFrame frame, String frameId) { if (Settings.loadIsFrameMaximized(frameId)) { frame.setExtendedState(Frame.MAXIMIZED_BOTH); } else { Point framePosition = Settings.loadFramePosition(frameId); if (framePosition != null) { frame.setLocation(framePosition); } else { frame.setLocationRelativeTo(null); } Dimension frameSize = Settings.loadFrameSize(frameId); if (frameSize != null) frame.setSize(frameSize); } } public static void storeFrameState(JFrame frame, String frameId) { Settings.storeIsFrameMaximized(frameId, frame.getExtendedState() == Frame.MAXIMIZED_BOTH); Settings.storeFramePosition(frameId, frame.getLocation()); Settings.storeFrameSize(frameId, frame.getSize()); } /** * @param frameId an identifier for the frame. * * @return <code>true</code> if the main frame should be displayed as * maximized at program start. */ private static boolean loadIsFrameMaximized(String frameId) { return PREFS.getBoolean(frameId + '.' + FRAME_MAXIMIZED, false); } /** * @see #loadIsFrameMaximized() */ private static void storeIsFrameMaximized(String frameId, boolean maximized) { PREFS.putBoolean(frameId + '.' + FRAME_MAXIMIZED, maximized); } /** * @param frameId an identifier for the frame. * * @return the position of the main frame. <code>null</code> if no * position is stored. */ private static Point loadFramePosition(String frameId) { String str = PREFS.get(frameId + '.' + FRAME_POSITION, null); if (str == null) return null; String[] pos = str.split(","); return new Point(Integer.parseInt(pos[0]), Integer.parseInt(pos[1])); } /** * @see #loadFramePosition() */ private static void storeFramePosition(String frameId, Point position) { PREFS.put(frameId + '.' + FRAME_POSITION, position.x + "," + position.y); } /** * @param frameId an identifier for the frame. * * @return the size of the main frame. <code>null</code> if no size is * stored. */ private static Dimension loadFrameSize(String frameId) { String str = PREFS.get(frameId + '.' + FRAME_SIZE, null); if (str == null) return null; String[] pos = str.split(","); return new Dimension(Integer.parseInt(pos[0]), Integer.parseInt(pos[1])); } /** * @see #loadFrameSize() */ private static void storeFrameSize(String frameId, Dimension size) { PREFS.put(frameId + '.' + FRAME_SIZE, (int)size.getWidth() + "," + (int)size.getHeight()); } private static String toSizeKey(String key) { return key + "-font-size"; } private static String toNameKey(String key) { return key + "-font-name"; } private static String toAlignmentKey(String key) { return key + "-font-alignment"; } private static String toVerticalAlignmentKey(String key) { return key + "-font-valignment"; } private static String toFontString(FontType type) { switch (type) { case CARD_FRONT: return FONT_FRONT_KEY; case CARD_FLIP: return FONT_FLIP_KEY; case LEARN_FRONT: return FONT_LEARN_FRONT_KEY; case LEARN_FLIP: return FONT_LEARN_FLIP_KEY; case TABLE_FRONT: return FONT_TABLE_FRONT_KEY; case TABLE_FLIP: return FONT_TABLE_FLIP_KEY; default: throw new IllegalArgumentException("Unknown font identifier"); } } }