/******************************************************************************* * Breakout Cave Survey Visualizer * * Copyright (C) 2014 James Edwards * * jedwards8 at fastmail dot fm * * 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 2 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, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *******************************************************************************/ package org.andork.swing; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.List; import javax.swing.SwingUtilities; import org.andork.collect.CollectionUtils; import org.andork.func.Mapper; import org.andork.swing.selector.DefaultSelector; /** * A {@link DefaultSelector} with an editable combo box that manages presets for * some user settings. The way settings are persisted is up to the * implementation.<br> * <br> * * Whenever the user types a name in the combo box, if a preset with that name * already exists (case insensitive), it will switch to it. Otherwise, it will * create a new preset with that name and save it.<br> * <br> * * The implementation must specify a {@link #getDefaultPreset() default preset} * and an {@link #getUntitledPreset() untitled preset}. Those will be the first * two elements of the combo box. When the default preset is selected, if the * user changes settings and {@link #saveCurrentPreset()} is called, it will * automatically switch to the untitled preset and overwrite it with the current * settings. * * @author andy.edwards * * @param <T> * the preset type. */ public abstract class DefaultSelectorPresetsManager<T> { private class ChangeHandler implements ItemListener { @SuppressWarnings("unchecked") @Override public void itemStateChanged(ItemEvent e) { if (ignoreChange || e.getStateChange() == ItemEvent.DESELECTED) { return; } Object item = selector.comboBox().getSelectedItem(); if (item != null) { T preset = null; if (getPresetClass().isInstance(item)) { preset = (T) item; // the user just selected a different item ignoreChange = true; try { loadPreset(preset); } finally { ignoreChange = false; } } else { String name = (String) item; List<T> presets = selector.getAvailableValues(); // find the index of the preset with the same name, or // sorted insert index if none exists int index = search(name); if (index >= 0) { // the user typed the name of an existing preset into // the combo box preset = presets.get(index); // set the selection to the object, not the name // (this will re-notify this listener) selector.setSelection(preset); } else { // the user typed a new preset name into the combo box preset = createNewPreset(name); preset = storePreset(preset); serialize(preset); ignoreChange = true; try { selector.addAvailableValue(-(index + 1), preset); selector.setSelection(preset); } finally { ignoreChange = false; } } } } } } private boolean ignoreChange; private DefaultSelector<T> selector; private ChangeHandler changeHandler; public DefaultSelectorPresetsManager() { selector = new DefaultSelector<T>(); selector.addAvailableValue(0, getDefaultPreset()); selector.addAvailableValue(1, getUntitledPreset()); selector.comboBox().setEditable(true); selector.setSelection(getDefaultPreset()); // selector.getComboBox().setUI(new SpecialComboBoxUI()); changeHandler = new ChangeHandler(); selector.comboBox().addItemListener(changeHandler); } protected abstract T createNewPreset(String name); protected abstract T getDefaultPreset(); protected abstract String getName(T preset); protected abstract Class<T> getPresetClass(); public DefaultSelector<T> getSelector() { return selector; } protected abstract T getUntitledPreset(); protected abstract void loadPreset(T preset); public void saveCurrentPreset() { if (!SwingUtilities.isEventDispatchThread()) { throw new RuntimeException("Must be called from EDT"); } if (ignoreChange) { return; } T selected = selector.getSelection(); if (selected == getDefaultPreset()) { ignoreChange = true; try { selected = getUntitledPreset(); selector.setSelection(selected); } finally { ignoreChange = false; } } if (selected != null) { T stored = storePreset(selected); if (stored != selected) { ignoreChange = true; try { selected = stored; selector.setSelection(selected); } finally { ignoreChange = false; } } if (selected != getUntitledPreset()) { serialize(selected); } } } protected int search(String name) { List<T> presets = selector.getAvailableValues(); String lcname = name.toLowerCase(); if (lcname.equals(getName(getDefaultPreset()).toLowerCase())) { return 0; } else if (lcname.equals(getName(getUntitledPreset()).toLowerCase())) { return 1; } // find the index of the preset with the same name, or // sorted insert index if none exists return CollectionUtils.binarySearch(presets, 2, presets.size(), new Mapper<T, String>() { @Override public String map(T in) { return getName(in).toLowerCase(); } }, lcname); } protected abstract void serialize(T preset); protected abstract T storePreset(T preset); }