jmemorize.gui.swing.panels.LearnSettingPanels.java Source code

Java tutorial

Introduction

Here is the source code for jmemorize.gui.swing.panels.LearnSettingPanels.java

Source

/*
 * jMemorize - Learning made easy (and fun) - A Leitner flashcards tool
 * Copyright(C) 2004-2008 Riad Djemili
 * 
 * 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.gui.swing.panels;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;

import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.SpinnerDateModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import jmemorize.core.Card;
import jmemorize.core.Category;
import jmemorize.core.Main;
import jmemorize.core.learn.LearnSettings;
import jmemorize.core.learn.LearnSettings.SchedulePreset;
import jmemorize.gui.LC;
import jmemorize.gui.Localization;
import jmemorize.gui.swing.SelectionProvider;
import jmemorize.gui.swing.widgets.CategoryComboBox;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

/**
 * @author djemili
 */
public class LearnSettingPanels {
    // general panel
    private JSpinner m_cardLimitSpinner = new JSpinner(new SpinnerNumberModel(20, 1, 300, 1));
    private JSpinner m_timeLimitSpinner = new JSpinner(new SpinnerNumberModel(20, 1, 300, 1));
    private boolean m_updatingSpinners = false;

    // card list panel
    private CategoryComboBox m_categoryComboBox = new CategoryComboBox();
    private JRadioButton m_allCardsButton = new JRadioButton(
            Localization.get(LC.LEARN_SETTINGS_LEARN_UNLEARNED_EXPIRED), true);
    private JRadioButton m_unlearnedCardsButton = new JRadioButton(
            Localization.get(LC.LEARN_SETTINGS_LEARN_UNLEARNED));
    private JRadioButton m_expiredCardsButton = new JRadioButton(Localization.get(LC.LEARN_SETTINGS_LEARN_EXPIRED));
    private JRadioButton m_selectedCardsButton = new JRadioButton(
            Localization.get(LC.LEARN_SETTINGS_LEARN_SELECTED));

    // limiter panel
    private JCheckBox m_timeLimitCheckBox = new JCheckBox(Localization.get(LC.LEARN_SETTINGS_TIME_LIMIT_TEXT));
    private JCheckBox m_cardLimitCheckBox = new JCheckBox(Localization.get(LC.LEARN_SETTINGS_CARD_LIMIT_TEXT));
    private JCheckBox m_dontRetestCheckBox = new JCheckBox(Localization.get(LC.LEARN_SETTINGS_DONT_RETEST));

    // side mods panel
    private JRadioButton m_sidesNormalButton = new JRadioButton(Localization.get(LC.LEARN_SETTINGS_MODE_NORMAL));
    private JRadioButton m_sidesFlippedButton = new JRadioButton(Localization.get(LC.LEARN_SETTINGS_MODE_FLIP));
    private JRadioButton m_sidesRandomButton = new JRadioButton(Localization.get(LC.LEARN_SETTINGS_MODE_RANDOM));
    private JRadioButton m_sidesBothButton = new JRadioButton(Localization.get(LC.LEARN_SETTINGS_MODE_BOTH));

