/** * geo-platform * Rich webgis framework * http://geo-platform.org * ==================================================================== * <p> * Copyright (C) 2008-2017 geoSDI Group (CNR IMAA - Potenza - ITALY). * <p> * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. This program 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 General Public License * for more details. You should have received a copy of the GNU General * Public License along with this program. If not, see http://www.gnu.org/licenses/ * <p> * ==================================================================== * <p> * Linking this library statically or dynamically with other modules is * making a combined work based on this library. Thus, the terms and * conditions of the GNU General Public License cover the whole combination. * <p> * As a special exception, the copyright holders of this library give you permission * to link this library with independent modules to produce an executable, regardless * of the license terms of these independent modules, and to copy and distribute * the resulting executable under terms of your choice, provided that you also meet, * for each linked independent module, the terms and conditions of the license of * that module. An independent module is a module which is not derived from or * based on this library. If you modify this library, you may extend this exception * to your version of the library, but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. */ package org.geosdi.geoplatform.gui.client.widget.wfs; import com.extjs.gxt.ui.client.Style; import com.extjs.gxt.ui.client.data.ModelData; import com.extjs.gxt.ui.client.data.ModelKeyProvider; import com.extjs.gxt.ui.client.event.*; import com.extjs.gxt.ui.client.store.ListStore; import com.extjs.gxt.ui.client.store.Record; import com.extjs.gxt.ui.client.store.StoreEvent; import com.extjs.gxt.ui.client.store.StoreListener; import com.extjs.gxt.ui.client.widget.grid.CellEditor; import com.extjs.gxt.ui.client.widget.grid.ColumnConfig; import com.extjs.gxt.ui.client.widget.grid.ColumnModel; import com.extjs.gxt.ui.client.widget.grid.EditorGrid; import com.google.common.collect.Lists; import com.google.gwt.event.dom.client.FocusEvent; import com.google.gwt.event.dom.client.FocusHandler; import com.google.gwt.user.client.Timer; import org.geosdi.geoplatform.connector.wfs.response.AttributeDTO; import org.geosdi.geoplatform.gui.client.model.binder.ILayerSchemaBinder; import org.geosdi.geoplatform.gui.client.model.wfs.FeatureDetail; import org.geosdi.geoplatform.gui.client.puregwt.map.event.FeatureMapHeightEvent; import org.geosdi.geoplatform.gui.client.puregwt.map.event.IncreaseHeightEvent; import org.geosdi.geoplatform.gui.client.puregwt.wfs.event.FeatureStatusBarEvent; import org.geosdi.geoplatform.gui.client.puregwt.wfs.event.ResetStatusBarEvent; import org.geosdi.geoplatform.gui.client.puregwt.wfs.handler.FeatureAttributesHandler; import org.geosdi.geoplatform.gui.client.puregwt.wfs.handler.IDateSelectedHandler; import org.geosdi.geoplatform.gui.client.puregwt.wfs.handler.WFSFeatureBindingHandler.WFSFeatureBindingEvent; import org.geosdi.geoplatform.gui.client.widget.GeoPlatformContentPanel; import org.geosdi.geoplatform.gui.client.widget.wfs.builder.AttributeCustomFieldsMap; import org.geosdi.geoplatform.gui.client.widget.wfs.builder.GetFeatureControlBuilder; import org.geosdi.geoplatform.gui.client.widget.wfs.statusbar.FeatureStatusBar; import org.geosdi.geoplatform.gui.client.widget.wfs.statusbar.FeatureStatusBar.FeatureStatusBarType; import org.geosdi.geoplatform.gui.client.widget.wfs.time.TimeInputWidget; import org.geosdi.geoplatform.gui.configuration.GPSecureStringTextField; import org.geosdi.geoplatform.gui.configuration.action.event.ActionEnableEvent; import org.geosdi.geoplatform.gui.puregwt.GPEventBus; import org.gwtopenmaps.openlayers.client.feature.VectorFeature; import org.gwtopenmaps.openlayers.client.protocol.WFSProtocolCRUDOptions; import javax.inject.Inject; import java.util.ArrayList; import java.util.List; /** * @author Vincenzo Monteverde <vincenzo.monteverde@geosdi.org> * @author Giuseppe La Scaleia <giuseppe.lascaleia@geosdi.org> */ public class FeatureAttributesWidget extends GeoPlatformContentPanel implements FeatureAttributesHandler, IDateSelectedHandler { static { mockColumnModel = new ColumnModel(new ArrayList<ColumnConfig>()); } public static final String ID = WFSWidgetNames.FEATURE_ATTRIBUTES.name(); private static final ColumnModel mockColumnModel; // @Inject private ILayerSchemaBinder layerSchemaBinder; private final GPEventBus bus; private final TimeInputWidget timeInputWidget; private ListStore<FeatureDetail> store; private EditorGrid<FeatureDetail> grid; // private final FeatureMapHeightEvent increaseHeightEvent = new IncreaseHeightEvent(); // private String dataAttributeName; // private final GetFeatureControlBuilder featureControlBuilder; private final WFSProtocolCRUDOptions featureCRUDProtocol; private final FeatureStatusBarEvent successStatusBarEvent = new FeatureStatusBarEvent("", FeatureStatusBarType.STATUS_OK); ResetStatusBarEvent resetStatusBarEvent = new ResetStatusBarEvent(); @Inject public FeatureAttributesWidget(GPEventBus bus, TimeInputWidget timeInputWidget, GetFeatureControlBuilder featureControlBuilder, WFSProtocolCRUDOptions featureCRUDProtocol) { super(Boolean.TRUE); this.bus = bus; this.timeInputWidget = timeInputWidget; this.bus.addHandlerToSource(IDateSelectedHandler.TYPE, timeInputWidget, this); this.bus.addHandler(FeatureAttributesHandler.TYPE, this); this.featureControlBuilder = featureControlBuilder; this.featureCRUDProtocol = featureCRUDProtocol; super.setMonitorWindowResize(Boolean.TRUE); addWidgetListener(new WidgetListener() { @Override public void widgetResized(ComponentEvent ce) { if ((getWidth() > 0)) { grid.setWidth(getWidth()); } } }); } public void reconfigureEditorGrid() { this.grid.reconfigure(store, this.prepareColumnModel()); } @Override protected void beforeRender() { super.beforeRender(); this.grid.reconfigure(store, this.prepareColumnModel()); } @Override protected void afterRender() { super.afterRender(); super.setId(ID); } @Override public void addComponent() { this.createStore(); this.createEditorGrid(); } @Override public void initSize() { } protected void manageGridHeight() { this.grid.setHeight(super.getHeight() - 25); } @Override public void collapse() { this.increaseHeightEvent.setHeight(getHeight()); this.bus.fireEvent(increaseHeightEvent); super.collapse(); } @Override public void setPanelProperties() { } @Override public void reset() { grid.stopEditing(Boolean.TRUE); store.removeAll(); bus.fireEvent(new ActionEnableEvent(Boolean.FALSE)); super.setVScrollPosition(0); } @Override public void dateSelected(String date) { FeatureDetail featureAttributeValuesDetail = this.grid. getSelectionModel().getSelectedItem(); if (featureAttributeValuesDetail != null) { featureAttributeValuesDetail.setValue(dataAttributeName, date); store.update(featureAttributeValuesDetail); } } private void createStore() { store = new ListStore<FeatureDetail>(); store.setKeyProvider(new ModelKeyProvider<FeatureDetail>() { @Override public String getKey(FeatureDetail model) { return model.getFeatureID(); } }); store.addStoreListener(new StoreListener<FeatureDetail>() { @Override public void storeClear(StoreEvent<FeatureDetail> se) { bus.fireEvent(new ActionEnableEvent(false)); } @Override public void storeUpdate(StoreEvent<FeatureDetail> se) { bus.fireEvent(new ActionEnableEvent(true)); } }); } private void createEditorGrid() { grid = new EditorGrid<FeatureDetail>(store, mockColumnModel); grid.setBorders(Boolean.TRUE); grid.setStripeRows(Boolean.TRUE); grid.setColumnLines(Boolean.TRUE); grid.setColumnResize(Boolean.TRUE); grid.setHeight(125); grid.setClicksToEdit(EditorGrid.ClicksToEdit.TWO); grid.getSelectionModel().setSelectionMode(Style.SelectionMode.SIMPLE); grid.addListener(Events.CellClick, new Listener<BaseEvent>() { @Override public void handleEvent(BaseEvent be) { FeatureDetail selectedItem = grid.getSelectionModel().getSelectedItem(); bus.fireEvent(new WFSFeatureBindingEvent(selectedItem.getVectorFeature())); } }); super.add(grid); } private ColumnModel prepareColumnModel() { List<AttributeDTO> attributesDTO = this.layerSchemaBinder.getLayerSchemaDTO().getAttributes(); List<ColumnConfig> configs = Lists.<ColumnConfig>newArrayListWithCapacity(attributesDTO.size()); int width = (getWidth() - 2) / attributesDTO.size(); for (final AttributeDTO att : attributesDTO) { final GPSecureStringTextField valueTextField = new GPSecureStringTextField(); valueTextField.setValidator(AttributeCustomFieldsMap.getValidatorForAttributeType(att.getType())); valueTextField.setAutoValidate(true); if (att.isDateType()) { FocusHandler focusHandler = new FocusHandler() { @Override public void onFocus(FocusEvent event) { dataAttributeName = att.getName(); timeInputWidget.show(); } }; valueTextField.addHandler(focusHandler, FocusEvent.getType()); } ColumnConfig valueColumn = new ColumnConfig(); String name = att.getName(); valueColumn.setId(name); valueColumn.setHeaderHtml(name); valueColumn.setEditor(buildCellEditor(valueTextField)); valueColumn.setWidth(width); valueColumn.setToolTip("Datatype: " + att.getType()); configs.add(valueColumn); } return new ColumnModel(configs); } @Override public void saveAttributes() { final List<VectorFeature> features = new ArrayList<VectorFeature>(); for (Record record : store.getModifiedRecords()) { ModelData model = record.getModel(); FeatureDetail attribute = (FeatureDetail) model; for (String name : attribute.getProperties().keySet()) { if ((name != null) && !(name.isEmpty())) { attribute.getVectorFeature().getAttributes().setAttribute(name, attribute.getValue(name)); } } attribute.getVectorFeature().toState(VectorFeature.State.Update); features.add(attribute.getVectorFeature()); } this.bus.fireEvent(new FeatureStatusBarEvent("Transaction in Progress", FeatureStatusBarType.STATUS_LOADING)); Timer t = new Timer() { @Override public void run() { featureControlBuilder.getWfsProtocol().commit(features.toArray(new VectorFeature[features.size()]), featureCRUDProtocol); } }; t.schedule(2000); } @Override public void resetAttributes() { grid.stopEditing(true); store.rejectChanges(); bus.fireEvent(new ActionEnableEvent(false)); } @Override public void postInstances(List<FeatureDetail> instaces) { assert (instaces != null) : "Feature instances must not be null."; int numFeature = instaces.size(); for (FeatureDetail featureDetail : instaces) { logger.fine("##################################FID : " + featureDetail.getFeatureID()); } grid.mask("Retrieve " + numFeature + " feature instance attributes"); this.populateStore(instaces); grid.unmask(); bus.fireEvent(new FeatureStatusBarEvent("Features Loaded " + numFeature, FeatureStatusBar.FeatureStatusBarType.STATUS_OK)); } private void populateStore(List<FeatureDetail> attValues) { store.removeAll(); store.add(attValues); } @Override public void resetInstances() { this.reset(); } @Override public void removeFeatureDetail(String featureID) { FeatureDetail featureDetail = this.store.findModel(featureID); if (featureDetail != null) { store.remove(featureDetail); grid.stopEditing(Boolean.TRUE); } this.bus.fireEvent(new FeatureStatusBarEvent("Feature Successfully Deleted", FeatureStatusBarType.STATUS_OK)); this.bus.fireEvent(new ActionEnableEvent(Boolean.FALSE)); } @Override public void successfulTransaction() { this.bus.fireEvent(new FeatureStatusBarEvent("Successful Transaction", FeatureStatusBarType.STATUS_OK)); store.commitChanges(); bus.fireEvent(new ActionEnableEvent(Boolean.FALSE)); } @Override public void maskAttributes(boolean mask) { if (mask) { grid.mask("Retrieving feature attributes"); } else { grid.unmask(); } } final CellEditor buildCellEditor(GPSecureStringTextField textField) { CellEditor valueEditor = new CellEditor(textField) { @Override public Object postProcessValue(Object value) { if (value == null) { return value; } successStatusBarEvent.setText("The value \"" + value + "\" is correct"); bus.fireEvent(successStatusBarEvent); return value; } }; valueEditor.addListener(Events.CancelEdit, new Listener<EditorEvent>() { @Override public void handleEvent(EditorEvent be) { bus.fireEvent(resetStatusBarEvent); } }); return valueEditor; } }