001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.widgets;
003
004import java.util.ArrayList;
005import java.util.Iterator;
006import java.util.List;
007import java.util.NoSuchElementException;
008
009import javax.swing.DefaultComboBoxModel;
010
011import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionListItem;
012
013/**
014 * A data model for {@link HistoryComboBox}
015 */
016class ComboBoxHistory extends DefaultComboBoxModel<AutoCompletionListItem> implements Iterable<AutoCompletionListItem> {
017
018    private final int maxSize;
019
020    private final transient List<HistoryChangedListener> listeners = new ArrayList<>();
021
022    /**
023     * Constructs a {@code ComboBoxHistory} keeping track of {@code maxSize} items
024     * @param size the history size
025     */
026    ComboBoxHistory(int size) {
027        maxSize = size;
028    }
029
030    /**
031     * Adds or moves an element to the top of the history
032     * @param s the element to add
033     */
034    public void addElement(String s) {
035        addElement(new AutoCompletionListItem(s));
036    }
037
038    /**
039     * Adds or moves an element to the top of the history
040     * @param o the element to add
041     */
042    @Override
043    public void addElement(AutoCompletionListItem o) {
044        String newEntry = o.getValue();
045
046        // if history contains this object already, delete it,
047        // so that it looks like a move to the top
048        for (int i = 0; i < getSize(); i++) {
049            String oldEntry = getElementAt(i).getValue();
050            if (oldEntry.equals(newEntry)) {
051                removeElementAt(i);
052            }
053        }
054
055        // insert element at the top
056        insertElementAt(o, 0);
057
058        // remove an element, if the history gets too large
059        if (getSize() > maxSize) {
060            removeElementAt(getSize()-1);
061        }
062
063        // set selected item to the one just added
064        setSelectedItem(o);
065
066        fireHistoryChanged();
067    }
068
069    @Override
070    public Iterator<AutoCompletionListItem> iterator() {
071        return new Iterator<AutoCompletionListItem>() {
072
073            private int position = -1;
074
075            @Override
076            public void remove() {
077                removeElementAt(position);
078            }
079
080            @Override
081            public boolean hasNext() {
082                return position < getSize()-1 && getSize() > 0;
083            }
084
085            @Override
086            public AutoCompletionListItem next() {
087                if (!hasNext())
088                    throw new NoSuchElementException();
089                position++;
090                return getElementAt(position);
091            }
092        };
093    }
094
095    /**
096     * {@link javax.swing.DefaultComboBoxModel#removeAllElements() Removes all items}
097     * and {@link ComboBoxHistory#addElement(String) adds} the given items.
098     * @param items the items to set
099     */
100    public void setItemsAsString(List<String> items) {
101        removeAllElements();
102        for (int i = items.size()-1; i >= 0; i--) {
103            addElement(items.get(i));
104        }
105    }
106
107    /**
108     * Returns the {@link AutoCompletionListItem} items as strings
109     * @return a list of strings
110     */
111    public List<String> asStringList() {
112        List<String> list = new ArrayList<>(maxSize);
113        for (AutoCompletionListItem item : this) {
114            list.add(item.getValue());
115        }
116        return list;
117    }
118
119    public void addHistoryChangedListener(HistoryChangedListener l) {
120        listeners.add(l);
121    }
122
123    public void removeHistoryChangedListener(HistoryChangedListener l) {
124        listeners.remove(l);
125    }
126
127    private void fireHistoryChanged() {
128        for (HistoryChangedListener l : listeners) {
129            l.historyChanged(asStringList());
130        }
131    }
132}