[Commits] r2183 - in extensions/geoext.ux: examples tests ux ux/FeatureEditing ux/FeatureEditing/examples ux/FeatureEditing/lib ux/FeatureEditing/lib/GeoExt.ux ux/FeatureEditing/tests ux/FeatureEditing/tests/lib ux/FeatureEditing/tests/lib/GeoExt.ux
commits at geoext.org
commits at geoext.org
Wed May 12 16:29:54 CEST 2010
Author: pgiraud
Date: 2010-05-12 16:29:54 +0200 (Wed, 12 May 2010)
New Revision: 2183
Added:
extensions/geoext.ux/ux/FeatureEditing/
extensions/geoext.ux/ux/FeatureEditing/examples/
extensions/geoext.ux/ux/FeatureEditing/examples/feature-editor-grid.html
extensions/geoext.ux/ux/FeatureEditing/examples/feature-editor-grid.js
extensions/geoext.ux/ux/FeatureEditing/lib/
extensions/geoext.ux/ux/FeatureEditing/lib/GeoExt.ux/
extensions/geoext.ux/ux/FeatureEditing/lib/GeoExt.ux/FeatureEditorGrid.js
extensions/geoext.ux/ux/FeatureEditing/tests/
extensions/geoext.ux/ux/FeatureEditing/tests/index.html
extensions/geoext.ux/ux/FeatureEditing/tests/lib/
extensions/geoext.ux/ux/FeatureEditing/tests/lib/GeoExt.ux/
extensions/geoext.ux/ux/FeatureEditing/tests/lib/GeoExt.ux/FeatureEditorGrid.html
Modified:
extensions/geoext.ux/examples/index.html
extensions/geoext.ux/tests/list-tests.html
Log:
FeatureEditorGrid: new ux for feature editing, extends Ext.grid.EditorGrid and makes usage of AttributeStore, contributors=aabt,elemoine,pgiraud, maintainers=elemoine,pgiraud
Modified: extensions/geoext.ux/examples/index.html
===================================================================
--- extensions/geoext.ux/examples/index.html 2010-05-12 13:39:00 UTC (rev 2182)
+++ extensions/geoext.ux/examples/index.html 2010-05-12 14:29:54 UTC (rev 2183)
@@ -1,14 +1,15 @@
<html>
<head>
<title>GeoExt.ux examples</title>
- </head>
- <body>
- <h2>GeoExt.ux examples</h2>
- <ul>
- <li><a href="../ux/GeoNamesSearchCombo/examples/GeoNamesSearchComboExample.html">GeoNames search combobox</a></li>
- <li><a href="../ux/PrintPreview/examples/PrintPreview.html">PrintPreview using GeoExt.data.PrintProvider</a></li>
- <li><a href="../ux/SimplePrint/examples/SimplePrint.html">SimplePrint Form using GeoExt.data.PrintProvider</a></li>
- <li><a href="../ux/Measure/examples/measure.html">Measure tools</a></li>
+ </head>
+ <body>
+ <h2>GeoExt.ux examples</h2>
+ <ul>
+ <li><a href="../ux/GeoNamesSearchCombo/examples/GeoNamesSearchComboExample.html">GeoNames search combobox</a></li>
+ <li><a href="../ux/PrintPreview/examples/PrintPreview.html">PrintPreview using GeoExt.data.PrintProvider</a></li>
+ <li><a href="../ux/SimplePrint/examples/SimplePrint.html">SimplePrint Form using GeoExt.data.PrintProvider</a></li>
+ <li><a href="../ux/Measure/examples/measure.html">Measure tools</a></li>
+ <li><a href="../ux/FeatureEditing/examples/feature-editor-grid.html">Feature editor grid</a></li>
</ul>
</body>
</html>
Modified: extensions/geoext.ux/tests/list-tests.html
===================================================================
--- extensions/geoext.ux/tests/list-tests.html 2010-05-12 13:39:00 UTC (rev 2182)
+++ extensions/geoext.ux/tests/list-tests.html 2010-05-12 14:29:54 UTC (rev 2183)
@@ -5,4 +5,5 @@
<li>../../geoext.ux/ux/Measure/tests/lib/GeoExt.ux/Measure.html</li>
<li>../../geoext.ux/ux/Measure/tests/lib/GeoExt.ux/MeasureArea.html</li>
<li>../../geoext.ux/ux/Measure/tests/lib/GeoExt.ux/MeasureLength.html</li>
+ <li>../../geoext.ux/ux/FeatureEditing/tests/lib/GeoExt.ux/FeatureEditorGrid.html</li>
</ul>
Added: extensions/geoext.ux/ux/FeatureEditing/examples/feature-editor-grid.html
===================================================================
--- extensions/geoext.ux/ux/FeatureEditing/examples/feature-editor-grid.html (rev 0)
+++ extensions/geoext.ux/ux/FeatureEditing/examples/feature-editor-grid.html 2010-05-12 14:29:54 UTC (rev 2183)
@@ -0,0 +1,25 @@
+<html>
+ <head>
+ <title>Feature Editor Grid</title>
+
+ <link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-3.1.1/resources/css/ext-all.css" />
+ <link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-3.1.1/examples/shared/examples.css" />
+ <script type="text/javascript" src="http://extjs.cachefly.net/ext-3.1.1/adapter/ext/ext-base.js"></script>
+ <script type="text/javascript" src="http://extjs.cachefly.net/ext-3.1.1/ext-all-debug.js"></script>
+ <script type="text/javascript" src="http://www.openlayers.org/api/2.9/OpenLayers.js"></script>
+ <script type="text/javascript" src="../../../../geoext/lib/GeoExt.js"></script>
+ <script type="text/javascript" src="../lib/GeoExt.ux/FeatureEditorGrid.js"></script>
+
+ <script type="text/javascript" src="feature-editor-grid.js"></script>
+
+ </head>
+ <body>
+ <h1></h1>
+
+ <p>When the Add feature button is clicked, you can click on the map to add a feature. When done, the feature attributes can be changed in the editing grid. Available values for symbol are for example "square", "star", "circle". The symbol on the map should reflect the changes in the editing grid.</p>
+
+ <p>See <a href="feature-editor-grid.js">feature-editor-grid.js</a>.</p>
+
+ <div id="panel"></div>
+ </body>
+</html>
Added: extensions/geoext.ux/ux/FeatureEditing/examples/feature-editor-grid.js
===================================================================
--- extensions/geoext.ux/ux/FeatureEditing/examples/feature-editor-grid.js (rev 0)
+++ extensions/geoext.ux/ux/FeatureEditing/examples/feature-editor-grid.js 2010-05-12 14:29:54 UTC (rev 2183)
@@ -0,0 +1,211 @@
+/**
+ * Copyright (c) 2008-2010 The Open Source Geospatial Foundation
+ *
+ * Published under the BSD license.
+ * See http://svn.geoext.org/core/trunk/geoext/license.txt for the full text
+ * of the license.
+ */
+
+/** api: example[feature-editing]
+ */
+
+var mapPanel, editingContainer, selectFeature;
+
+function closeEditing() {
+ // avoid reentrance
+ if(!arguments.callee._in) {
+ arguments.callee._in = true;
+ editingContainer.removeAll(true);
+ selectFeature.unselectAll();
+ delete arguments.callee._in;
+ }
+}
+
+function createVectorLayer() {
+
+ var styleDefault = Ext.applyIf({
+ graphicName: "${symbol}",
+ pointRadius: "${size}"
+ }, OpenLayers.Feature.Vector.style["default"]);
+
+ var styleSelect = Ext.applyIf({
+ graphicName: "${symbol}",
+ pointRadius: "${size}"
+ }, OpenLayers.Feature.Vector.style["select"]);
+
+ return new OpenLayers.Layer.Vector("vector", {
+ styleMap: new OpenLayers.StyleMap({
+ "default": styleDefault,
+ "select": styleSelect
+ }),
+ eventListeners: {
+ beforefeatureadded: function(e) {
+ closeEditing();
+ selectFeature.select(e.feature);
+ },
+ beforefeatureselected: function(e) {
+ addEditorGrid(e.feature);
+ },
+ featureunselected: function(e) {
+ closeEditing();
+ }
+ }
+ });
+}
+
+function addEditorGrid(feature) {
+
+ var store = new GeoExt.data.AttributeStore({
+ feature: feature,
+ fields: ["name", "type", "restriction", "label"],
+ data: [{
+ name: "symbol",
+ label: "Symbol",
+ type: {
+ xtype: "combo",
+ store: new Ext.data.ArrayStore({
+ fields: ['symbol'],
+ data: [['star'], ['square'], ['circle']]
+ }),
+ displayField: 'symbol',
+ valueField: 'symbol',
+ mode: 'local',
+ editable: false,
+ forceSelection: true,
+ triggerAction: 'all',
+ selectOnFocus: true
+ },
+ value: "star"
+ }, {
+ name: "size",
+ label: "Size",
+ type: "number",
+ value: 6,
+ restriction: {
+ "minInclusive": 0,
+ "maxInclusive": 10
+ }
+ }]
+ });
+
+ var editorGrid = new GeoExt.ux.FeatureEditorGrid({
+ nameField: "label",
+ store: store,
+ forceValidation: true,
+ allowSave: true,
+ allowCancel: true,
+ allowDelete: true,
+ listeners: {
+ done: function(panel, e) {
+ closeEditing();
+ var feature = e.feature, modified = e.modified;
+ if(feature.state != null) {
+ // simulate save to server
+ setTimeout(function() {
+ if(feature.state === OpenLayers.State.DELETE) {
+ feature.layer.destroyFeatures([feature]);
+ } else {
+ feature.state = null;
+ }
+ }, 1);
+ }
+ },
+ cancel: function(panel, e) {
+ var feature = e.feature, modified = e.modified;
+ panel.cancel();
+ closeEditing();
+ if(feature.state === OpenLayers.State.INSERT) {
+ feature.layer.destroyFeatures([feature]);
+ }
+ // we call cancel() ourselves so return false here
+ return false;
+ }
+ }
+ });
+
+ editingContainer.add(editorGrid);
+ editingContainer.doLayout();
+
+ feature.layer.drawFeature(feature, "select");
+}
+
+Ext.onReady(function() {
+
+ var map = new OpenLayers.Map({controls: []});
+
+ var wmsLayer = new OpenLayers.Layer.WMS(
+ "vmap0",
+ "http://labs.metacarta.com/wms/vmap0",
+ {layers: 'basic'}
+ );
+ var vecLayer = createVectorLayer();
+
+ map.addLayers([wmsLayer, vecLayer]);
+
+ var drawFeature = new OpenLayers.Control.DrawFeature(
+ vecLayer, OpenLayers.Handler.Point, {
+ eventListeners: {
+ deactivate: closeEditing
+ }
+ });
+
+ selectFeature = new OpenLayers.Control.SelectFeature(vecLayer, {
+ eventListeners: {
+ deactivate: closeEditing
+ }
+ });
+
+ var tools = [
+ "->",
+ new GeoExt.Action({
+ control: new OpenLayers.Control.Navigation(),
+ map: map,
+ toggleGroup: "edit",
+ pressed: true,
+ allowDepress: false,
+ text: "Navigate"
+ }),
+ new GeoExt.Action({
+ control: drawFeature,
+ map: map,
+ toggleGroup: "edit",
+ pressed: false,
+ allowDepress: false,
+ text: "Add feature"
+ }),
+ new GeoExt.Action({
+ control: selectFeature,
+ map: map,
+ toggleGroup: "edit",
+ pressed: false,
+ allowDepress: false,
+ text: "Edit feature"
+ })
+ ];
+
+ mapPanel = new GeoExt.MapPanel({
+ title: "Map",
+ region: "center",
+ height: 400,
+ width: 600,
+ map: map,
+ center: new OpenLayers.LonLat(5, 45),
+ zoom: 6,
+ tbar: tools
+ });
+
+ editingContainer = new Ext.Panel({
+ title: "Editing",
+ region: "east",
+ layout: "fit",
+ width: 320
+ });
+
+ new Ext.Panel({
+ renderTo: "panel",
+ layout: "border",
+ height: 400,
+ width: 920,
+ items: [mapPanel, editingContainer]
+ });
+});
Added: extensions/geoext.ux/ux/FeatureEditing/lib/GeoExt.ux/FeatureEditorGrid.js
===================================================================
--- extensions/geoext.ux/ux/FeatureEditing/lib/GeoExt.ux/FeatureEditorGrid.js (rev 0)
+++ extensions/geoext.ux/ux/FeatureEditing/lib/GeoExt.ux/FeatureEditorGrid.js 2010-05-12 14:29:54 UTC (rev 2183)
@@ -0,0 +1,469 @@
+/**
+ * Copyright (c) 2008-2009 The Open Source Geospatial Foundation
+ *
+ * Published under the BSD license.
+ * See http://svn.geoext.org/core/trunk/geoext/license.txt for the full text
+ * of the license.
+ */
+
+Ext.namespace("GeoExt.ux");
+
+/*
+ * @include GeoExt/data/AttributeStore.js
+ * @include GeoExt/widgets/form.js
+ */
+
+/** api: (define)
+ * module = GeoExt.ux
+ * class = FeatureEditorGrid
+ * base_link = `Ext.grid.EditorGridPanel <http://www.extjs.com/deploy/dev/docs/?class=Ext.grid.EditorGridPanel>`_
+ */
+
+/** api: constructor
+ * .. class:: FeatureEditorGrid(config)
+ *
+ * A grid including the attributes of a feature and making the feature
+ * editable, using an ``OpenLayers.Control.ModifyFeature``.
+ */
+GeoExt.ux.FeatureEditorGrid = Ext.extend(Ext.grid.EditorGridPanel, {
+
+ /* begin i18n */
+ /** api: config[deleteMsgTitle] ``String`` i18n */
+ deleteMsgTitle: "Delete Feature?",
+ /** api: config[deleteMsg]
+ * ``String`` i18n for the delete confirmation, no confirmation message
+ * will appear if not provided.
+ */
+ deleteMsg: "Are you sure you want to delete this feature?",
+ /** api: config[deleteButtonText] ``String`` i18n */
+ deleteButtonText: "Delete",
+ /** api: config[deleteButtonTooltip] ``String`` i18n */
+ deleteButtonTooltip: "Delete this feature",
+ /** api: config[cancelMsgTitle] ``String`` i18n */
+ cancelMsgTitle: "Cancel Editing?",
+ /** api: config[cancelMsg]
+ * ``String`` i18n for the cancel confirmation, no confirmation message
+ * will appear if not provided.
+ */
+ cancelMsg: "There are unsaved changes. Are you sure you want to cancel?",
+ /** api: config[cancelButtonText] ``String`` i18n */
+ cancelButtonText: "Cancel",
+ /** api: config[cancelButtonTooltip] ``String`` i18n */
+ cancelButtonTooltip: "Stop editing, discard changes",
+ /** api: config[saveButtonText] ``String`` i18n */
+ saveButtonText: "Save",
+ /** api: config[saveButtonTooltip] ``String`` i18n */
+ saveButtonTooltip: "Save changes",
+ /** api: config[nameHeader] ``String`` i18n */
+ nameHeader: "Name",
+ /** api: config[valueHeader] ``String`` i18n */
+ valueHeader: "Value",
+ /* end i18n */
+
+ /** api: config[feature]
+ * ``OpenLayers.Feature.Vector`` The feature to edit and display. This
+ * option ignored if a store is provided. Either this option or the
+ * "store" option should be set.
+ */
+
+ /** api: config[nameField]
+ * ``String`` The name of the store field associated to the "Name"
+ * column in the grid. Default is "name".
+ */
+ /** private: property[nameField]
+ * ``String``
+ */
+ nameField: "name",
+
+ /** api: config[store]
+ * ``Ext.data.Store`` A store of records representing attributes,
+ * typically an :class:`GeoExt.data.AttributeStore` object with
+ * a vector feature set into it. If not provided one will be
+ * created based on the attributes of the provided feature.
+ * So either this option or the "feature" option should be set.
+ */
+ /** api: property[store]
+ * ``Ext.data.Store`` The attribute store. Read-only.
+ */
+ store: undefined,
+
+ /** api: config[allowDelete]
+ * ``Boolean`` Set to true to provide a Delete button for deleting the
+ * feature. Default is false.
+ */
+ allowDelete: false,
+
+ /** api: config[allowSave]
+ * ``Boolean`` Set to true to provide a Save button for saving the
+ * feature. Default is true.
+ */
+ allowSave: true,
+
+ /** api: config[allowCancel]
+ * ``Boolean`` Set to true to provide a Cancel button for canceling
+ * the editing of feature. Default is true.
+ */
+ allowCancel: true,
+
+ /** api: config[extraColumns]
+ * ``Array`` Extra columns to use in this grid's column model.
+ */
+ extraColumns: undefined,
+
+ /** private: property[modifyControl]
+ * ``OpenLayers.Control.ModifyFeature`` the control for geometry editing.
+ */
+ modifyControl: undefined,
+
+ /** private: property[featureInfo]
+ * ``Object`` Where we store the original state (in a broad sense) of
+ * the feature, so we can undo changes if necessary.
+ */
+ featureInfo: undefined,
+
+ /** private: property[cancelButton]
+ * ``Ext.Button``
+ */
+ cancelButton: undefined,
+
+ /** private: property[saveButton]
+ * ``Ext.Button``
+ */
+ saveButton: undefined,
+
+ /** private: property[deleteButton]
+ * ``Ext.Button``
+ */
+ deleteButton: undefined,
+
+ /** public: property[dirty]
+ * ``Boolean`` This property is used by this grid to track
+ * whether the feature is modified. Read-only.
+ */
+ dirty: false,
+
+ /** private: method[initComponent]
+ */
+ initComponent: function() {
+ this.addEvents(
+
+ /** api: events[cancel]
+ * Fires when the user cancels editing by clicking on the
+ * "Cancel" button.
+ *
+ * Listener arguments:
+ * * panel - :class:`GeoExt.ux.FeatureEditorGrid` This grid.
+ * * e - ``Object`` An object with two properties: "feature",
+ * referencing the feature being edited, and "modified", a
+ * ``Boolean`` value specifying if the feature has been
+ * modified.
+ */
+ "cancel",
+
+ /** api: events[done]
+ * Fires when the user finishes the editing either by clicking the
+ * "Save" button or when he clicks "Yes" in the modification
+ * cancel confirm dialog.
+ *
+ * Listener arguments:
+ * * panel - :class:`GeoExt.ux.FeatureEditorGrid` This grid.
+ * * e - ``Object`` An object with two properties: "feature",
+ * referencing the feature being edited, and "modified", a
+ * ``Boolean`` value specifying if the feature has been
+ * modified.
+ */
+ "done"
+ );
+
+ // create an attribute store if none is provided
+ if(!this.store) {
+ var data = [], attributes = this.feature.attributes;
+ for(var a in attributes) {
+ if(attributes.hasOwnProperty(a)) {
+ data.push({
+ "name": a,
+ "type": typeof attributes[a]
+ });
+ }
+ }
+ this.store = new GeoExt.data.AttributeStore({
+ feature: this.feature,
+ data: data
+ });
+ }
+
+ delete this.feature;
+
+ // create bottom bar
+ this.deleteButton = new Ext.Button({
+ text: this.deleteButtonText,
+ tooltip: this.deleteButtonTooltip,
+ iconCls: "delete",
+ hidden: !this.allowDelete,
+ handler: this.deleteHandler,
+ scope: this
+ });
+ this.cancelButton = new Ext.Button({
+ text: this.cancelButtonText,
+ tooltip: this.cancelButtonTooltip,
+ iconCls: "cancel",
+ hidden: !this.allowCancel,
+ handler: this.cancelHandler,
+ scope: this
+ });
+ this.saveButton = new Ext.Button({
+ text: this.saveButtonText,
+ tooltip: this.saveButtonTooltip,
+ iconCls: "save",
+ hidden: !this.allowSave,
+ handler: this.saveHandler,
+ scope: this
+ });
+ this.bbar = new Ext.Toolbar({
+ items: [
+ '->',
+ this.deleteButton,
+ this.saveButton,
+ this.cancelButton
+ ]
+ });
+
+ // create column model
+ var columns = [
+ { header: this.nameHeader, dataIndex: this.nameField },
+ new Ext.grid.Column({
+ header: this.valueHeader,
+ dataIndex: "value",
+ editable: true,
+ getEditor: this.getEditor.createDelegate(this)
+ })
+ ];
+ if(this.extraColumns) {
+ columns = columns.concat(this.extraColumns);
+ }
+ this.colModel = new Ext.grid.ColumnModel({
+ columns: columns
+ });
+
+ // call parent to finish the initialization of the component
+ GeoExt.ux.FeatureEditorGrid.superclass.initComponent.call(this);
+
+ var feature = this.store.feature;
+
+ // store the initial state of the feature
+ this.featureInfo = {
+ geometry: feature.geometry.clone(),
+ attributes: Ext.apply({}, feature.attributes),
+ state: feature.state
+ };
+
+ // create modify feature control
+ this.modifyControl = new OpenLayers.Control.ModifyFeature(
+ feature.layer,
+ {standalone: true}
+ );
+ feature.layer.map.addControl(this.modifyControl);
+ this.modifyControl.activate();
+ this.modifyControl.selectFeature(feature);
+
+ // register a featuremodified listener on the layer
+ feature.layer.events.on({
+ featuremodified: this.onFeaturemodified,
+ scope: this
+ });
+
+ // register an afteredit listener to change the
+ // feature state
+ this.on({
+ "afteredit": function() {
+ this.setFeatureState(this.getDirtyState());
+ }
+ });
+ },
+
+ /** private: method[onFeaturemodified]
+ * :param e: ``Object`` The event.
+ *
+ * Called when a feature is modified in the layer.
+ */
+ onFeaturemodified: function(e) {
+ if(e.feature === this.store.feature) {
+ this.dirty = true;
+ }
+ },
+
+ /** private: method[getEditor]
+ * :param rowIndex: ``Number``
+ * :return: ``Ext.grid.GridEditor``
+ *
+ * Return a GridEditor object for a given row in the grid.
+ */
+ getEditor: function(rowIndex) {
+ var record = this.store.getAt(rowIndex), config;
+ var field = (config = GeoExt.form.recordToField(record)) ?
+ Ext.ComponentMgr.create(config) : new Ext.form.TextField();
+ return new Ext.grid.GridEditor(field);
+ },
+
+ /** private: method[getDirtyState]
+ * :return: ``Number`` The feature state.
+ *
+ * Get the appropriate OpenLayers.State value to indicate a dirty feature.
+ * We don't cache this value because the popup may remain open through
+ * several state changes.
+ */
+ getDirtyState: function() {
+ return this.store.feature.state === OpenLayers.State.INSERT ?
+ this.store.feature.state : OpenLayers.State.UPDATE;
+ },
+
+ /** private: method[cancelHandler]
+ * :param e: {Object} Properties defined in this object are set in the
+ * "cancel" event.
+ *
+ * Called when the "Cancel" button is clicked.
+ */
+ cancelHandler: function(e) {
+
+ var _cancel = function() {
+ e = Ext.applyIf({
+ feature: this.store.feature,
+ modified: this.dirty
+ }, e);
+ if(this.fireEvent("cancel", this, e) !== false) {
+ this.cancel();
+ }
+ }.createDelegate(this);
+
+ if(this.cancelMsg && this.dirty) {
+ Ext.Msg.show({
+ title: this.cancelMsgTitle,
+ msg: this.cancelMsg,
+ buttons: Ext.Msg.YESNO,
+ icon: Ext.MessageBox.QUESTION,
+ fn: function(button) {
+ if(button === "yes") {
+ _cancel();
+ }
+ }
+ });
+ } else {
+ _cancel();
+ }
+ },
+
+ /** public: method[cancel]
+ * Undo changes, gets the initial geometry, attributes and
+ * state back in the feature.
+ */
+ cancel: function() {
+ var feature = this.store.feature, layer = feature.layer;
+
+ // a bit of a hack here: we're about to set a new geometry
+ // in the feature, and we cannot just do it and redraw the
+ // feature as this will cause the renderer to draw two
+ // shapes. So we force the renderer to unrender the shape
+ // by using display: "none" in the style.
+
+ layer.drawFeature(feature, {display: "none"});
+
+ feature.geometry = this.featureInfo.geometry;
+ feature.attributes = this.featureInfo.attributes;
+ feature.state = this.featureInfo.state;
+
+ layer.drawFeature(feature);
+ },
+
+ /** private: method[deleteHandler]
+ * :param e: {Object} Properties defined in this object are set in the
+ * "done" event.
+ *
+ * Called when the "Delete" button is clicked.
+ */
+ deleteHandler: function(e) {
+
+ var _delete = function() {
+ this.setFeatureState(OpenLayers.State.DELETE);
+ e = Ext.applyIf({
+ feature: this.store.feature,
+ modified: this.dirty
+ }, e);
+ this.fireEvent("done", this, e);
+ }.createDelegate(this);
+
+ if(this.deleteMsg) {
+ Ext.Msg.show({
+ title: this.deleteMsgTitle,
+ msg: this.deleteMsg,
+ buttons: Ext.Msg.YESNO,
+ icon: Ext.MessageBox.QUESTION,
+ fn: function(button) {
+ if(button === "yes") {
+ _delete();
+ }
+ }
+ });
+ } else {
+ _delete();
+ }
+ },
+
+ /** private: method[saveHandler]
+ * :param e: {Object} Properties defined in this object are set in the
+ * "done" event.
+ *
+ * Called when the "Save" button is clicked.
+ */
+ saveHandler: function(e) {
+ e = Ext.applyIf({
+ feature: this.store.feature,
+ modified: this.dirty
+ }, e);
+ this.fireEvent("done", this, e);
+ },
+
+ /** private: method[setFeatureState]
+ * :param state: ``String`` The new state.
+ *
+ * Set the state of the feature and trigger a featuremodified
+ * event on the layer.
+ */
+ setFeatureState: function(state) {
+ var feature = this.store.feature, layer = feature.layer;
+ feature.state = state;
+ if(layer && layer.events) {
+ layer.events.triggerEvent("featuremodified", {
+ feature: feature
+ });
+ layer.drawFeature(feature);
+ }
+ },
+
+ /** private: method[beforeDestroy]
+ * Private method called during the destroy sequence.
+ */
+ beforeDestroy: function() {
+ GeoExt.ux.FeatureEditorGrid.superclass.beforeDestroy.apply(
+ this, arguments);
+
+ var layer = this.store.feature.layer;
+ if(layer && layer.events) {
+ layer.events.un({
+ featuremodified: this.onFeaturemodified,
+ scope: this
+ });
+ }
+
+ // remove the line below when
+ // http://trac.openlayers.org/ticket/2210 is fixed.
+ this.modifyControl.deactivate();
+ this.modifyControl.destroy();
+
+ if(!this.initialConfig.store) {
+ this.store.destroy();
+ }
+ }
+});
+
+/** api: xtype = gxux_featureeditorgrid */
+Ext.reg("gxux_featureeditorgrid", GeoExt.ux.FeatureEditorGrid);
Added: extensions/geoext.ux/ux/FeatureEditing/tests/index.html
===================================================================
--- extensions/geoext.ux/ux/FeatureEditing/tests/index.html (rev 0)
+++ extensions/geoext.ux/ux/FeatureEditing/tests/index.html 2010-05-12 14:29:54 UTC (rev 2183)
@@ -0,0 +1,4 @@
+<html>
+ <head><meta http-equiv="refresh" content="0;url=../../../../geoext/tests/run-tests.html?testlist=../../geoext.ux/ux/FeatureEditing/tests/list-tests.html"></head>
+ <body></body>
+</html>
Added: extensions/geoext.ux/ux/FeatureEditing/tests/lib/GeoExt.ux/FeatureEditorGrid.html
===================================================================
--- extensions/geoext.ux/ux/FeatureEditing/tests/lib/GeoExt.ux/FeatureEditorGrid.html (rev 0)
+++ extensions/geoext.ux/ux/FeatureEditing/tests/lib/GeoExt.ux/FeatureEditorGrid.html 2010-05-12 14:29:54 UTC (rev 2183)
@@ -0,0 +1,429 @@
+<!DOCTYPE html>
+<html debug="true">
+ <head>
+ <script type="text/javascript" src="http://www.openlayers.org/api/2.9/OpenLayers.js"></script>
+ <script type="text/javascript" src="http://extjs.cachefly.net/ext-3.1.1/adapter/ext/ext-base.js"></script>
+ <script type="text/javascript" src="http://extjs.cachefly.net/ext-3.1.1/ext-all.js"></script>
+ <script type="text/javascript" src="../../../../../../geoext/lib/GeoExt.js"></script>
+ <script type="text/javascript" src="../../../lib/GeoExt.ux/FeatureEditorGrid.js"></script>
+
+ <script type="text/javascript">
+ function test_ctor(t) {
+ t.plan(27);
+
+ // set up
+
+ var map, layer, feature, grid, store;
+
+ map = new OpenLayers.Map("map", {allOverlays: true});
+
+ layer = new OpenLayers.Layer.Vector("vector");
+ map.addLayer(layer);
+
+ feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(1, 2),
+ {foo: "foo", bar: "bar"}
+ );
+ layer.addFeatures(feature);
+
+ // test
+ function _test(id) {
+ // 12 tests
+ t.ok(grid instanceof GeoExt.ux.FeatureEditorGrid,
+ "[" + id + "] ctor returns a FeatureEditorGrid");
+ t.ok(grid instanceof Ext.grid.EditorGridPanel,
+ "[" + id + "] ctor returns an EditorGridPanel");
+ t.ok(grid.feature === undefined,
+ "[" + id + "] ctor does not set the feature in the instance");
+ t.ok(grid.modifyControl instanceof OpenLayers.Control.ModifyFeature,
+ "[" + id + "] ctor creates a ModifyFeature control");
+ t.ok(grid.modifyControl.layer === layer,
+ "[" + id + "] ctor provides the layer to the ModifyFeature");
+ t.ok(grid.modifyControl.active,
+ "[" + id + "] ctor activates the ModifyFeature control");
+ t.ok(grid.modifyControl.feature === feature,
+ "[" + id + "] ctor selects the feature");
+ t.ok(layer.events.listeners["featuremodified"][0].func === grid.onFeaturemodified,
+ "[" + id + "] ctor registers a featuremodified listener");
+ t.ok(grid.hasListener("afteredit"),
+ "[" + id + "] ctor registers an afteredit listener");
+ t.eq(grid.deleteButton.hidden, true,
+ "[" + id + "] ctor hides the delete button by default");
+ t.eq(grid.cancelButton.hidden, false,
+ "[" + id + "] ctor doesn't hide the cancel button by default");
+ t.eq(grid.saveButton.hidden, false,
+ "[" + id + "] ctor doesn't hide the save button by default");
+ }
+
+ // provide the ctor with a feature
+ // 14 tests
+ grid = new GeoExt.ux.FeatureEditorGrid({
+ renderTo: "grid",
+ feature: feature
+ });
+ _test("1");
+ t.ok(grid.store instanceof GeoExt.data.AttributeStore,
+ "[1] ctor creates an AttributeStore if none is provided");
+ t.ok(grid.store.feature === feature,
+ "[1] ctor passes the feature to the created AttributeStore");
+ grid.destroy();
+
+ // provide the ctor with a store
+ // 13 tests
+ store = new GeoExt.data.AttributeStore({
+ data: [{
+ name: "foo",
+ type: "string"
+ }, {
+ name: "bar",
+ type: "string"
+ }],
+ feature: feature
+ });
+ grid = new GeoExt.ux.FeatureEditorGrid({
+ renderTo: "grid",
+ store: store
+ });
+ _test("2");
+ t.ok(grid.store === store,
+ "[2] ctor sets the provided store in the instance");
+ grid.destroy();
+
+ // tear down
+
+ map.destroy();
+ }
+
+ function test_destroy(t) {
+ t.plan(2);
+
+ // set up
+
+ var map, layer, feature, grid, store;
+
+ map = new OpenLayers.Map("map", {allOverlays: true});
+
+ layer = new OpenLayers.Layer.Vector("vector");
+ map.addLayer(layer);
+
+ feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(1, 2),
+ {foo: "foo", bar: "bar"}
+ );
+ layer.addFeatures(feature);
+
+ store = new GeoExt.data.AttributeStore({
+ data: [{
+ name: "foo",
+ type: "string"
+ }, {
+ name: "bar",
+ type: "string"
+ }],
+ feature: feature
+ });
+
+ grid = new GeoExt.ux.FeatureEditorGrid({
+ renderTo: "grid",
+ store: store
+ });
+
+
+ // test
+
+ grid.destroy();
+ t.ok(layer.events.listeners["featuremodified"].length === 0,
+ "destroy unregisters the featuremodified listeners");
+ t.ok(!grid.modifyControl.events,
+ "destroy destroys the ModifyFeature");
+
+ // tear down
+
+ map.destroy();
+ }
+
+ function test_onFeaturemodified(t) {
+ t.plan(2);
+
+ // set up
+
+ var map, layer, feature, grid, store;
+
+ map = new OpenLayers.Map("map", {allOverlays: true});
+
+ layer = new OpenLayers.Layer.Vector("vector");
+ map.addLayer(layer);
+
+ feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(1, 2),
+ {foo: "foo", bar: "bar"}
+ );
+ layer.addFeatures(feature);
+
+ store = new GeoExt.data.AttributeStore({
+ data: [{
+ name: "foo",
+ type: "string"
+ }, {
+ name: "bar",
+ type: "string"
+ }],
+ feature: feature
+ });
+
+ grid = new GeoExt.ux.FeatureEditorGrid({
+ renderTo: "grid",
+ store: store
+ });
+
+ // test
+
+ // trigger "featuremodified" on the layer for our feature
+ // and test that the dirty property is set to true
+ grid.dirty = false;
+ layer.events.triggerEvent("featuremodified", {feature: feature});
+ t.eq(grid.dirty, true,
+ "dirty set to true when the feature is modified");
+
+ // trigger "featuremodified" on the layer for another feature
+ // and test that the dirty property isn't set to true
+ grid.dirty = false;
+ layer.events.triggerEvent("featuremodified", {feature: "fake"});
+ t.eq(grid.dirty, false,
+ "dirty not set to true when another feature is modified");
+
+ // tear down
+
+ grid.destroy();
+ }
+
+ function test_getEditor(t) {
+ t.plan(2);
+
+ // set up
+
+ var map, layer, feature, grid, store, field;
+
+ map = new OpenLayers.Map("map", {allOverlays: true});
+
+ layer = new OpenLayers.Layer.Vector("vector");
+ map.addLayer(layer);
+
+ feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(1, 2),
+ {foo: "foo", bar: "bar"}
+ );
+ layer.addFeatures(feature);
+
+ store = new GeoExt.data.AttributeStore({
+ data: [{
+ name: "foo",
+ type: "string"
+ }, {
+ name: "bar",
+ type: "unknown"
+ }],
+ feature: feature
+ });
+
+ grid = new GeoExt.ux.FeatureEditorGrid({
+ renderTo: "grid",
+ store: store
+ });
+
+ // test
+
+ field = grid.getEditor(0).field;
+ t.ok(field instanceof Ext.form.TextField,
+ "getEditor returns a TextField for a record of type \"string\"");
+
+ field = grid.getEditor(1).field;
+ t.ok(field instanceof Ext.form.TextField,
+ "getEditor returns a TextField for a record of an unknown type");
+
+ // tear down
+
+ grid.destroy();
+ }
+
+ function test_cancelHandler(t) {
+ t.plan(18);
+
+ // set up
+
+ var map, layer, feature, grid, store, ret, log1, log2;
+
+ map = new OpenLayers.Map("map", {allOverlays: true});
+
+ layer = new OpenLayers.Layer.Vector("vector");
+ map.addLayer(layer);
+
+ feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(1, 2),
+ {foo: "foo", bar: "bar"}
+ );
+ layer.addFeatures(feature);
+
+ store = new GeoExt.data.AttributeStore({
+ data: [{
+ name: "foo",
+ type: "string"
+ }, {
+ name: "bar",
+ type: "string"
+ }],
+ feature: feature
+ });
+
+ grid = new GeoExt.ux.FeatureEditorGrid({
+ renderTo: "grid",
+ store: store
+ });
+
+ grid.cancelMsg = undefined;
+
+ grid.on({
+ cancel: function(grid, e) {
+ log1.push({
+ grid: grid,
+ feature: e.feature,
+ modified: e.modified,
+ foo: e.foo
+ });
+ return ret;
+ }
+ });
+
+ grid.cancel = function() {
+ log2++;
+ };
+
+ var _test = function(id, state, foo, cancel) {
+ t.eq(log1.length, 1,
+ "[" + id + "] cancelHandler triggers \"cancel\" event once");
+ t.ok(log1[0].grid === grid,
+ "[" + id + "] \"cancel\" listener given expected grid");
+ t.ok(log1[0].feature === feature,
+ "[" + id + "] \"cancel\" listener given expected feature");
+ t.eq(log1[0].modified, state,
+ "[" + id + "] \"cancel\" listener given expected state");
+ t.eq(log1[0].foo, foo,
+ "[" + id + "] \"cancel\" listener given expected extra property");
+ t.eq(log2, cancel,
+ "[" + id + "] \"cancel\" method called " + cancel + " time");
+ };
+
+ // test
+
+ // with "dirty" set to false
+ log1 = []; log2 = 0;
+ ret = undefined;
+ grid.dirty = false;
+ grid.cancelHandler({foo: "foo"});
+ _test("1", false, "foo", 1);
+
+ // with "dirty" set to true
+ log1 = []; log2 = 0;
+ ret = undefined;
+ grid.dirty = true;
+ grid.cancelHandler({foo: "foo"});
+ _test("2", true, "foo", 1);
+
+ // with the "cancel" listener returning false
+ log1 = []; log2 = 0;
+ ret = false;
+ grid.dirty = true;
+ grid.cancelHandler({foo: "foo"});
+ _test("3", true, "foo", 0);
+
+ // tear down
+
+ grid.destroy();
+ }
+
+ function test_deleteHandler(t) {
+ t.plan(12);
+
+ // set up
+
+ var map, layer, feature, grid, store, log;
+
+ map = new OpenLayers.Map("map", {allOverlays: true});
+
+ layer = new OpenLayers.Layer.Vector("vector");
+ map.addLayer(layer);
+
+ feature = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(1, 2),
+ {foo: "foo", bar: "bar"}
+ );
+ layer.addFeatures(feature);
+
+ store = new GeoExt.data.AttributeStore({
+ data: [{
+ name: "foo",
+ type: "string"
+ }, {
+ name: "bar",
+ type: "string"
+ }],
+ feature: feature
+ });
+
+ grid = new GeoExt.ux.FeatureEditorGrid({
+ renderTo: "grid",
+ store: store
+ });
+
+ grid.deleteMsg = undefined;
+
+ grid.on({
+ done: function(grid, e) {
+ log.push({
+ grid: grid,
+ feature: e.feature,
+ modified: e.modified,
+ foo: e.foo
+ });
+ }
+ });
+
+ var _test = function(id, foo) {
+ t.eq(log.length, 1,
+ "[" + id + "] deletelHandler triggers \"done\" event once");
+ t.ok(log[0].grid === grid,
+ "[" + id + "] \"done\" listener given expected grid");
+ t.ok(log[0].feature === feature,
+ "[" + id + "] \"done\" listener given expected feature");
+ t.eq(log[0].modified, true,
+ "[" + id + "] \"done\" listener given modified true");
+ t.eq(log[0].foo, foo,
+ "[" + id + "] \"done\" listener given expected extra property");
+ t.eq(log[0].feature.state, OpenLayers.State.DELETE,
+ "[" + id + "] feature has the DELETE state");
+ };
+
+ // test
+
+ // with "dirty" set to false
+ log = [];
+ grid.dirty = false;
+ grid.deleteHandler({foo: "foo"});
+ _test("1", "foo");
+
+ // with "dirty" set to true
+ log = [];
+ grid.dirty = true;
+ grid.deleteHandler({foo: "foo"});
+ _test("2", "foo");
+
+ // tear down
+
+ grid.destroy();
+ }
+ </script>
+ <body>
+ <div id="map" style="width:600px; height: 400px"></div>
+ <div id="grid"></div>
+ </body>
+</html>
More information about the Commits
mailing list