001    /*
002     *  Copyright 2012 GWT-Bootstrap
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 com.github.gwtbootstrap.client.ui;
017    
018    import java.util.ArrayList;
019    import java.util.Collection;
020    import java.util.HashMap;
021    import java.util.List;
022    import java.util.Map;
023    
024    import com.github.gwtbootstrap.client.ui.base.HasAlternateSize;
025    import com.github.gwtbootstrap.client.ui.base.HasId;
026    import com.github.gwtbootstrap.client.ui.base.HasSize;
027    import com.github.gwtbootstrap.client.ui.base.HasStyle;
028    import com.github.gwtbootstrap.client.ui.base.IsResponsive;
029    import com.github.gwtbootstrap.client.ui.base.IsSearchQuery;
030    import com.github.gwtbootstrap.client.ui.base.ResponsiveHelper;
031    import com.github.gwtbootstrap.client.ui.base.SearchQueryStyleHelper;
032    import com.github.gwtbootstrap.client.ui.base.SizeHelper;
033    import com.github.gwtbootstrap.client.ui.base.Style;
034    import com.github.gwtbootstrap.client.ui.base.StyleHelper;
035    import com.github.gwtbootstrap.client.ui.constants.AlternateSize;
036    import com.github.gwtbootstrap.client.ui.constants.Constants;
037    import com.github.gwtbootstrap.client.ui.constants.Device;
038    import com.google.gwt.editor.client.IsEditor;
039    import com.google.gwt.editor.client.adapters.TakesValueEditor;
040    import com.google.gwt.event.dom.client.ChangeEvent;
041    import com.google.gwt.event.dom.client.ChangeHandler;
042    import com.google.gwt.event.logical.shared.ValueChangeEvent;
043    import com.google.gwt.event.logical.shared.ValueChangeHandler;
044    import com.google.gwt.event.shared.HandlerRegistration;
045    import com.google.gwt.text.shared.Renderer;
046    import com.google.gwt.user.client.ui.Composite;
047    import com.google.gwt.user.client.ui.HasConstrainedValue;
048    import com.google.gwt.user.client.ui.HasEnabled;
049    import com.google.gwt.user.client.ui.HasName;
050    import com.google.gwt.view.client.ProvidesKey;
051    import com.google.gwt.view.client.SimpleKeyProvider;
052    
053    /**
054     * The TW Bootstrap style ValueListBox
055     * 
056     * @param <T> the value type
057     * @author ohashi keisuke
058     * @since 2.0.4.0
059     */
060    public class ValueListBox<T> extends Composite implements HasConstrainedValue<T>, IsEditor<TakesValueEditor<T>>, HasName, HasId, HasEnabled, HasSize, HasAlternateSize, IsSearchQuery, IsResponsive, HasStyle {
061    
062            private final Map<Object, Integer> valueKeyToIndex = new HashMap<Object, Integer>();
063    
064            private final List<T> valueList = new ArrayList<T>();
065    
066            private final Renderer<T> renderer;
067    
068            private final ProvidesKey<T> keyProvider;
069    
070            private TakesValueEditor<T> editor;
071    
072            private T value;
073    
074            public ValueListBox(Renderer<T> renderer) {
075                    this(renderer, new SimpleKeyProvider<T>());
076            }
077    
078            public ValueListBox(Renderer<T> renderer,
079                    ProvidesKey<T> keyProvider) {
080                    this.keyProvider = keyProvider;
081                    this.renderer = renderer;
082                    initWidget(new ListBox());
083    
084                    getListBox().addChangeHandler(new ChangeHandler() {
085    
086                            public void onChange(ChangeEvent event) {
087                                    int selectedIndex = getListBox().getSelectedIndex();
088    
089                                    if (selectedIndex < 0) {
090                                            return;
091                                    }
092                                    T newValue = valueList.get(selectedIndex);
093                                    setValue(newValue, true);
094                            }
095                    });
096            }
097    
098            public void setName(String name) {
099                    getListBox().setName(name);
100            }
101    
102            public String getName() {
103                    return getListBox().getName();
104            }
105    
106            public HandlerRegistration addValueChangeHandler(ValueChangeHandler<T> handler) {
107                    return addHandler(handler, ValueChangeEvent.getType());
108            }
109    
110            /**
111             * Returns a {@link TakesValueEditor} backed by the ValueListBox.
112             */
113            public TakesValueEditor<T> asEditor() {
114                    if (editor == null) {
115                            editor = TakesValueEditor.of(this);
116                    }
117                    return editor;
118            }
119    
120            public T getValue() {
121                    return value;
122            }
123    
124            public void setAcceptableValues(Collection<T> newValues) {
125    
126                    valueList.clear();
127                    
128                    valueKeyToIndex.clear();
129                    
130                    ListBox listBox = getListBox();
131                    
132                    listBox.clear();
133    
134                    for (T nextNewValue : newValues) {
135                            addValue(nextNewValue);
136                    }
137    
138                    updateListBox();
139            }
140    
141            /**
142             * Set the value and display it in the select element. Add the value to the
143             * acceptable set if it is not already there.
144             */
145            public void setValue(T value) {
146                    setValue(value, false);
147            }
148    
149            public void setValue(T value, boolean fireEvents) {
150                    if (value == this.value || (this.value != null && this.value.equals(value))) {
151                            return;
152                    }
153    
154                    T before = this.value;
155                    this.value = value;
156                    updateListBox();
157    
158                    if (fireEvents) {
159                            ValueChangeEvent.fireIfNotEqual(this, before, value);
160                    }
161            }
162    
163            private void addValue(T value) {
164                    Object key = keyProvider.getKey(value);
165                    if (valueKeyToIndex.containsKey(key)) {
166                            throw new IllegalArgumentException("Duplicate value: " + value);
167                    }
168    
169                    valueKeyToIndex.put(key, valueList.size());
170                    valueList.add(value);
171                    getListBox().addItem(renderer.render(value));
172                    assert valueList.size() == getListBox().getItemCount();
173            }
174    
175            private ListBox getListBox() {
176                    return (ListBox) getWidget();
177            }
178    
179            private void updateListBox() {
180                    Object key = keyProvider.getKey(value);
181                    Integer index = valueKeyToIndex.get(key);
182                    if (index == null) {
183                            addValue(value);
184                    }
185    
186                    index = valueKeyToIndex.get(key);
187                    getListBox().setSelectedIndex(index);
188            }
189    
190            /**
191             * {@inheritDoc}
192             */
193            @Override
194            public String getId() {
195                    return getListBox().getId();
196            }
197    
198            /**
199             * {@inheritDoc}
200             */
201            @Override
202            public void setId(String id) {
203                    getListBox().setId(id);
204            }
205    
206            /**
207             * {@inheritDoc}
208             */
209            @Override
210            public void setSearchQuery(boolean searchQuery) {
211                    SearchQueryStyleHelper.setSearchQuery(this, searchQuery);
212            }
213    
214            /**
215             * {@inheritDoc}
216             */
217            @Override
218            public boolean isSearchQuery() {
219                    return SearchQueryStyleHelper.isSearchQuery(this);
220            }
221    
222    
223            /**
224             * {@inheritDoc}
225             */
226            @Override
227            public void setAlternateSize(AlternateSize size) {
228                    StyleHelper.changeStyle(this, size, AlternateSize.class);
229            }
230    
231            /**
232             * {@inheritDoc}
233             */
234            @Override
235            public void setSize(int size) {
236                    SizeHelper.setSize(this, size);
237            }
238            /**
239             * {@inheritDoc}
240             */
241            @Override
242            public void setEnabled(boolean enabled) {
243                    getListBox().setEnabled(enabled);
244                    if(enabled) {
245                            removeStyleName(Constants.DISABLED);
246                    } else {
247                            addStyleName(Constants.DISABLED);
248                    }
249            }
250    
251            @Override
252            public boolean isEnabled() {
253                    return getListBox().isEnabled();
254            }
255            
256    
257            /**
258             * {@inheritDoc}
259             */
260            @Override
261            public void setShowOn(Device device) {
262                    ResponsiveHelper.setShowOn(this, device);
263            }
264    
265            /**
266             * {@inheritDoc}
267             */
268            @Override
269            public void setHideOn(Device device) {
270                    ResponsiveHelper.setHideOn(this, device);
271                    
272            }
273    
274            /**
275             * {@inheritDoc}
276             */
277            @Override
278            public void setStyle(Style style) {
279                    StyleHelper.setStyle(this, style);
280            }
281    
282            /**
283             * {@inheritDoc}
284             */
285            @Override
286            public void addStyle(Style style) {
287                    StyleHelper.addStyle(this, style);
288            }
289    
290            /**
291             * {@inheritDoc}
292             */
293            @Override
294            public void removeStyle(Style style) {
295                    StyleHelper.removeStyle(this, style);
296                    
297            }
298    
299    }