001    /*
002     * Copyright 2002-2004 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.springframework.richclient.form.binding.swing;
017    
018    import java.beans.PropertyChangeEvent;
019    import java.beans.PropertyChangeListener;
020    
021    import javax.swing.ComboBoxEditor;
022    import javax.swing.ComboBoxModel;
023    import javax.swing.JComboBox;
024    import javax.swing.ListCellRenderer;
025    import javax.swing.ListModel;
026    import javax.swing.event.ListDataEvent;
027    
028    import org.springframework.binding.form.FormModel;
029    import org.springframework.richclient.list.AbstractFilteredListModel;
030    
031    /**
032     * TODO: support for filters
033     * 
034     * @author Oliver Hutchison
035     */
036    public class ComboBoxBinding extends AbstractListBinding {
037    
038        private Object emptySelectionValue;
039    
040        private BoundComboBoxModel boundModel;
041    
042        public ComboBoxBinding(FormModel formModel, String formPropertyPath) {
043            this(new JComboBox(), formModel, formPropertyPath, null);
044        }
045    
046        public ComboBoxBinding(JComboBox comboBox, FormModel formModel, String formPropertyPath) {
047            this(comboBox, formModel, formPropertyPath, null);
048        }
049    
050        public ComboBoxBinding(JComboBox comboBox, FormModel formModel, String formPropertyPath, Class requiredSourceClass) {
051            super(comboBox, formModel, formPropertyPath, requiredSourceClass);
052        }
053    
054        protected void doBindControl(ListModel bindingModel) {
055            boundModel = new BoundComboBoxModel(bindingModel);
056            getComboBox().setModel(boundModel);
057        }
058    
059        protected ListModel getDefaultModel() {
060            return getComboBox().getModel();
061        }
062    
063        public ListCellRenderer getRenderer() {
064            return getComboBox().getRenderer();
065        }
066    
067        public JComboBox getComboBox() {
068            return (JComboBox) getComponent();
069        }
070    
071        public void setRenderer(ListCellRenderer renderer) {
072            getComboBox().setRenderer(renderer);
073        }
074    
075        public void setEditor(ComboBoxEditor comboBoxEditor) {
076            getComboBox().setEditor(comboBoxEditor);
077        }
078    
079        public ComboBoxEditor getEditor() {
080            return getComboBox().getEditor();
081        }
082    
083        private class BoundComboBoxModel extends AbstractFilteredListModel implements ComboBoxModel, PropertyChangeListener {
084    
085            public BoundComboBoxModel(ListModel listModel) {
086                super(listModel);
087                getValueModel().addValueChangeListener(this);
088            }
089    
090            public int getSize() {
091                if (emptySelectionValue != null) {
092                    return super.getSize() + 1;
093                }
094                return super.getSize();
095            }
096    
097            public Object getElementAt(int index) {
098                if (emptySelectionValue != null) {
099                    if (index == 0)
100                        return emptySelectionValue;
101                    return super.getElementAt(index - 1);
102                }
103                return super.getElementAt(index);
104            }
105    
106            private boolean updateSelectedItem() {
107                Object selectedItem = getSelectedItem();
108                if (selectedItem != null) {
109                    boolean found = false;
110                    for (int i = 0, size = getSize(); i < size && !found; i++) {
111                        found = selectedItem.equals(getElementAt(i));
112                    }
113                    if (!found) {
114                        setSelectedItem(null);
115                        return true;
116                    }
117                }
118                return false;
119            }
120    
121            public void contentsChanged(ListDataEvent e) {
122                if (updateSelectedItem()) {
123                    fireContentsChanged(this, -1, -1);
124                } else {
125                    super.contentsChanged(e);
126                }
127            }
128    
129            public void intervalRemoved(ListDataEvent e) {
130                if (updateSelectedItem()) {
131                    fireContentsChanged(this, -1, -1);
132                } else {
133                    super.intervalRemoved(e);
134                }
135            }
136    
137            public void setSelectedItem(Object selectedItem) {
138                if (selectedItem == emptySelectionValue) {
139                    selectedItem = null;
140                }
141                getValueModel().setValueSilently(selectedItem, this);
142                fireContentsChanged(this, -1, -1);
143            }
144    
145            public Object getSelectedItem() {
146                Object value = getValue();
147                if(emptySelectionValue != null && value == null) {
148                    return emptySelectionValue;
149                }
150                return value;
151            }
152    
153            public void propertyChange(PropertyChangeEvent evt) {
154                fireContentsChanged(this, -1, -1);
155            }
156    
157            public void emptySelectionValueChanged() {
158                fireContentsChanged(this, -1, -1);
159            }
160        }
161    
162        /**
163         * @param value
164         */
165        public void setEmptySelectionValue(Object value) {
166            if (value != emptySelectionValue && boundModel != null) {
167                emptySelectionValue = value;
168                boundModel.emptySelectionValueChanged();
169            } else {
170                emptySelectionValue = value;
171            }
172        }
173    
174        public Object getEmptySelectionValue() {
175            return emptySelectionValue;
176        }
177    }