Java tutorial
/* * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package cc.alcina.framework.gwt.client.gwittir.widget; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; /* * ListBox.java * * Created on July 5, 2007, 6:12 PM * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Vector; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.FocusEvent; import com.google.gwt.event.dom.client.FocusHandler; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.ui.ChangeListener; import com.google.gwt.user.client.ui.ClickListener; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.FocusListener; import com.google.gwt.user.client.ui.HasFocus; import com.google.gwt.user.client.ui.KeyboardListener; import com.google.gwt.user.client.ui.SourcesChangeEvents; import com.google.gwt.user.client.ui.SourcesFocusEvents; import com.google.gwt.user.client.ui.Widget; import com.totsp.gwittir.client.beans.SourcesPropertyChangeEvents; import com.totsp.gwittir.client.log.Level; import com.totsp.gwittir.client.log.Logger; import com.totsp.gwittir.client.ui.AbstractBoundCollectionWidget; import com.totsp.gwittir.client.ui.HasEnabled; import com.totsp.gwittir.client.ui.Renderer; import com.totsp.gwittir.client.ui.SimpleComparator; import com.totsp.gwittir.client.ui.ToStringRenderer; import cc.alcina.framework.common.client.actions.InlineButtonHandler; import cc.alcina.framework.common.client.collections.CollectionFilter; import cc.alcina.framework.common.client.logic.domain.HasId; import cc.alcina.framework.common.client.logic.domaintransform.TransformManager; import cc.alcina.framework.common.client.logic.reflection.registry.Registry; import cc.alcina.framework.common.client.provider.TextProvider; import cc.alcina.framework.common.client.util.Callback; import cc.alcina.framework.common.client.util.CommonUtils; import cc.alcina.framework.gwt.client.ClientNotifications; import cc.alcina.framework.gwt.client.gwittir.RequiresContextBindable; import cc.alcina.framework.gwt.client.gwittir.customiser.ListAddItemHandler; import cc.alcina.framework.gwt.client.ide.widget.Toolbar.ToolbarButton; import cc.alcina.framework.gwt.client.widget.UsefulWidgetFactory; import cc.alcina.framework.gwt.client.widget.dialog.OkCancelDialogBox; import cc.alcina.framework.gwt.client.widget.dialog.Prompter; /** * */ @SuppressWarnings({ "unchecked", "deprecation" }) public class SetBasedListBox extends AbstractBoundCollectionWidget implements HasFocus, SourcesFocusEvents, SourcesChangeEvents, HasEnabled { public static final String VALUE_PROPERTY_NAME = "value"; private static final Logger LOGGER = Logger.getLogger(SetBasedListBox.class.toString()); private ArrayList selected = new ArrayList(); private Collection options = new ArrayList(); protected com.google.gwt.user.client.ui.ListBox base; private Vector changeListeners = new Vector(); private boolean sortOptionsByToString = true; private ListAddItemHandler listAddItemHandler; private ToolbarButton addButton; public SetBasedListBox() { super(); init0(); } /** Creates a new instance of ListBox */ public SetBasedListBox(ListAddItemHandler listAddItemHandler) { super(); this.listAddItemHandler = listAddItemHandler; init0(); } public void addChangeListener(final ChangeListener listener) { this.changeListeners.add(listener); } public HandlerRegistration addClickHandler(ClickHandler handler) { return addDomHandler(handler, ClickEvent.getType()); } public void addClickListener(final ClickListener listener) { this.base.addClickListener(listener); } public HandlerRegistration addFocusHandler(FocusHandler handler) { return addDomHandler(handler, FocusEvent.getType()); } public void addFocusListener(final FocusListener listener) { this.base.addFocusListener(listener); } public void addItem(final Object o) { options.add(o); this.base.addItem((String) this.getRenderer().render(o)); } public void addKeyboardListener(KeyboardListener listener) { this.base.addKeyboardListener(listener); } public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) { return addDomHandler(handler, MouseDownEvent.getType()); } public void addStyleName(final String style) { this.base.addStyleName(style); } public boolean equals(final Object obj) { if (obj == null || !(obj instanceof SetBasedListBox)) { return false; } final SetBasedListBox other = (SetBasedListBox) obj; if ((this.options != other.options) && ((this.options == null) || !this.options.equals(other.options))) { return false; } return true; } public int getAbsoluteLeft() { int retValue; retValue = this.base.getAbsoluteLeft(); return retValue; } public int getAbsoluteTop() { int retValue; retValue = this.base.getAbsoluteTop(); return retValue; } public int getItemCount() { int retValue; retValue = this.base.getItemCount(); return retValue; } public String getItemText(int index) { String retValue; retValue = this.base.getItemText(index); return retValue; } public String getName() { String retValue; retValue = this.base.getName(); return retValue; } public int getOffsetHeight() { int retValue; retValue = this.base.getOffsetHeight(); return retValue; } public int getOffsetWidth() { int retValue; retValue = this.base.getOffsetWidth(); return retValue; } public Collection getOptions() { return options; } public int getSelectedIndex() { int retValue; retValue = this.base.getSelectedIndex(); return retValue; } public String getStyleName() { String retValue; retValue = this.base.getStyleName(); return retValue; } public int getTabIndex() { int retValue; retValue = this.base.getTabIndex(); return retValue; } public String getTitle() { String retValue; retValue = this.base.getTitle(); return retValue; } public Object getValue() { final Object returnValue; if (this.base.isMultipleSelect()) { SetBasedListBox.LOGGER.log(Level.SPAM, "IsMultipleSelect. Returning collection", null); returnValue = this.selected; } else if (this.selected.size() == 0) { returnValue = null; } else { SetBasedListBox.LOGGER.log(Level.SPAM, "NotMultipleSelect. Returning first item", null); returnValue = this.selected.get(0); } return returnValue; } public int getVisibleItemCount() { int retValue; retValue = this.base.getVisibleItemCount(); return retValue; } public int hashCode() { return this.base.hashCode(); } public boolean isEnabled() { boolean retValue; retValue = this.base.isEnabled(); return retValue; } public boolean isItemSelected(int index) { boolean retValue; retValue = this.base.isItemSelected(index); return retValue; } public boolean isMultipleSelect() { return this.base.isMultipleSelect(); } public boolean isSortOptionsByToString() { return sortOptionsByToString; } public Object provideOtherValue() { Iterator itr = getOptions().iterator(); Object value = getValue(); Object other = null; while (other == value && itr.hasNext()) { other = itr.next(); } return other; } public void removeChangeListener(final ChangeListener listener) { this.changeListeners.remove(listener); } public void removeClickListener(final ClickListener listener) { this.base.removeClickListener(listener); } public void removeFocusListener(final FocusListener listener) { this.base.removeFocusListener(listener); } public void removeItem(final int index) { this.base.removeItem(index); } public void removeItem(final Object o) { int i = 0; for (Iterator it = this.options.iterator(); it.hasNext(); i++) { Object option = it.next(); if (safeCompare(option, o) == 0) { this.options.remove(option); this.base.removeItem(i); this.update(); } } } public void removeKeyboardListener(final KeyboardListener listener) { this.base.removeKeyboardListener(listener); } public void removeStyleName(final String style) { this.base.removeStyleName(style); } public int safeCompare(Object value, Object item) { try { return this.getComparator().compare(value, item); } catch (ClassCastException e) { return value.getClass().getName().compareTo(item.getClass().getName()); } } public void setAccessKey(char key) { this.base.setAccessKey(key); } public void setEnabled(boolean enabled) { this.base.setEnabled(enabled); } public void setFocus(boolean focused) { this.base.setFocus(focused); } public void setHeight(String height) { this.base.setHeight(height); } public void setItemText(int index, String text) { this.base.setItemText(index, text); } public void setMultipleSelect(boolean multiple) { this.base.setMultipleSelect(multiple); if (this.selected.size() > 1) { Object o = this.selected.get(0); this.selected = new ArrayList(); this.selected.add(o); } } public void setName(String name) { this.base.setName(name); } public void setOptions(Collection options) { this.options = new ArrayList(); base.clear(); ArrayList newSelected = new ArrayList(); TextProvider.get().setDecorated(false); TextProvider.get().setTrimmed(true); if (isSortOptionsByToString()) { options = CommonUtils.sortByStringValue(options); } for (Iterator it = options.iterator(); it.hasNext();) { Object item = it.next(); this.base.addItem((String) this.getRenderer().render(item)); if (contains(this.selected, item)) { this.base.setItemSelected(this.base.getItemCount() - 1, true); newSelected.add(item); } this.options.add(item); } TextProvider.get().setDecorated(true); TextProvider.get().setTrimmed(false); ArrayList old = this.selected; this.selected = newSelected; if (this.isMultipleSelect()) { fireAdaptedChange(VALUE_PROPERTY_NAME, old, selected); } else { Object prev = ((old == null) || (old.size() == 0)) ? null : old.get(0); Object curr = (this.selected.size() == 0) ? null : this.selected.get(0); // ignore null-null changes if (prev != curr) { changes.firePropertyChange(VALUE_PROPERTY_NAME, prev, curr); } } fireChangeListeners(); } public void setPixelSize(int width, int height) { this.base.setPixelSize(width, height); } public void setRenderer(Renderer renderer) { super.setRenderer(renderer); this.setOptions(this.options); } public void setSize(String width, String height) { this.base.setSize(width, height); } public void setSortOptionsByToString(boolean sortOptions) { this.sortOptionsByToString = sortOptions; } public void setStyleName(String style) { this.base.setStyleName(style); } public void setTabIndex(int index) { this.base.setTabIndex(index); } public void setTitle(String title) { this.base.setTitle(title); } public void setValue(Object value) { int i = 0; ArrayList old = this.selected; this.selected = new ArrayList(); if (value instanceof Collection) { Collection c = (Collection) value; for (Iterator it = this.options.iterator(); it.hasNext(); i++) { Object item = it.next(); if (contains(c, item)) { base.setItemSelected(i, true); this.selected.add(item); } else { base.setItemSelected(i, false); } } } else { for (Iterator it = this.options.iterator(); it.hasNext(); i++) { Object item = it.next(); if (safeCompare(value, item) == 0) { base.setItemSelected(i, true); } else { base.setItemSelected(i, false); } } this.selected.add(value); } if (this.isMultipleSelect()) { fireAdaptedChange(VALUE_PROPERTY_NAME, old, selected); } else { Object prev = ((old == null) || (old.size() == 0)) ? null : old.get(0); Object curr = (this.selected.size() == 0) ? null : this.selected.get(0); if (prev != curr) { changes.firePropertyChange(VALUE_PROPERTY_NAME, prev, curr); } } fireChangeListeners(); } public void setVisibleItemCount(final int visibleItems) { this.base.setVisibleItemCount(visibleItems); } public void setWidth(final String width) { this.base.setWidth(width); } private void fireAdaptedChange(String propertyName, List old, List selected) { changes.firePropertyChange(VALUE_PROPERTY_NAME, new HashSet(old), new HashSet(selected)); } private void fireChangeListeners() { for (Iterator it = this.changeListeners.iterator(); it.hasNext();) { ChangeListener l = (ChangeListener) it.next(); l.onChange(this); } if (this.getAction() != null) { this.getAction().execute(this); } } private void init0() { this.base = new com.google.gwt.user.client.ui.ListBox(); this.setRenderer(ToStringRenderer.INSTANCE); this.setComparator(SimpleComparator.INSTANCE); this.base.addClickListener(new ClickListener() { public void onClick(Widget sender) { update(); } }); this.base.addChangeListener(new ChangeListener() { public void onChange(Widget sender) { update(); } // foo! }); Widget delegate = base; if (listAddItemHandler != null) { FlowPanel fp = new FlowPanel(); fp.setStyleName("nowrap"); delegate = fp; fp.add(base); InlineButtonHandler addItemAction = new AddItemHandler(); addButton = new ToolbarButton(addItemAction, true); fp.add(UsefulWidgetFactory.createSpacer(2)); fp.add(addButton); } super.initWidget(delegate); } private void update() { ArrayList selected = new ArrayList(); Iterator it = this.options.iterator(); for (int i = 0; (i < base.getItemCount()) && it.hasNext(); i++) { Object item = it.next(); if (this.base.isItemSelected(i)) { selected.add(item); } } ArrayList old = this.selected; this.selected = selected; if (this.isMultipleSelect()) { fireAdaptedChange(VALUE_PROPERTY_NAME, old, selected); } else { Object prev = ((old == null) || (old.size() == 0)) ? null : old.get(0); Object curr = (this.selected.size() == 0) ? null : this.selected.get(0); if (prev == null && curr == null) { return;// pcs is not MutablePropertyChangeLister } changes.firePropertyChange(VALUE_PROPERTY_NAME, prev, curr); } fireChangeListeners(); } protected boolean contains(final Collection c, final Object o) { for (Iterator it = c.iterator(); it.hasNext();) { Object next = it.next(); if (safeCompare(o, next) == 0) { return true; } } return false; } @Override protected void onDetach() { super.onDetach(); } public static class DomainListBox extends SetBasedListBox { private Class domainClass; private CollectionFilter filter; private boolean hasNullOption; private boolean refreshOnModelChange; public DomainListBox(Class domainClass, CollectionFilter filter, boolean hasNullOption, ListAddItemHandler addHandler) { super(addHandler); this.domainClass = domainClass; this.filter = filter; this.hasNullOption = hasNullOption; if (!(filter instanceof RequiresContextBindable)) { refreshOptions(); } } public CollectionFilter getFilter() { return this.filter; } public boolean isHasNullOption() { return this.hasNullOption; } public boolean isRefreshOnModelChange() { return this.refreshOnModelChange; } public void refreshOptions() { Collection<HasId> collection = TransformManager.get().getCollection(domainClass); ArrayList options = new ArrayList(); if (filter == null) { options.addAll(collection); } else { if (filter instanceof RequiresContextBindable) { ((RequiresContextBindable) filter).setBindable((SourcesPropertyChangeEvents) getModel()); } Iterator itr = collection.iterator(); while (itr.hasNext()) { Object obj = itr.next(); if (filter.allow(obj)) { options.add(obj); } } } if (!isSortOptionsByToString()) { if (getComparator() != null) { Collections.sort(options, getComparator()); } else { if (!options.isEmpty() && options.get(0) instanceof Comparable) { Collections.sort(options); } } } if (hasNullOption) { options.add(0, null); } setOptions(options); } public void setFilter(CollectionFilter filter) { this.filter = filter; refreshOptions(); } public void setHasNullOption(boolean hasNullOption) { this.hasNullOption = hasNullOption; refreshOptions(); } @Override public void setModel(Object model) { super.setModel(model); if (model != null) { refreshOptions(); } ensureModelListener(true); } private SourcesPropertyChangeEvents listenedModel; private PropertyChangeListener refreshListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { refreshOptions(); } }; private void ensureModelListener(boolean add) { if (listenedModel != null) { listenedModel.removePropertyChangeListener(refreshListener); listenedModel = null; } if (add) { if (getModel() instanceof SourcesPropertyChangeEvents && isAttached() && isRefreshOnModelChange()) { listenedModel = (SourcesPropertyChangeEvents) getModel(); listenedModel.addPropertyChangeListener(refreshListener); } } } @Override protected void onAttach() { super.onAttach(); ensureModelListener(true); } @Override protected void onDetach() { super.onDetach(); ensureModelListener(false); } public void setRefreshOnModelChange(boolean refreshOnModelChange) { this.refreshOnModelChange = refreshOnModelChange; } } private class AddItemHandler extends InlineButtonHandler { @Override public String getDisplayName() { return "+"; } @Override public void onClick(ClickEvent event) { String validationMessage = listAddItemHandler.validateCanAdd(getValue()); if (validationMessage != null) { Registry.impl(ClientNotifications.class).showMessage(validationMessage); return; } String namePrompt = listAddItemHandler.getPrompt(); String nameValue = null; Callback<String> actionCallback = new Callback<String>() { @Override public void apply(String nameValue) { Object newItem = listAddItemHandler.createNewItem(nameValue); List optionsCopy = new ArrayList(getOptions()); optionsCopy.add(newItem); setOptions(optionsCopy); setValue(newItem); } }; Callback<OkCancelDialogBox> positioningCallback = new Callback<OkCancelDialogBox>() { @Override public void apply(OkCancelDialogBox box) { box.setPopupPosition(addButton.getAbsoluteLeft(), addButton.getAbsoluteTop() + addButton.getOffsetHeight()); } }; if (namePrompt != null) { String defaultName = listAddItemHandler.getDefaultName(); new Prompter("Message", namePrompt, defaultName, null, positioningCallback, actionCallback); } else { actionCallback.apply(null); } } } }