    /*
     * There must be at least one side that is checked before raising the level.
     * Without loss of generality, we can assume this will be the front, so
     * enforce at least one check for the front. If the user wants it the other
     * way, he/she should enter the card the opposite way around.
     */
    private JSpinner m_frontChecksAmountSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1));
    private JSpinner m_backChecksAmountSpinner = new JSpinner(new SpinnerNumberModel(1, 0, 100, 1));

    // card order panel
    private JCheckBox m_categoryGroupsCheckBox = new JCheckBox(Localization.get(LC.LEARN_SETTINGS_GROUP_CARDS));
    private JRadioButton m_categoryOrderFixedButton = new JRadioButton(
            Localization.get(LC.LEARN_SETTINGS_NATURAL_CATEGORY_ORDER));
    private JRadioButton m_categoryOrderRandomButton = new JRadioButton(
            Localization.get(LC.LEARN_SETTINGS_RANDOM_CATEGORY_ORDER));
    private JSlider m_shuffleRatioSlider = new JSlider(0, 100, 30);

    // schedule panel
    private JSpinner[] m_scheduleDays = new JSpinner[LearnSettings.SCHEDULE_LEVELS];
    private JSpinner[] m_scheduleHours = new JSpinner[LearnSettings.SCHEDULE_LEVELS];
    private JSpinner[] m_scheduleMinutes = new JSpinner[LearnSettings.SCHEDULE_LEVELS];
    private JComboBox m_schedulePresetsComboBox = new JComboBox(LearnSettings.SCHEDULE_PRESETS);

    private JCheckBox m_fixedExpirationTimeCheckBox = new JCheckBox(
            Localization.get(LC.LEARN_SETTINGS_FIXED_EXPIRATION_TIME));

    private JSpinner m_fixedExpirationTimeSpinner = new JSpinner();

    // other
    private LearnSettings m_settings;
    private boolean m_hasSelectedCards;

    public LearnSettingPanels() {
        for (int level = 0; level < LearnSettings.SCHEDULE_LEVELS; level++) {
            m_scheduleDays[level] = new JSpinner(new SpinnerNumberModel(1, 0, 999, 1));
            m_scheduleHours[level] = new JSpinner(new SpinnerNumberModel(1, 0, 23, 1));
            m_scheduleMinutes[level] = new JSpinner(new SpinnerNumberModel(1, 0, 59, 1));
        }

        m_categoryComboBox.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                categoryBoxActionPerformed();
            }
        });
    }

    /**
     * @param strategy The strategy that is shown and editted by the panels.
     */
    public void setStrategy(LearnSettings strategy) {
        m_settings = strategy;

        resetSettings();
    }

    public void setProvider(SelectionProvider provider) {
        Category rootCategory = Main.getInstance().getLesson().getRootCategory();

        m_categoryComboBox.setRootCategory(rootCategory);
        m_categoryComboBox.setSelectedCategory(provider.getCategory());

        List<Card> selected = provider.getSelectedCards();
        m_hasSelectedCards = selected != null && !selected.isEmpty();

        updateCardButtons();
    }

    /**
     * @return the currently selected category.
     */
    public Category getCategory() {
        return m_categoryComboBox.getSelectedCategory();
    }

    public boolean isLearnUnlearnedCards() {
        return m_allCardsButton.isSelected() || m_unlearnedCardsButton.isSelected();
    }

    public boolean isLearnExpiredCards() {
        return m_allCardsButton.isSelected() || m_expiredCardsButton.isSelected();
    }

    /**
     * Sets all learn settings widgets according to the settings of the current
     * strategy.
     */
    public void resetSettings() {
        // get limiter settings
        m_cardLimitCheckBox.setSelected(m_settings.isCardLimitEnabled());
        m_timeLimitCheckBox.setSelected(m_settings.isTimeLimitEnabled());
        updateLimiterCheckboxes();

        m_cardLimitSpinner.setValue(new Integer(m_settings.getCardLimit()));
        m_timeLimitSpinner.setValue(new Integer(m_settings.getTimeLimit()));
        m_dontRetestCheckBox.setSelected(!m_settings.isRetestFailedCards());

        switch (m_settings.getSidesMode()) {
        case LearnSettings.SIDES_NORMAL:
            m_sidesNormalButton.setSelected(true);
            break;

        case LearnSettings.SIDES_FLIPPED:
            m_sidesFlippedButton.setSelected(true);
            break;

        case LearnSettings.SIDES_BOTH:
            m_sidesBothButton.setSelected(true);
            break;

        default: // SIDES.RANDOM
            m_sidesRandomButton.setSelected(true);
            break;
        }

        // get side amounts
        m_frontChecksAmountSpinner.setValue(new Integer(m_settings.getAmountToTest(true)));
        m_backChecksAmountSpinner.setValue(new Integer(m_settings.getAmountToTest(false)));

        // get schedule
        SchedulePreset preset = m_settings.getSchedulePreset();
        m_schedulePresetsComboBox.setSelectedIndex(preset.ordinal());
        updateScheduleSpinners(m_settings.getSchedule());

        // get fixed due times
        m_fixedExpirationTimeCheckBox.setSelected(m_settings.isFixedExpirationTimeEnabled());
        SpinnerDateModel model = (SpinnerDateModel) m_fixedExpirationTimeSpinner.getModel();

        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, m_settings.getFixedExpirationHour());
        calendar.set(Calendar.MINUTE, m_settings.getFixedExpirationMinute());
        model.setValue(calendar.getTime());

        updateFixedDueTimeSpinner();

        // get category settings
        m_categoryGroupsCheckBox.setSelected(m_settings.isGroupByCategory());

        if (m_settings.getCategoryOrder() == LearnSettings.CATEGORY_ORDER_FIXED) {
            m_categoryOrderFixedButton.setSelected(true);
        } else {
            m_categoryOrderRandomButton.setSelected(true);
        }
        updateCategoryOrderButtons();

        m_shuffleRatioSlider.setValue((int) (100 * m_settings.getShuffleRatio()));
    }

    /**
     * Applies all settings currently entered into the learn settings panels to 
     * the current strategy.
     */
    public void applySettings() {
        // apply limiter settings
        m_settings.setCardLimitEnabled(m_cardLimitCheckBox.isSelected());
        m_settings.setCardLimit(intValue(m_cardLimitSpinner));

        m_settings.setTimeLimitEnabled(m_timeLimitCheckBox.isSelected());
        m_settings.setTimeLimit(intValue(m_timeLimitSpinner));

        m_settings.setRetestFailedCards(!m_dontRetestCheckBox.isSelected());

        m_settings.setSidesMode(m_sidesNormalButton.isSelected() ? LearnSettings.SIDES_NORMAL
                : m_sidesFlippedButton.isSelected() ? LearnSettings.SIDES_FLIPPED
                        : m_sidesBothButton.isSelected() ? LearnSettings.SIDES_BOTH : LearnSettings.SIDES_RANDOM);

        m_settings.setAmountToTest(true, intValue(m_frontChecksAmountSpinner));
        m_settings.setAmountToTest(false, intValue(m_backChecksAmountSpinner));

        // apply schedule
        int idx = m_schedulePresetsComboBox.getSelectedIndex();
        SchedulePreset preset = SchedulePreset.values()[idx];

        if (preset != SchedulePreset.CUSTOM) {
            m_settings.setSchedulePreset(preset);
        } else {
            // schedule holds the time spans in minutes
            int[] schedule = new int[LearnSettings.SCHEDULE_LEVELS];
            for (int i = 0; i < LearnSettings.SCHEDULE_LEVELS; i++) {
                schedule[i] = (24 * 60 * intValue(m_scheduleDays[i])) + (60 * intValue(m_scheduleHours[i]))
                        + intValue(m_scheduleMinutes[i]);
            }

            m_settings.setCustomSchedule(schedule);
        }

        // apply fixed due time
        m_settings.setFixedExpirationTimeEnabled(m_fixedExpirationTimeCheckBox.isSelected());

        SpinnerDateModel model = (SpinnerDateModel) m_fixedExpirationTimeSpinner.getModel();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(model.getDate());

        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);
        m_settings.setFixedExpirationTime(hour, minute);

        // apply category settings
        m_settings.setGroupByCategory(m_categoryGroupsCheckBox.isSelected());
        m_settings.setCategoryOrder(m_categoryOrderRandomButton.isSelected() ? LearnSettings.CATEGORY_ORDER_RANDOM
                : LearnSettings.CATEGORY_ORDER_FIXED);

        m_settings.setShuffleRatio(m_shuffleRatioSlider.getValue() / 100.0f);
    }

    public Category getSelectedCategory() {
        return m_categoryComboBox.getSelectedCategory();
    }

    public JPanel buildCardsPanel() {
        // prepare widgets
        ButtonGroup group = new ButtonGroup();
        group.add(m_allCardsButton);
        group.add(m_unlearnedCardsButton);
        group.add(m_expiredCardsButton);
        group.add(m_selectedCardsButton);

        // build panel
        FormLayout layout = new FormLayout("300dlu", // columns //$NON-NLS-1$
                "p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p"); // rows //$NON-NLS-1$

        CellConstraints cc = new CellConstraints();

        DefaultFormBuilder builder = new DefaultFormBuilder(layout);
        builder.addSeparator(Localization.get("LearnSettings.CARDS_TO_LEARN"), cc.xy(1, 1)); //$NON-NLS-1$
        builder.add(m_categoryComboBox, cc.xy(1, 3));
        builder.add(m_allCardsButton, cc.xy(1, 5));
        builder.add(m_unlearnedCardsButton, cc.xy(1, 7));
        builder.add(m_expiredCardsButton, cc.xy(1, 9));
        builder.add(m_selectedCardsButton, cc.xy(1, 11));

        return builder.getPanel();
    }

    public JPanel buildCardOrderPanel() {
        Dictionary<Integer, JLabel> labels = new Hashtable<Integer, JLabel>();
        for (int i = 0; i <= 10; i++)
            labels.put(i * 10, new JLabel(Integer.toString(i * 10) + '%'));
        m_shuffleRatioSlider.setLabelTable(labels);

        // also see http://java.sun.com/docs/books/tutorial/uiswing/components/slider.html
        m_shuffleRatioSlider.setPaintLabels(true);
        m_shuffleRatioSlider.setPaintTicks(true);
        m_shuffleRatioSlider.setMinorTickSpacing(5);
        m_shuffleRatioSlider.setMajorTickSpacing(10);

        m_categoryGroupsCheckBox.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                updateCategoryOrderButtons();
            }
        });

        ButtonGroup categoriesGroup = new ButtonGroup();
        categoriesGroup.add(m_categoryOrderFixedButton);
        categoriesGroup.add(m_categoryOrderRandomButton);

        // build panel
        FormLayout layout = new FormLayout("18dlu, p:grow", // columns       //$NON-NLS-1$
                "p, 3dlu, p, 3dlu, p, 16dlu, p, 3dlu, p, 3dlu, p"); // grouping rows //$NON-NLS-1$

        CellConstraints cc = new CellConstraints();

        DefaultFormBuilder builder = new DefaultFormBuilder(layout);

        String shuffleText = Localization.get(LC.LEARN_SETTINGS_SHUFFLE_RATIO, LC.LEARN_SETTINGS_SHUFFLE);

        builder.addSeparator(Localization.get(LC.LEARN_SETTINGS_CARDS_ORDER), cc.xyw(1, 1, 2));
        builder.addLabel(shuffleText, cc.xyw(1, 3, 2));
        builder.add(m_shuffleRatioSlider, cc.xyw(1, 5, 2));

        builder.add(m_categoryGroupsCheckBox, cc.xyw(1, 7, 2));
        builder.add(m_categoryOrderFixedButton, cc.xy(2, 9));
        builder.add(m_categoryOrderRandomButton, cc.xy(2, 11));

        return builder.getPanel();
    }

    public JPanel buildSidesModePanel() {
        // radio button groups
        ButtonGroup sidesModeGroup = new ButtonGroup();
        sidesModeGroup.add(m_sidesNormalButton);
        sidesModeGroup.add(m_sidesFlippedButton);
        sidesModeGroup.add(m_sidesRandomButton);
        sidesModeGroup.add(m_sidesBothButton);
        m_sidesNormalButton.setSelected(true);

        // add listener
        m_sidesBothButton.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                updateCheckAmountFields();
            }
        });
        updateCheckAmountFields();

        // build panel
        FormLayout layout = new FormLayout("18dlu, d, 9dlu, left:d:grow", // columns        //$NON-NLS-1$
                "p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p"); // side mode rows //$NON-NLS-1$

        CellConstraints cc = new CellConstraints();

        DefaultFormBuilder builder = new DefaultFormBuilder(layout);

        builder.addSeparator(Localization.get("LearnSettings.SIDE_MODE"), cc.xyw(1, 1, 4)); //$NON-NLS-1$
        builder.add(m_sidesNormalButton, cc.xyw(1, 3, 4));
        builder.add(m_sidesFlippedButton, cc.xyw(1, 5, 4));
        builder.add(m_sidesRandomButton, cc.xyw(1, 7, 4));

        builder.add(m_sidesBothButton, cc.xyw(1, 9, 4));

        builder.addLabel(Localization.get(LC.FRONTSIDE), cc.xy(2, 11));
        builder.add(m_frontChecksAmountSpinner, cc.xy(4, 11));

        builder.addLabel(Localization.get(LC.FLIPSIDE), cc.xy(2, 13));
        builder.add(m_backChecksAmountSpinner, cc.xy(4, 13));

        return builder.getPanel();
    }

    public JPanel buildLimiterPanel() {
        // add action listeners
        ActionListener listener = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                updateLimiterCheckboxes();
            }
        };

        m_cardLimitCheckBox.addActionListener(listener);
        m_timeLimitCheckBox.addActionListener(listener);
        updateLimiterCheckboxes();

        // build panel
        FormLayout layout = new FormLayout("18dlu, d, 9dlu, left:d:grow", // columns             //$NON-NLS-1$
                "p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p"); // stop condition rows //$NON-NLS-1$

        CellConstraints cc = new CellConstraints();

        DefaultFormBuilder builder = new DefaultFormBuilder(layout);

        builder.addSeparator(Localization.get(LC.LEARN_SETTINGS_DELIMITERS), cc.xyw(1, 1, 4));
        builder.add(m_timeLimitCheckBox, cc.xyw(1, 3, 4));
        builder.addLabel(Localization.get(LC.LEARN_SETTINGS_TIME_LIMIT), cc.xy(2, 5));
        builder.add(m_timeLimitSpinner, cc.xy(4, 5));

        builder.add(m_cardLimitCheckBox, cc.xyw(1, 7, 4));
        builder.addLabel(Localization.get(LC.LEARN_SETTINGS_CARD_LIMIT), cc.xy(2, 9));
        builder.add(m_cardLimitSpinner, cc.xy(4, 9));

        builder.add(m_dontRetestCheckBox, cc.xyw(1, 11, 4));

        return builder.getPanel();
    }

    public JPanel buildSchedulePanel() {
        // prepare widgets
        m_schedulePresetsComboBox.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                onPresetScheduleSelected();
            }
        });

        // build panel
        FormLayout layout = new FormLayout("p, 20dlu:grow, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, " + // columns //$NON-NLS-1$
                "p,  3dlu,  p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p", //$NON-NLS-1$
                "p, 15dlu,  p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, " + // rows    //$NON-NLS-1$
                        "p,  3dlu,  p, 3dlu, p, 3dlu, p, 3dlu"); //$NON-NLS-1$

        CellConstraints cc = new CellConstraints();

        DefaultFormBuilder builder = new DefaultFormBuilder(layout);
        builder.setDefaultDialogBorder();

        builder.addLabel(Localization.get(LC.LEARN_SETTINGS_PRESETS), cc.xy(1, 1));
        builder.add(m_schedulePresetsComboBox, cc.xyw(3, 1, 11));

        for (int i = 0; i < LearnSettings.SCHEDULE_LEVELS; i++) {
            addScheduleRow(builder, cc, i);
        }

        return builder.getPanel();
    }

    public JPanel buildFixedDueTimePanel() {
        Date date = new Date();
        SpinnerDateModel model = new SpinnerDateModel(date, null, null, Calendar.HOUR);
        m_fixedExpirationTimeSpinner.setModel(model);

        String pattern = "h:mm a";
        if (Localization.SHORT_TIME_FORMATER instanceof SimpleDateFormat) {
            SimpleDateFormat formatter = (SimpleDateFormat) Localization.SHORT_TIME_FORMATER;
            pattern = formatter.toPattern();
        }

        JSpinner.DateEditor de = new JSpinner.DateEditor(m_fixedExpirationTimeSpinner, pattern);
        m_fixedExpirationTimeSpinner.setEditor(de);

        m_fixedExpirationTimeCheckBox.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                updateFixedDueTimeSpinner();
            }
        });

        // build panel
        FormLayout layout = new FormLayout("18dlu, 70dlu, left:d:grow", // columns //$NON-NLS-1$
                "p, 3dlu, p"); // rows //$NON-NLS-1$

        CellConstraints cc = new CellConstraints();

        DefaultFormBuilder builder = new DefaultFormBuilder(layout);
        builder.setDefaultDialogBorder();

        builder.add(m_fixedExpirationTimeCheckBox, cc.xyw(1, 1, 3));
        builder.add(m_fixedExpirationTimeSpinner, cc.xyw(2, 3, 1));

        return builder.getPanel();
    }

    private void updateFixedDueTimeSpinner() {
        m_fixedExpirationTimeSpinner.setEnabled(m_fixedExpirationTimeCheckBox.isSelected());
    }

    private void categoryBoxActionPerformed() {
        updateCardButtons();
    }

    private void addScheduleRow(PanelBuilder builder, CellConstraints cc, int level) {
        ChangeListener listener = new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                spinnerValueChanged();
            }
        };

        m_scheduleDays[level].addChangeListener(listener);
        m_scheduleHours[level].addChangeListener(listener);
        m_scheduleMinutes[level].addChangeListener(listener);

        String deckLabel = MessageFormat.format(Localization.get(LC.LEARN_SETTINGS_DELAY), level);

        builder.addLabel(deckLabel, cc.xy(1, 3 + 2 * level));
        builder.add(m_scheduleDays[level], cc.xy(3, 3 + 2 * level));
        builder.addLabel(Localization.get(LC.LEARN_SETTINGS_DAYS), cc.xy(5, 3 + 2 * level));
        builder.add(m_scheduleHours[level], cc.xy(7, 3 + 2 * level));
        builder.addLabel(Localization.get(LC.LEARN_SETTINGS_HOURS), cc.xy(9, 3 + 2 * level));
        builder.add(m_scheduleMinutes[level], cc.xy(11, 3 + 2 * level));
        builder.addLabel(Localization.get(LC.LEARN_SETTINGS_MINUTES), cc.xy(13, 3 + 2 * level));
    }

    private void updateLimiterCheckboxes() {
        m_timeLimitSpinner.setEnabled(m_timeLimitCheckBox.isSelected());
        m_cardLimitSpinner.setEnabled(m_cardLimitCheckBox.isSelected());
    }

    private void updateCategoryOrderButtons() {
        m_categoryOrderRandomButton.setEnabled(m_categoryGroupsCheckBox.isSelected());
        m_categoryOrderFixedButton.setEnabled(m_categoryGroupsCheckBox.isSelected());
    }

    private void updateCardButtons() {
        boolean enableUnlearned = !getSelectedCategory().getUnlearnedCards().isEmpty();
        boolean enableExpired = !getSelectedCategory().getExpiredCards().isEmpty();
        boolean enableAll = enableUnlearned && enableExpired;
        boolean enableSelected = m_hasSelectedCards;

        m_unlearnedCardsButton.setEnabled(enableUnlearned);
        m_expiredCardsButton.setEnabled(enableExpired);
        m_allCardsButton.setEnabled(enableAll);
        m_selectedCardsButton.setEnabled(enableSelected);

        boolean reselect = (m_allCardsButton.isSelected() && !enableAll)
                || (m_unlearnedCardsButton.isSelected() && !enableUnlearned)
                || (m_expiredCardsButton.isSelected() && !enableExpired)
                || (m_selectedCardsButton.isSelected() && !enableSelected);

        if (reselect) {
            if (enableAll)
                m_allCardsButton.setSelected(true);

            else if (enableUnlearned)
                m_unlearnedCardsButton.setSelected(true);

            else if (enableExpired)
                m_expiredCardsButton.setSelected(true);

            else if (enableSelected)
                m_selectedCardsButton.setSelected(true);

            // TODO log else case
        }
    }

    private void updateCheckAmountFields() {
        m_frontChecksAmountSpinner.setEnabled(m_sidesBothButton.isSelected());
        m_backChecksAmountSpinner.setEnabled(m_sidesBothButton.isSelected());
    }

    private void onPresetScheduleSelected() {
        int idx = m_schedulePresetsComboBox.getSelectedIndex();
        SchedulePreset preset = SchedulePreset.values()[idx];

        if (preset != SchedulePreset.CUSTOM) {
            int[] schedule = LearnSettings.getPresetSchedule(preset);
            updateScheduleSpinners(schedule);
        }
    }

    private void updateScheduleSpinners(int[] schedule) {
        m_updatingSpinners = true;

        for (int i = 0; i < LearnSettings.SCHEDULE_LEVELS; i++) {
            m_scheduleDays[i].setValue(new Integer(schedule[i] / (60 * 24)));
            m_scheduleHours[i].setValue(new Integer((schedule[i] % (60 * 24)) / 60));
            m_scheduleMinutes[i].setValue(new Integer(schedule[i] % 60));
        }

        m_updatingSpinners = false;
    }

    private void spinnerValueChanged() {
        // just to make sure that the value was changed by a user and not 
        // by choosing from the presets combobox.
        if (!m_updatingSpinners) {
            m_schedulePresetsComboBox.setSelectedIndex(LearnSettings.SCHEDULE_PRESETS.length - 1);
        }
    }

    private int intValue(JSpinner spinner) {
        return ((SpinnerNumberModel) spinner.getModel()).getNumber().intValue();
    }
}