/*******************************************************************************
* Copyright (c) 2010 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
******************************************************************************/
package org.csstudio.ui.util.helpers;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Combo;
/**
* Decorates a Combo box to maintain the history.
* <p>
* Newly entered items are added to the top of the combo list, dropping last
* items off the list when reaching a configurable maximum list size. If an item
* is selected/entered again, it will pop at the beginning of the list (so
* that continuously used items are not lost).
* <p>
* You must
* <ul>
* <li>either implement <code>itemSelected()</code> or <code>newSelection()</code>
* to handle entered/selected values.
* <li>decide if you want to call loadSettings() to restore the saved values
* <li>save values via saveSettings, or use the save_on_dispose option of the
* constructor.
* </ul>
*
* @see #itemSelected(String)
* @see #newSelection(String)
* @author Kay Kasemir
* @author Gabriele Carcassi
*/
public class ComboHistoryHelper {
private static final String TAG = "values"; //$NON-NLS-1$
private static final int DEFAULT_HISTORY_SIZE = 10;
private final IDialogSettings settings;
private final String tag;
private final Combo combo;
private final int max;
/**
* Attach helper to given combo box, using max list length.
*
* @param settings where to persist the combo box list
* @param tag tag used for persistence
* @param combo the combo box
*/
public ComboHistoryHelper(IDialogSettings settings, String tag, Combo combo) {
this(settings, tag, combo, DEFAULT_HISTORY_SIZE, true);
}
/**
* Attach helper to given combo box, using max list length.
*
* @param settings where to persist the combo box list
* @param tag tag used for persistence
* @param combo the combo box
* @param max number of elements to keep in history
* @param saveOnDispose whether current values should be saved at widget disposal
*/
public ComboHistoryHelper(IDialogSettings settings, String tag,
Combo combo, int max, boolean saveOnDispose) {
this.settings = settings;
this.tag = tag;
this.combo = combo;
this.max = max;
// React whenever an existing entry is selected,
// or a new name is entered.
// New names are also added to the list.
combo.addSelectionListener(new SelectionListener() {
// Called after <Return> was pressed
@Override
public void widgetDefaultSelected(SelectionEvent e) {
String new_entry = ComboHistoryHelper.this.combo.getText();
addEntry(new_entry);
ComboHistoryHelper.this.combo.select(0);
itemSelected(new_entry);
}
// Called after existing entry was picked from list
@Override
public void widgetSelected(SelectionEvent e) {
String name = ComboHistoryHelper.this.combo.getText();
itemSelected(name);
}
});
// Register saving on dispose
if (saveOnDispose) {
combo.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
saveSettings();
}
});
}
}
private boolean changing_combo = false;
public void changeSelection(final String entry) {
int index = combo.indexOf(entry);
if (index != -1) {
combo.select(index);
} else {
addEntry(entry);
combo.select(0);
}
}
/**
* Adds a new entry to the list. If entry already there, do nothing.
*
* @param newEntry the new value
*/
public void addEntry(final String newEntry)
{
// Possibly removing an entry below can trigger a selection change.
// At least on Mac OS X that sometimes resulted in infinite recursion.
// Avoid such recursion.
if (changing_combo)
return;
changing_combo = true;
try
{
// Avoid empty entries
if (newEntry.trim().isEmpty())
return;
// Remove if present, so that is re-added on top
int entry = -1;
while ((entry = combo.indexOf(newEntry)) >= 0)
combo.remove(entry);
// Maybe remove oldest, i.e. bottom-most, entry
if (combo.getItemCount() >= max)
combo.remove(combo.getItemCount() - 1);
// Add at the top
combo.add(newEntry, 0);
}
finally
{
changing_combo = false;
}
}
private String oldSelection = null;
/** Invoked whenever an item is selected.
* <p>Default implementation will compare
* with previous selection, and only invoke
* <code>newSelection</code>
* when the selection changed.
*
* <p>Override this method to be notified
* of any selection, including the case where the
* user re-selects the same entry, or presses 'Return'
* in the combo's text field without changing its content.
*
* @param selection Selected item, may be <code>null</code>
*/
public void itemSelected(final String selection)
{
if (oldSelection == null) {
if (selection == null) {
return;
}
} else if (oldSelection.equals(selection)) {
return;
}
oldSelection = selection;
newSelection(selection);
}
/** Invoked whenever a new entry was entered or selected.
*
* <p>Override this method to be notified only when
* the user selects a different item.
* Re-selection of the same item will be ignored.
*
* @param selection Selected item, may be <code>null</code>
*/
public void newSelection(final String selection)
{
// Default: NOP
}
/** Load persisted list values. */
public void loadSettings() {
if (settings != null) {
IDialogSettings pvs = settings.getSection(tag);
if (pvs == null)
return;
String values[] = pvs.getArray(TAG);
if (values != null)
for (int i = values.length-1; i >= 0; i--)
// Load as if they were entered, i.e. skip duplicates
addEntry(values[i]);
}
}
/** Save list values to persistent storage. */
public void saveSettings() {
if (settings != null) {
IDialogSettings values = settings.addNewSection(tag);
values.put(TAG, combo.getItems());
}
}
}