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 */ 016package com.github.gwtbootstrap.client.ui; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import com.github.gwtbootstrap.client.ui.base.HasAlternateSize; 025import com.github.gwtbootstrap.client.ui.base.HasId; 026import com.github.gwtbootstrap.client.ui.base.HasSize; 027import com.github.gwtbootstrap.client.ui.base.HasStyle; 028import com.github.gwtbootstrap.client.ui.base.IsResponsive; 029import com.github.gwtbootstrap.client.ui.base.IsSearchQuery; 030import com.github.gwtbootstrap.client.ui.base.ResponsiveHelper; 031import com.github.gwtbootstrap.client.ui.base.SearchQueryStyleHelper; 032import com.github.gwtbootstrap.client.ui.base.SizeHelper; 033import com.github.gwtbootstrap.client.ui.base.Style; 034import com.github.gwtbootstrap.client.ui.base.StyleHelper; 035import com.github.gwtbootstrap.client.ui.constants.AlternateSize; 036import com.github.gwtbootstrap.client.ui.constants.Constants; 037import com.github.gwtbootstrap.client.ui.constants.Device; 038import com.google.gwt.editor.client.IsEditor; 039import com.google.gwt.editor.client.adapters.TakesValueEditor; 040import com.google.gwt.event.dom.client.ChangeEvent; 041import com.google.gwt.event.dom.client.ChangeHandler; 042import com.google.gwt.event.logical.shared.ValueChangeEvent; 043import com.google.gwt.event.logical.shared.ValueChangeHandler; 044import com.google.gwt.event.shared.HandlerRegistration; 045import com.google.gwt.text.shared.Renderer; 046import com.google.gwt.user.client.ui.Composite; 047import com.google.gwt.user.client.ui.HasConstrainedValue; 048import com.google.gwt.user.client.ui.HasEnabled; 049import com.google.gwt.user.client.ui.HasName; 050import com.google.gwt.view.client.ProvidesKey; 051import 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 */ 060public 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}