package org.activityinfo.ui.client.component.filter; import com.extjs.gxt.ui.client.Style; import com.extjs.gxt.ui.client.event.*; import com.extjs.gxt.ui.client.store.ListStore; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.ListView; import com.extjs.gxt.ui.client.widget.ListViewSelectionModel; import com.extjs.gxt.ui.client.widget.button.Button; import com.extjs.gxt.ui.client.widget.form.ComboBox; import com.extjs.gxt.ui.client.widget.layout.RowData; import com.extjs.gxt.ui.client.widget.layout.RowLayout; import com.google.common.base.Function; import com.google.common.base.Strings; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.inject.Inject; import org.activityinfo.i18n.shared.I18N; import org.activityinfo.legacy.client.Dispatcher; import org.activityinfo.legacy.shared.Log; import org.activityinfo.legacy.shared.command.DimensionType; import org.activityinfo.legacy.shared.command.Filter; import org.activityinfo.legacy.shared.command.GetLocations; import org.activityinfo.legacy.shared.command.SearchLocations; import org.activityinfo.legacy.shared.command.result.LocationResult; import org.activityinfo.legacy.shared.model.LocationDTO; import org.activityinfo.promise.Promise; import org.activityinfo.ui.client.component.filter.FilterToolBar.ApplyFilterEvent; import org.activityinfo.ui.client.component.filter.FilterToolBar.ApplyFilterHandler; import org.activityinfo.ui.client.component.filter.FilterToolBar.RemoveFilterEvent; import org.activityinfo.ui.client.component.filter.FilterToolBar.RemoveFilterHandler; import org.activityinfo.ui.client.style.legacy.icon.IconImageBundle; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; public class LocationFilterPanel extends ContentPanel implements FilterPanel { private static final int TIMER_DELAY = 100; private Dispatcher dispatcher; private FilterToolBar filterToolBar; private Filter baseFilter = new Filter(); private Filter value = new Filter(); private ListStore<LocationDTO> store = new ListStore<>(); private ListStore<LocationDTO> filterComboboxStore = new ListStore<>(); private ComboBox<LocationDTO> filterCombobox; private ListView<LocationDTO> listView; private ListViewSelectionModel<LocationDTO> listSelectionModel = new ListViewSelectionModel<LocationDTO>(); private Button removeSelectedItem = new Button(I18N.CONSTANTS.removeSelectedLocations(), IconImageBundle.ICONS.remove()); private final Timer fetchLocationsTimer = new Timer() { @Override public void run() { fetchLocations(); } }; @Inject public LocationFilterPanel(Dispatcher dispatcher) { this.dispatcher = dispatcher; initializeComponent(); createFilterToolBar(); createList(); } private void initializeComponent() { setHeadingText(I18N.CONSTANTS.filterByLocation()); setIcon(IconImageBundle.ICONS.filter()); setLayout(new RowLayout(Style.Orientation.VERTICAL)); setScrollMode(Style.Scroll.NONE); setHeadingText(I18N.CONSTANTS.filterByLocation()); setIcon(IconImageBundle.ICONS.filter()); } private void createFilterToolBar() { filterToolBar = new FilterToolBar(); filterToolBar.add(removeSelectedItem); filterToolBar.addApplyFilterHandler(new ApplyFilterHandler() { @Override public void onApplyFilter(ApplyFilterEvent deleteEvent) { applyFilter(); } }); filterToolBar.addRemoveFilterHandler(new RemoveFilterHandler() { @Override public void onRemoveFilter(RemoveFilterEvent deleteEvent) { clearFilter(); ValueChangeEvent.fire(LocationFilterPanel.this, value); } }); setTopComponent(filterToolBar); removeSelectedItem.setEnabled(false); removeSelectedItem.addSelectionListener(new SelectionListener<ButtonEvent>() { @Override public void componentSelected(ButtonEvent ce) { for (LocationDTO location : listSelectionModel.getSelection()) { store.remove(location); } } }); listSelectionModel.addSelectionChangedListener(new SelectionChangedListener<LocationDTO>() { @Override public void selectionChanged(SelectionChangedEvent<LocationDTO> se) { removeSelectedItem.setEnabled(se.getSelectedItem() != null); } }); } protected void applyFilter() { value = new Filter(); if (isRendered()) { List<Integer> selectedIds = getSelectedIds(); if (selectedIds.size() > 0) { value.addRestriction(DimensionType.Location, getSelectedIds()); } } ValueChangeEvent.fire(this, value); filterToolBar.setApplyFilterEnabled(false); filterToolBar.setRemoveFilterEnabled(true); } private List<Integer> getSelectedIds() { List<Integer> list = new ArrayList<Integer>(); for (LocationDTO model : store.getModels()) { list.add(model.getId()); } return list; } private void createList() { filterCombobox = new ComboBox<LocationDTO>(); filterCombobox.setEmptyText(I18N.CONSTANTS.searchForLocationToAdd()); filterCombobox.setDisplayField("name"); filterCombobox.setStore(filterComboboxStore); filterCombobox.setTypeAhead(true); filterCombobox.addSelectionChangedListener(new SelectionChangedListener<LocationDTO>() { @Override public void selectionChanged(SelectionChangedEvent<LocationDTO> se) { if (se.getSelectedItem() != null && !store.contains(se.getSelectedItem())) { store.add(se.getSelectedItem()); filterToolBar.setApplyFilterEnabled(true); filterCombobox.setRawValue(""); // reset typed filter } } }); filterCombobox.addKeyListener(new KeyListener() { @Override public void componentKeyUp(ComponentEvent event) { String nameFilter = filterCombobox.getRawValue(); if (!Strings.isNullOrEmpty(nameFilter)) { fetchLocationsTimer.schedule(TIMER_DELAY); } } }); listView = new ListView<LocationDTO>(); listView.setStore(store); listView.setDisplayProperty("name"); listView.setSelectionModel(listSelectionModel); add(filterCombobox, new RowData(1, -1)); add(listView, new RowData(1, 1)); } protected void clearFilter() { store.removeAll(); value = new Filter(); filterToolBar.setApplyFilterEnabled(false); filterToolBar.setRemoveFilterEnabled(false); } @Override public Filter getValue() { return value; } @Override public void setValue(Filter value) { setValue(value, false); } @Override public void setValue(final Filter value, final boolean fireEvents) { this.value = new Filter(); this.value.addRestriction(DimensionType.Location, value.getRestrictions(DimensionType.Location)); applyInternalValue() .then(new Function<LocationResult, Object>() { @Nullable @Override public Object apply(@Nullable LocationResult input) { if (fireEvents) { ValueChangeEvent.fire(LocationFilterPanel.this, value); } return null; } }); } private Promise<LocationResult> applyInternalValue() { Promise<LocationResult> promise = new Promise<>(); promise.then(new Function<LocationResult, Object>() { @Override public Object apply(LocationResult input) { filterToolBar.setApplyFilterEnabled(false); filterToolBar.setRemoveFilterEnabled(value.isRestricted(DimensionType.Location)); return null; } }); dispatcher.execute(new GetLocations(new ArrayList<>(value.getRestrictions(DimensionType.Location))), promise); return promise; } @Override public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Filter> handler) { return addHandler(handler, ValueChangeEvent.getType()); } @Override public void applyBaseFilter(Filter rawFilter) { final Filter filter = new Filter(rawFilter); filter.clearRestrictions(DimensionType.Location); // avoid fetching a list of ALL locations if no indicators have been selected if (!filter.isRestricted(DimensionType.Indicator)) { filterComboboxStore.removeAll(); return; } if (baseFilter == null || !baseFilter.equals(filter)) { baseFilter = filter; fetchLocationsTimer.schedule(TIMER_DELAY); } } private void fetchLocations() { if (baseFilter == null || baseFilter.isNull()) { return; } SearchLocations searchLocations = new SearchLocations() .setName(filterCombobox.getRawValue()) .setIndicatorIds(baseFilter.getRestrictions(DimensionType.Indicator)) .setLimit(100); dispatcher.execute(searchLocations, new AsyncCallback<LocationResult>() { @Override public void onFailure(Throwable caught) { Log.error(caught.getMessage(), caught); } @Override public void onSuccess(LocationResult result) { filterComboboxStore.removeAll(); filterComboboxStore.add(result.getData()); } }); } }