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 }