001 package org.gwtbootstrap3.extras.select.client.ui;
002
003 /*
004 * #%L
005 * GwtBootstrap3
006 * %%
007 * Copyright (C) 2013 - 2014 GwtBootstrap3
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 * http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023 import com.google.gwt.core.client.ScriptInjector;
024 import com.google.gwt.core.client.JavaScriptObject;
025 import com.google.gwt.core.client.JsArrayString;
026 import com.google.gwt.core.client.Scheduler;
027 import com.google.gwt.dom.client.Document;
028 import com.google.gwt.dom.client.Element;
029 import com.google.gwt.dom.client.SelectElement;
030 import com.google.gwt.event.dom.client.ChangeEvent;
031 import com.google.gwt.event.dom.client.ChangeHandler;
032 import com.google.gwt.event.shared.HandlerRegistration;
033 import com.google.gwt.user.client.Command;
034 import com.google.gwt.user.client.ui.Focusable;
035 import com.google.gwt.user.client.ui.HasEnabled;
036 import com.google.gwt.user.client.ui.HasName;
037
038 import org.gwtbootstrap3.client.ui.base.ComplexWidget;
039 import org.gwtbootstrap3.client.ui.base.helper.StyleHelper;
040 import org.gwtbootstrap3.client.ui.base.mixin.AttributeMixin;
041 import org.gwtbootstrap3.client.ui.base.mixin.EnabledMixin;
042 import org.gwtbootstrap3.client.ui.base.mixin.FocusableMixin;
043 import org.gwtbootstrap3.client.ui.constants.ButtonType;
044 import org.gwtbootstrap3.extras.select.client.constants.Styles;
045 import org.gwtbootstrap3.extras.select.client.constants.SelectLanguage;
046 import org.gwtbootstrap3.extras.select.client.ui.interfaces.HasLanguage;
047
048 import java.util.ArrayList;
049 import java.util.List;
050
051 import static org.gwtbootstrap3.extras.select.client.constants.DataAttributes.*;
052
053 /**
054 * @author godi
055 */
056 public class Select extends ComplexWidget implements Focusable, HasEnabled, HasLanguage, HasName {
057 private static final String REFRESH = "refresh";
058 private static final String RENDER = "render";
059 private static final String SHOW = "show";
060 private static final String HIDE = "hide";
061 private static final String SELECT_ALL = "selectAll";
062 private static final String DESELECT_ALL = "deselectAll";
063 private static final String TRUE = "true";
064 private SelectLanguage language = SelectLanguage.EN;
065
066 private final SelectElement selectElement;
067 private final AttributeMixin<Select> attributeMixin = new AttributeMixin<Select>(this);
068 private final FocusableMixin<Select> focusableMixin = new FocusableMixin<Select>(this);
069 private final EnabledMixin<Select> enabledMixin = new EnabledMixin<Select>(this);
070
071 public Select() {
072 selectElement = Document.get().createSelectElement();
073 setElement(selectElement);
074 setStyleName(Styles.BOOTSTRAP_SELECT);
075 addStyleName(org.gwtbootstrap3.client.ui.constants.Styles.FORM_CONTROL);
076 }
077
078 @Override
079 protected void onLoad() {
080 super.onLoad();
081
082 initialize();
083 }
084
085 public HandlerRegistration addChangeHandler(final ChangeHandler handler) {
086 return addDomHandler(handler, ChangeEvent.getType());
087 }
088
089 public void setHeader(final String header) {
090 attributeMixin.setAttribute(DATA_HEADER, header);
091 }
092
093 public String getHeader() {
094 return attributeMixin.getAttribute(DATA_HEADER);
095 }
096
097 public void clearHeader() {
098 attributeMixin.removeAttribute(DATA_HEADER);
099 }
100
101 public void setShowSubtext(final boolean showSubtext) {
102 if (showSubtext) {
103 attributeMixin.setAttribute(DATA_SHOW_SUBTEXT, Boolean.toString(true));
104 } else {
105 attributeMixin.removeAttribute(DATA_SHOW_SUBTEXT);
106 }
107 }
108
109 public boolean getShowSubtext() {
110 return attributeMixin.getAttribute(DATA_SHOW_SUBTEXT) != null;
111 }
112
113 /**
114 * {@inheritDoc}
115 */
116 @Override
117 public void setEnabled(final boolean enabled) {
118 enabledMixin.setEnabled(enabled);
119 }
120
121 /**
122 * {@inheritDoc}
123 */
124 @Override
125 public boolean isEnabled() {
126 return enabledMixin.isEnabled();
127 }
128
129 @Override
130 public void setLanguage(final SelectLanguage language) {
131 this.language = language;
132
133 // Inject the JS for the language
134 if (language.getJs() != null) {
135 ScriptInjector.fromString(language.getJs().getText())
136 .setWindow(ScriptInjector.TOP_WINDOW).inject();
137 }
138 }
139
140 @Override
141 public SelectLanguage getLanguage() {
142 return language;
143 }
144
145 /**
146 * Sets the number of lines to show before scrolling
147 * <p/>
148 * Values:
149 * <p/>
150 * (1) auto - (default) shows them all
151 * (2) x - shows x number of entries before scrolling
152 *
153 * @param size String visible size
154 */
155 public void setVisibleSize(final String size) {
156 attributeMixin.setAttribute(DATA_SIZE, size);
157 }
158
159 public String getVisibleSize() {
160 return attributeMixin.getAttribute(DATA_SIZE);
161 }
162
163 public void clearVisibleSize() {
164 attributeMixin.removeAttribute(DATA_SIZE);
165 }
166
167 /**
168 * Sets the width of the select
169 * <p/>
170 * !! use 'auto' to automatically adjust the width of the select to its widest option, or just use
171 * specific values (50px, 50%, etc...)
172 */
173 @Override
174 public void setWidth(final String width) {
175 attributeMixin.setAttribute(DATA_WIDTH, width);
176 }
177
178 public String getWidth() {
179 return attributeMixin.getAttribute(DATA_WIDTH);
180 }
181
182 /**
183 * Sets the Max Number of selectable option of the select
184 * <p/>
185 */
186 public void setMaxOption(final String maxOption) {
187 attributeMixin.setAttribute(DATA_MAX_OPTION, maxOption);
188 }
189
190 public String getMaxOption() {
191 return attributeMixin.getAttribute(DATA_MAX_OPTION);
192 }
193
194 public void setShowMenuArrow(final boolean showMenuArrow) {
195 if (showMenuArrow) {
196 addStyleName(Styles.SHOW_MENU_ARROW);
197 } else {
198 removeStyleName(Styles.SHOW_MENU_ARROW);
199 }
200 }
201
202 public boolean getShowMenuArrow() {
203 return StyleHelper.containsStyle(getStyleName(), Styles.SHOW_MENU_ARROW);
204 }
205
206 public void setShowTick(final boolean showTick) {
207 if (showTick) {
208 addStyleName(Styles.SHOW_TICK);
209 } else {
210 removeStyleName(Styles.SHOW_TICK);
211 }
212 }
213
214 public boolean getShowTick() {
215 return StyleHelper.containsStyle(getStyleName(), Styles.SHOW_TICK);
216 }
217
218 /**
219 * Supported Values:
220 * <p/>
221 * (1) values - default, comma delimited list
222 * (2) count - If one item is selected, then the value is shown, if more than one is selected then the number of selected items is displayed, eg 2 of 6 selected
223 * (3) count greater than x - Where X is the number of items selected when the display format changes from values to count
224 *
225 * @param format selected text format
226 */
227 public void setSelectedTextFormat(final String format) {
228 attributeMixin.setAttribute(DATA_SELECTED_TEXT_FORMAT, format);
229 }
230
231 public String getSelectedTextFormat() {
232 return attributeMixin.getAttribute(DATA_SELECTED_TEXT_FORMAT);
233 }
234
235 public void clearSelectedTextFormat() {
236 attributeMixin.removeAttribute(DATA_SELECTED_TEXT_FORMAT);
237 }
238
239 public void setMultiple(final boolean multiple) {
240 if (multiple) {
241 attributeMixin.setAttribute(MULTIPLE, "");
242 } else {
243 attributeMixin.removeAttribute(MULTIPLE);
244 }
245 }
246
247 public boolean isMultiple() {
248 return attributeMixin.hasAttribute(MULTIPLE);
249 }
250
251 public void setMobile(final boolean mobile) {
252 if (mobile) {
253 attributeMixin.setAttribute(DATA_MOBILE, Boolean.toString(true));
254 } else {
255 attributeMixin.removeAttribute(DATA_MOBILE);
256 }
257 }
258
259 public boolean isMobile() {
260 return attributeMixin.hasAttribute(DATA_MOBILE);
261 }
262
263 public void setLiveSearch(final boolean liveSearch) {
264 if (liveSearch) {
265 attributeMixin.setAttribute(DATA_LIVE_SEARCH, Boolean.toString(true));
266 } else {
267 attributeMixin.removeAttribute(DATA_LIVE_SEARCH);
268 }
269 }
270
271 public boolean isLiveSearch() {
272 return !attributeMixin.getAttribute(DATA_LIVE_SEARCH).isEmpty() || attributeMixin.getAttribute(DATA_LIVE_SEARCH).equals(TRUE);
273 }
274
275 public void setStyle(final ButtonType style) {
276 attributeMixin.setAttribute(DATA_STYLE, style.getCssName());
277 }
278
279 public void clearStyle() {
280 attributeMixin.removeAttribute(DATA_STYLE);
281 }
282
283 public String getStyle() {
284 return attributeMixin.getAttribute(DATA_STYLE);
285 }
286
287 public void setValue(final String value) {
288 // Need to defer the setValue to make sure the element is actually in the DOM to manipulate
289 Scheduler.get().scheduleDeferred(new Command() {
290 @Override
291 public void execute() {
292 setValue(getElement(), value);
293 }
294 });
295 }
296
297 public void setValues(final String... values) {
298 final JsArrayString array = JavaScriptObject.createArray().cast();
299
300 for (final String value : values) {
301 array.push(value);
302 }
303
304 // Need to defer the setValue to make sure the element is actually in the DOM to manipulate
305 Scheduler.get().scheduleDeferred(new Command() {
306 @Override
307 public void execute() {
308 setValue(getElement(), array);
309 }
310 });
311 }
312
313 public void setValue(final Option opt) {
314 setValue(opt.getText());
315 }
316
317 public void setValues(final Option... opts) {
318 final String[] values = new String[opts.length];
319 for (int i = 0; i < opts.length; i++) {
320 values[i] = opts[i].getText();
321 }
322 setValues(values);
323 }
324
325 /**
326 * @return the selected value, if multiple it will return the first selected item see {@link #isItemSelected(int)}
327 * and {@link #getValue(int)} for getting all the values selected or {@link #getAllSelectedValues()}
328 */
329 public String getValue() {
330 int selectedIndex = getSelectElement().getSelectedIndex();
331 return selectedIndex == -1 ? null : getSelectElement().getOptions().getItem(selectedIndex).getValue();
332 }
333
334 public List<String> getAllSelectedValues() {
335 final List<String> allSelected = new ArrayList<String>();
336
337 for (int i = 0; i < getItemCount(); i++) {
338 if (isItemSelected(i)) {
339 allSelected.add(getValue(i));
340 }
341 }
342 return allSelected;
343 }
344
345 public boolean isItemSelected(final int index) {
346 checkIndex(index);
347 return getSelectElement().getOptions().getItem(index).isSelected();
348 }
349
350 public String getValue(final int index) {
351 checkIndex(index);
352 return getSelectElement().getOptions().getItem(index).getValue();
353 }
354
355 public void selectAll() {
356 command(getElement(), SELECT_ALL);
357 }
358
359 public void deselectAll() {
360 command(getElement(), DESELECT_ALL);
361 }
362
363 public void render() {
364 command(getElement(), RENDER);
365 }
366
367 /**
368 * WHEN CHANGING ANY SETTINGS CALL REFRESH AFTER!!
369 */
370 public void refresh() {
371 command(getElement(), REFRESH);
372 }
373
374 public void hide() {
375 command(getElement(), HIDE);
376 }
377
378 public void show() {
379 command(getElement(), SHOW);
380 }
381
382 private void initialize() {
383 initialize(getElement());
384 }
385
386 private void checkIndex(final int index) {
387 if (index < 0 || index >= getItemCount()) {
388 throw new IndexOutOfBoundsException();
389 }
390 }
391
392 protected SelectElement getSelectElement() {
393 return getElement().cast();
394 }
395
396 /**
397 * {@inheritDoc}
398 */
399 @Override
400 public int getTabIndex() {
401 return focusableMixin.getTabIndex();
402 }
403
404 /**
405 * {@inheritDoc}
406 */
407 @Override
408 public void setAccessKey(final char c) {
409 focusableMixin.setAccessKey(c);
410 }
411
412 /**
413 * {@inheritDoc}
414 */
415 @Override
416 public void setFocus(final boolean b) {
417 focusableMixin.setFocus(b);
418 }
419
420 /**
421 * {@inheritDoc}
422 */
423 @Override
424 public void setTabIndex(final int i) {
425 focusableMixin.setTabIndex(i);
426 }
427
428 @Override
429 public void setName(String name) {
430 selectElement.setName(name);
431 }
432
433 @Override
434 public String getName() {
435 return selectElement.getName();
436 }
437
438 /**
439 * Gets the number of items present in the list box.
440 *
441 * @return the number of items
442 */
443 public int getItemCount() {
444 return getSelectElement().getOptions().getLength();
445 }
446
447 private native void initialize(Element e) /*-{
448 $wnd.jQuery(e).selectpicker({
449 iconBase: 'fa',
450 tickIcon: 'fa-check'
451 });
452 }-*/;
453
454 private native void setValue(Element e, JsArrayString value) /*-{
455 $wnd.jQuery(e).selectpicker('val', value);
456 }-*/;
457
458 private native void setValue(Element e, String value) /*-{
459 $wnd.jQuery(e).selectpicker('val', value);
460 }-*/;
461
462 private native void command(Element e, String command) /*-{
463 $wnd.jQuery(e).selectpicker(command);
464 }-*/;
465 }