001    /*
002     * LAPIS lightweight structured text processing system
003     *
004     * Copyright (C) 1998-2002 Carnegie Mellon University,
005     * Copyright (C) 2003 Massachusetts Institute of Technology.
006     * All rights reserved.
007     *
008     * This library is free software; you can redistribute it
009     * and/or modify it under the terms of the GNU General
010     * Public License as published by the Free Software
011     * Foundation, version 2.
012     *
013     * LAPIS homepage: http://graphics.lcs.mit.edu/lapis/
014     */
015    
016    
017    package lapisx.swing;
018    
019    import javax.swing.*;
020    import javax.swing.event.*;
021    import lapisx.util.History;
022    import java.util.NoSuchElementException;
023    
024    /**
025     * An adapter for lapisx.util.History that allows a history to 
026     * be used as the model for a JComboBox.
027     * <P>
028     * Example of use:
029     * <PRE>
030     * // make a history with room for 10 elements
031     * HistoryComboBoxModel history = new HistoryComboBoxModel (10);
032     * // make a combo box that displays the history in its drop-down list
033     * JComboBox combobox = new JComboBox (history);
034     * </PRE>
035     */
036    public class HistoryComboBoxModel extends History implements ComboBoxModel {
037        /**
038         * Listeners on this object.
039         */
040        protected EventListenerList listenerList = new EventListenerList ();
041    
042        /**
043         * Make a HistoryComboBoxModel.
044         * @param max Maximum length of history
045         */
046        public HistoryComboBoxModel (int max) {
047            super (max);
048        }
049        
050        /**
051         * Make a duplicate of another History.
052         * @param h History to copy
053         */
054        public HistoryComboBoxModel (History h) {
055            super (h);
056        }
057    
058        ///////////
059        /////////// ComboBoxModel interface
060        ///////////
061    
062        public void addListDataListener(ListDataListener l) {
063            listenerList.add(ListDataListener.class, l);
064        }
065        
066        public void removeListDataListener(ListDataListener l) {
067            listenerList.remove(ListDataListener.class, l);
068        }
069    
070        public int getSize () {
071            return size ();
072        }
073        
074        public Object getElementAt (int index) {
075            if (index < 0 || index > getSize ())
076                throw new ArrayIndexOutOfBoundsException (index + " out of bounds");
077            
078            return history[linearToCircular (index)];
079        }
080        
081        public Object getSelectedItem () {
082            return get ();
083        }
084        
085        public void setSelectedItem (Object obj) {
086            try {
087                jumpTo (obj);
088            } catch (NoSuchElementException e) {
089            }
090        }
091        
092        ///////////
093        /////////// Implementation
094        ///////////
095    
096        protected void fireRemoved (int i, int j) {
097            fire (ListDataEvent.INTERVAL_REMOVED, i, j);
098        }
099        
100        protected void fireAdded (int i, int j) {
101            fire (ListDataEvent.INTERVAL_ADDED, i, j);
102        }
103        
104        protected void fireChanged (int i, int j) {
105            fire (ListDataEvent.CONTENTS_CHANGED, i, j);
106        }
107        
108        protected void fire (int eventType, int start, int end) {
109            Object[] listeners = listenerList.getListenerList();
110            ListDataEvent event = 
111                new ListDataEvent(this, eventType,
112                                  circularToLinear (start), 
113                                  circularToLinear (end));
114            
115            for (int i = listeners.length - 2; i >= 0; i -= 2)
116                if (listeners[i] == ListDataListener.class)
117                    switch (eventType) {
118                    case ListDataEvent.CONTENTS_CHANGED:
119                        ((ListDataListener)listeners[i+1]).contentsChanged (event);
120                        break;
121                    case ListDataEvent.INTERVAL_ADDED:
122                        ((ListDataListener)listeners[i+1]).intervalAdded (event);
123                        break;
124                    case ListDataEvent.INTERVAL_REMOVED:
125                        ((ListDataListener)listeners[i+1]).intervalRemoved (event);
126                        break;
127                    }
128        }
129        
130        /**
131         * Maps end-1 to 0, end-2 to 1, ..., start to L-1.
132         */
133        private int circularToLinear (int i) {
134            return (end-1 - i + history.length) % history.length;
135        }
136        
137        /**
138         * Maps 0 to end-1, 1 to end-2, ..., L-1 to start.
139         */
140        private int linearToCircular (int i) {
141            return (end-1 - i + history.length) % history.length;
142        }
143        
144    }
145