~mortenoh/+junk/dhis2-detailed-import-export

« back to all changes in this revision

Viewing changes to gis/dhis-gis-geostat/mfbase/mapfish/widgets/editing/FeatureList.js

  • Committer: larshelge at gmail
  • Date: 2009-03-03 16:46:36 UTC
  • Revision ID: larshelge@gmail.com-20090303164636-2sjlrquo7ib1gf7r
Initial check-in

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2007-2008  Camptocamp
 
3
 *
 
4
 * This file is part of MapFish Client
 
5
 *
 
6
 * MapFish Client is free software: you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation, either version 3 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * MapFish Client is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with MapFish Client.  If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
/**
 
21
 * @requires OpenLayers/Map.js
 
22
 * @requires OpenLayers/Control/ModifyFeature.js
 
23
 * @requires OpenLayers/Layer/Vector.js
 
24
 */
 
25
 
 
26
Ext.namespace('mapfish.widgets');
 
27
 
 
28
Ext.namespace('mapfish.widgets.editing');
 
29
 
 
30
/**
 
31
 * Class: mapfish.widgets.editing.FeatureList
 
32
 * Use this class to create an editable grid of features.
 
33
 *
 
34
 * Inherits from:
 
35
 * - {Ext.grid.EditorGridPanel}
 
36
 */
 
37
 
 
38
/**
 
39
 * Constructor: mapfish.widgets.editing.FeatureList
 
40
 *
 
41
 * Parameters:
 
42
 * config - {Object} The Grid config.
 
43
 */
 
44
mapfish.widgets.editing.FeatureList = function(config) {
 
45
    Ext.apply(this, config, {
 
46
        sm: new Ext.grid.RowSelectionModel({singleSelect: true}),
 
47
        clicksToEdit: 1,
 
48
        enableDragDrop: true
 
49
    });
 
50
    mapfish.widgets.editing.FeatureList.superclass.constructor.call(this);
 
51
};
 
52
 
 
53
Ext.extend(mapfish.widgets.editing.FeatureList, Ext.grid.EditorGridPanel, {
 
54
 
 
55
    /**
 
56
     * APIProperty: featureType
 
57
     * {Ext.data.Record} Definition of a feature record,
 
58
     * created by Ext.data.Record.create()
 
59
     */
 
60
    featureType: null,
 
61
 
 
62
    /**
 
63
     * APIProperty: map
 
64
     * {<OpenLayers.Map>} Where to display the selected geometries
 
65
     */
 
66
    map: null,
 
67
 
 
68
    /**
 
69
     * APIProperty: layer
 
70
     * {<OpenLayers.Layer.Vector>} The Vector layer to use to draw the
 
71
     * selected geometries.
 
72
     */
 
73
    layer: null,
 
74
 
 
75
    /**
 
76
     * APIProperty: automaticMode
 
77
     * {Boolean} If true, the geometry to edit can be selected on click.
 
78
     * Otherwise, the user has to click on the geometry in the list to
 
79
     * put it in edit mode.
 
80
     */
 
81
    automaticMode: false,
 
82
 
 
83
    /**
 
84
     * APIProperty: autoFocusMode
 
85
     * {Integer} If 0, don't change the visible extent of the map.
 
86
     * If 1, change the map's visible extent to match the selected geometry.
 
87
     * If 2, make sure the selected geometry is visible (default).
 
88
     */
 
89
    autoFocusMode: 2,
 
90
 
 
91
    /**
 
92
     * APIProperty: displayNotEdited
 
93
     * {Boolean} If "false", display only the edited feature. If "true", shows
 
94
     * the edited features and the others. Only relevant when automaticMode
 
95
     * is false.
 
96
     */
 
97
    displayNotEdited: true,
 
98
 
 
99
    /**
 
100
     * APIMethod: editGeometryVisual
 
101
     * How to represent a geometry in the grid. It will be the thing the user
 
102
     * will have to click in order to edit the geometry.
 
103
     *
 
104
     * Parameters:
 
105
     * geometry - {<OpenLayers.Geometry>}
 
106
     * record - {<Ext.data.Record>}
 
107
     * edited - {Boolean} true if the geometry is being edited on the map.
 
108
     */
 
109
    editGeometryVisual: function(geometry, record, edited) {
 
110
        return geometry ? (edited ? "->" : "X") : "";
 
111
    },
 
112
 
 
113
    /**
 
114
     * Property: isDnd
 
115
     * {Boolean} True if we are currently performing a drag and drop. Used
 
116
     *      when we receive a "remove" and "add" notification on the store, to
 
117
     *      know if it was a drag and drop.
 
118
     */
 
119
    isDnd: false,
 
120
 
 
121
    /**
 
122
     * Property: colDefs
 
123
     * {Array(Object)} Save the columns parameter (dady nullifies the
 
124
     * this.columns property).
 
125
     */
 
126
    colDefs: null,
 
127
 
 
128
    /**
 
129
     * Property: modifyFeature
 
130
     * {OpenLayers.Control.ModifyFeature} The control used to modify geometries.
 
131
     */
 
132
    modifyFeature: null,
 
133
 
 
134
    /**
 
135
     * Method: initComponent
 
136
     *      Initialize the component.
 
137
     */
 
138
    initComponent: function() {
 
139
        // sanity checks
 
140
        if (!this.map && !this.layer) {
 
141
            OpenLayers.Console.error(
 
142
                "Mandatory param for FeatureList missing: layer and/or map");
 
143
        }
 
144
        if (!this.featureType) {
 
145
            OpenLayers.Console.error(
 
146
                "Mandatory param for FeatureList missing: featureType");
 
147
        }
 
148
 
 
149
        // keep a reference on the definition of columns
 
150
        this.colDefs = this.columns;
 
151
        mapfish.widgets.editing.FeatureList.superclass.initComponent.call(this);
 
152
 
 
153
        this.setGeoColRenderer();
 
154
 
 
155
        // take care of the connections with the map
 
156
        if (!this.map) {
 
157
            this.map = this.layer.map;
 
158
        } else if (!this.layer) {
 
159
            this.layer = new OpenLayers.Layer.Vector("Geometry editing");
 
160
            this.map.addLayer(this.layer);
 
161
        }
 
162
 
 
163
        var self = this;
 
164
 
 
165
        // define events for "start modifying a geometry", "modifying a geometry",
 
166
        // and "done modifying a geometry"
 
167
        this.addEvents({geomodifstart: true, geomodif: true, geomodifend: true});
 
168
 
 
169
        // create modify feature control
 
170
        var mode = OpenLayers.Control.ModifyFeature.RESHAPE |
 
171
                   OpenLayers.Control.ModifyFeature.DRAG;
 
172
        this.modifyFeature = new OpenLayers.Control.ModifyFeature(this.layer, {
 
173
            mode: mode,
 
174
            onModificationStart: function(feature) {
 
175
                // temporarily activate the modify feature control
 
176
                if (!self.automaticMode) {
 
177
                    this.activate();
 
178
                }
 
179
                self.refreshGeometryVisual(feature.data);
 
180
                // select and show the row of the feature we are editing
 
181
                var record = feature.data;
 
182
                var row = self.getStore().findBy(function(r) {
 
183
                    return r.id == record.id;
 
184
                });
 
185
                self.getView().focusCell(row, 0);
 
186
                self.getSelectionModel().selectRange(row, row);
 
187
                self.fireEvent('geomodifstart', self, record, feature);
 
188
            },
 
189
            onModification: function(feature) {
 
190
                self.fireEvent('geomodif', self, feature.data, feature);
 
191
            },
 
192
            onModificationEnd: function(feature) {
 
193
                // deactivate the modify feature control
 
194
                if (!self.automaticMode) {
 
195
                    this.deactivate();
 
196
                }
 
197
                if (feature.data) {
 
198
                    self.refreshGeometryVisual(feature.data);
 
199
                }
 
200
                if (!self.displayNotEdited) {
 
201
                    self.layer.removeFeatures(feature);
 
202
                }
 
203
                self.fireEvent('geomodifend', self, feature.data, feature);
 
204
            }
 
205
        });
 
206
        // add modify feature control to the map
 
207
        this.map.addControl(this.modifyFeature);
 
208
 
 
209
        // make sure the geometries are removed from the layer if a
 
210
        // feature is removed.
 
211
        this.getStore().on("remove", function(store, record, index) {
 
212
            this.removeGeometries(record);
 
213
        }, this);
 
214
        this.getStore().on("clear", function(store) {
 
215
            store.each(this.removeGeometries, this);
 
216
        }, this);
 
217
 
 
218
        // add added to the layer if a feature is added.
 
219
        function add(store, records, index) {
 
220
            if (self.displayNotEdited) {
 
221
                for (var i = 0; i < records.length; ++i) {
 
222
                    self.addGeometries(records[i]);
 
223
                }
 
224
            }
 
225
            return true;
 
226
        }
 
227
        this.getStore().on("add", add);
 
228
        this.getStore().on("load", function(store, records, options) {
 
229
            if (!options.add) {
 
230
                // On load, the data was cleared, but no other notification was
 
231
                // fired. So we have to do some cleanup. Then, we can add the
 
232
                // geometries.
 
233
                if (this.modifyFeature.feature) {
 
234
                    this.modifyFeature.selectControl.unselect(
 
235
                        this.modifyFeature.feature);
 
236
                }
 
237
                this.clearLayer();
 
238
            }
 
239
            add(store, records, 0);
 
240
            return true;
 
241
        }, this);
 
242
    },
 
243
 
 
244
    /**
 
245
     * Method: onRender
 
246
     * Called by EXT when the component is rendered.
 
247
     */
 
248
    onRender: function() {
 
249
        mapfish.widgets.editing.FeatureList.superclass.onRender.apply(
 
250
            this, arguments);
 
251
 
 
252
        // add the possibility to drag and drop rows for re-ordering them
 
253
        var self = this;
 
254
        var ddrow = new Ext.dd.DropTarget(this.getView().mainBody, {
 
255
            ddGroup: 'GridDD',
 
256
            notifyOver: function(source, e, data) {
 
257
                var cindex = source.getDragData(e).rowIndex;
 
258
                if (typeof cindex != "undefined") {
 
259
                    return this.dropAllowed;
 
260
                }
 
261
                return this.dropNotAllowed;
 
262
            },
 
263
            notifyDrop: function(dd, e, data) {
 
264
                var dragData = dd.getDragData(e);
 
265
                var destIndex = dragData.rowIndex;
 
266
                if (typeof destIndex != "undefined") {
 
267
                    var record = data.selections[0];
 
268
                    self.isDnd = true;
 
269
                    data.grid.store.remove(record);
 
270
                    dragData.grid.store.insert(destIndex, record);
 
271
                    self.isDnd = false;
 
272
                    return true;
 
273
                }
 
274
                return false;
 
275
            }
 
276
        });
 
277
 
 
278
        // draw the features
 
279
        if (this.displayNotEdited) {
 
280
            this.drawAllFeatures();
 
281
        }
 
282
    },
 
283
 
 
284
    /**
 
285
     * Method: eachGeoColumn
 
286
     * Iterate over each columns
 
287
     *
 
288
     * Parameters:
 
289
     * callback - {Function} What to call for each column.
 
290
     */
 
291
    eachGeoColumn: function(callback) {
 
292
        for (var i = 0; i < this.colDefs.length; ++i) {
 
293
            var col = this.colDefs[i];
 
294
            var colDesc = this.featureType.prototype.fields.get(col.dataIndex);
 
295
            if (colDesc.type == 'geo') {
 
296
                callback.call(this, col, colDesc, i);
 
297
            }
 
298
        }
 
299
    },
 
300
 
 
301
    /**
 
302
     * Method: setGeoColRenderer.
 
303
     * Sets the renderer to the geometry columns' definition.
 
304
     */
 
305
    setGeoColRenderer: function() {
 
306
        this.eachGeoColumn(function(col, colDesc, colNum) {
 
307
            col.renderer = OpenLayers.Function.bind(
 
308
                function(value, cellMetaData, record, rowNum, colNum, store) {
 
309
                    if (value) {
 
310
                        var edited = (this.grid.modifyFeature.feature != null) &&
 
311
                                     (this.grid.getFeatureByGeometry(value) ==
 
312
                                      this.grid.modifyFeature.feature);
 
313
                        return '<div onclick="mapfish.widgets.editing.FeatureList.geometryClickHandler(\''
 
314
                            + this.grid.id + '\', ' + record.id + ', \'' + this.colName + '\');">'
 
315
                            + this.grid.editGeometryVisual(value, record, edited) + '</div>';
 
316
                    } else {
 
317
                        return this.grid.editGeometryVisual(value, record, false);
 
318
                    }
 
319
                }, {grid: this, colName: colDesc.name}
 
320
            );
 
321
        });
 
322
    },
 
323
 
 
324
    /**
 
325
     * Method: drawAllFeatures
 
326
     * Add all the geometries to the vector layer.
 
327
     */
 
328
    drawAllFeatures: function() {
 
329
        this.clearLayer();
 
330
        if (this.displayNotEdited) {
 
331
            var features = [];
 
332
            this.eachGeoColumn(function(col, colDesc, colNum) {
 
333
                this.store.each(function (record) {
 
334
                    var geometry = record.get(colDesc.name);
 
335
                    if (geometry && !this.getFeatureByGeometry(geometry)) {
 
336
                        var vector = new OpenLayers.Feature.Vector(
 
337
                            geometry, record);
 
338
                        features.push(vector);
 
339
                    }
 
340
                }, this);
 
341
            });
 
342
            this.layer.addFeatures(features);
 
343
        }
 
344
    },
 
345
 
 
346
    /**
 
347
     * Method: addGeometries
 
348
     * Add all the geometries of a feature to the vector layer.
 
349
     *
 
350
     * Parameters:
 
351
     * record - {<Ext.data.Record>}
 
352
     */
 
353
    addGeometries: function(record) {
 
354
        var layer = this.layer;
 
355
        this.eachGeoColumn(function(col, colDesc, colNum) {
 
356
            var geometry = record.get(colDesc.name);
 
357
            if (geometry && !this.getFeatureByGeometry(geometry)) {
 
358
                var vector = new OpenLayers.Feature.Vector(
 
359
                    geometry, record);
 
360
                layer.addFeatures(vector);
 
361
            }
 
362
        });
 
363
    },
 
364
 
 
365
    /**
 
366
     * Method: removeGeometries
 
367
     * Remove all the geometries of a feature from the vector layer.
 
368
     *
 
369
     * Parameters:
 
370
     * record - {Ext.data.Record}
 
371
     */
 
372
    removeGeometries: function(record) {
 
373
        this.eachGeoColumn(function(col, colDesc, colNum) {
 
374
            var geometry = record.get(colDesc.name);
 
375
            if (geometry) {
 
376
                var feature = this.getFeatureByGeometry(geometry);
 
377
                if (feature) {
 
378
                    if (feature == this.modifyFeature.feature) {
 
379
                        // not to have bad stuff happening
 
380
                        // in modifyFeature.onModificationEnd
 
381
                        feature.data = null;
 
382
                        this.modifyFeature.selectControl.unselect(feature);
 
383
                    }
 
384
                    this.layer.removeFeatures([feature]);
 
385
                    feature.destroy();
 
386
                }
 
387
            }
 
388
        });
 
389
        return true;
 
390
    },
 
391
 
 
392
    /**
 
393
     * Method: getFeatureByGeometry
 
394
     * Find the feature in the vector layer in function of the geometry.
 
395
     *
 
396
     * Parameters:
 
397
     * geometry - {<OpenLayers.Geometry>}
 
398
     *
 
399
     * Returns:
 
400
     * {<OpenLayers.Feature.Vector>}
 
401
     */
 
402
    getFeatureByGeometry: function(geometry) {
 
403
        var features = this.layer.features;
 
404
        for (var i = 0; i < features.length; ++i) {
 
405
            var cur = features[i];
 
406
            if (cur.geometry == geometry) {
 
407
                return cur;
 
408
            }
 
409
        }
 
410
        return null;
 
411
    },
 
412
 
 
413
    /**
 
414
     * APIMethod: editFirstGeometry
 
415
     * Start to edit the first geometry of the given feature.
 
416
     * Usefull only if not in automatic mode
 
417
     *
 
418
     * Parameters:
 
419
     * record - {<Ext.data.Record>}
 
420
     */
 
421
    editFirstGeometry: function(record) {
 
422
        if (this.automaticMode) {
 
423
            return;
 
424
        }
 
425
        var colName;
 
426
        for (var i = 0; i < this.colDefs.length; ++i) {
 
427
            var col = this.colDefs[i];
 
428
            var colDesc = this.featureType.prototype.fields.get(col.dataIndex);
 
429
            if (colDesc.type == 'geo') {
 
430
                colName = colDesc.name;
 
431
                break;
 
432
            }
 
433
        }
 
434
        this.editGeometry(record, colName, false);
 
435
    },
 
436
 
 
437
    /**
 
438
     * Method: editGeometry
 
439
     * Start to edit one geometry of the given feature.
 
440
     *
 
441
     * Parameters:
 
442
     * record - {Ext.data.Record}
 
443
     * colName - {String}
 
444
     * focus - {Boolean} If true and in auto-focus mode, will zoom the map on
 
445
     *                   the geometry
 
446
     */
 
447
    editGeometry: function(record, colName, focus) {
 
448
        var geometry = record.get(colName);
 
449
        if (!geometry){
 
450
            return;
 
451
        }
 
452
        var feature = this.getFeatureByGeometry(geometry);
 
453
        if (!feature && !this.displayNotEdited) {
 
454
            feature = new OpenLayers.Feature.Vector(
 
455
                geometry, record);
 
456
            this.layer.addFeatures(feature);
 
457
        }
 
458
        if (feature) {
 
459
            var previousFeature = this.modifyFeature.feature;
 
460
            if (previousFeature) {
 
461
                this.modifyFeature.selectControl.unselect(
 
462
                    this.modifyFeature.feature);
 
463
            }
 
464
            if (previousFeature != feature) {
 
465
                this.modifyFeature.selectControl.select(feature);
 
466
                if (focus) {
 
467
                    this.manageAutoFocus(geometry);
 
468
                }
 
469
            }
 
470
        } else {
 
471
            OpenLayers.Console.error(
 
472
                "BUG: cannot find vector feature for: " + record);
 
473
        }
 
474
    },
 
475
 
 
476
    /**
 
477
     * Method: manageAutoFocus
 
478
     * Change the extent of the map in function of the configuration.
 
479
     *
 
480
     * Parameters:
 
481
     * geometry - {OpenLayers.Geometry}
 
482
     */
 
483
    manageAutoFocus: function (geometry) {
 
484
         if (this.autoFocusMode == 1) {
 
485
             this.map.zoomToExtent(geometry.getBounds());
 
486
         } else if (this.autoFocusMode == 2) {
 
487
             var extent = this.map.getExtent();
 
488
             extent.extend(geometry.getBounds());
 
489
             var margin = extent.getWidth() * 0.02;
 
490
             extent.left += margin;
 
491
             extent.right -= margin;
 
492
             extent.top -= margin;
 
493
             extent.bottom += margin;
 
494
             this.map.zoomToExtent(extent);
 
495
         }
 
496
    },
 
497
 
 
498
    /**
 
499
     * Method: refreshGeometryVisual
 
500
     * Force the refresh of the icon representing the geometry in the grid.
 
501
     *
 
502
     * Parameters:
 
503
     * record - {Ext.data.Record}
 
504
     */
 
505
    refreshGeometryVisual: function(record) {
 
506
        this.getView().refreshRow(record);
 
507
    },
 
508
 
 
509
    /**
 
510
     * APIMethod: setAutomaticMode
 
511
     * Change the mode.
 
512
     *
 
513
     * Parameters:
 
514
     * automatic - {Boolean}
 
515
     */
 
516
    setAutomaticMode: function(automatic) {
 
517
        if (automatic == this.automaticMode) {
 
518
            return;
 
519
        }
 
520
        this.automaticMode = automatic;
 
521
        if (this.modifyFeature.feature) {
 
522
            this.modifyFeature.selectControl.unselect(
 
523
                this.modifyFeature.feature);
 
524
        }
 
525
        if (automatic) {
 
526
            this.modifyFeature.activate();
 
527
        } else {
 
528
            this.modifyFeature.deactivate();
 
529
        }
 
530
    },
 
531
 
 
532
    /**
 
533
     * APIMethod: setDisplayNotEdited
 
534
     * Change the configuration of what should be displayed.
 
535
     *
 
536
     * Parameters:
 
537
     * value - {Boolean}
 
538
     */
 
539
    setDisplayNotEdited: function(value) {
 
540
        if (value == this.displayNotEdited) {
 
541
            return;
 
542
        }
 
543
        this.displayNotEdited = value;
 
544
        if (value) {
 
545
            this.drawAllFeatures();
 
546
        } else {
 
547
            this.clearLayer();
 
548
            this.setAutomaticMode(false);
 
549
        }
 
550
    },
 
551
 
 
552
    /**
 
553
     * Method: clearLayer
 
554
     * Delete all the geometries from the layer with the exception of the
 
555
     * currently edited feature (if any) and its handles.
 
556
     *
 
557
     * Parameters:
 
558
     * value - {Boolean}
 
559
     */
 
560
    clearLayer: function() {
 
561
        var toRemove = [];
 
562
        var layer = this.layer;
 
563
        var edited = this.modifyFeature.feature;
 
564
        for (var i = 0; i < layer.features.length; ++i) {
 
565
            var cur = layer.features[i];
 
566
            if (cur != edited &&
 
567
               cur.data && cur.data.endEdit) {
 
568
                toRemove.push(cur);
 
569
            }
 
570
        }
 
571
        layer.removeFeatures(toRemove);
 
572
    }
 
573
});
 
574
Ext.reg('featurelist', mapfish.widgets.editing.FeatureList);
 
575
 
 
576
/**
 
577
 * Method: geometryClickHandler
 
578
 * Global function call by the onclick handler of the geometries' icons.
 
579
 *
 
580
 * Parameters:
 
581
 * gridId - {String} ID of the Grid
 
582
 * recordId - {String} ID of the record
 
583
 * colName - {String} Name of the geometry column
 
584
 */
 
585
mapfish.widgets.editing.FeatureList.geometryClickHandler = function(
 
586
    gridId, recordId, colName) {
 
587
 
 
588
    var grid = Ext.getCmp(gridId);
 
589
    if (grid) {
 
590
        var record = grid.store.getById(recordId);
 
591
        if (record) {
 
592
            grid.editGeometry(record, colName, true);
 
593
        } else {
 
594
            OpenLayers.Console.error("Cannot find record with id=" + recordId);
 
595
        }
 
596
    } else {
 
597
        OpenLayers.Console.error("Cannot find grid with id=" + gridId);
 
598
    }
 
599
};
 
600
 
 
601
/**
 
602
 * APIMethod: createRecord
 
603
 * Generate a Record constructor for a specific record layout and with
 
604
 * support for the 'geo' type. This function wraps Ext.data.Record.create.
 
605
 *
 
606
 * Parameters:
 
607
 * cols - {Array} An Array of field definition objects which specify
 
608
 *     field names, and optionally, data types, and a mapping for an
 
609
 *     Ext.data.Reader to extract the field's value from a data
 
610
 *     object. For further information see the Ext API reference
 
611
 *     (Ext.data.Record.create).
 
612
 *
 
613
 * Returns
 
614
 * {Function} - Record constructor
 
615
 */
 
616
mapfish.widgets.editing.FeatureList.createRecord = function(cols) {
 
617
    for (var i = 0; i < cols.length; ++i) {
 
618
        var col = cols[i];
 
619
        if (col.type == 'geo') {
 
620
            if (!col.convert) {
 
621
                col.convert = function(v) {
 
622
                    return v;
 
623
                };
 
624
            }
 
625
            if (!col.sortType) {
 
626
                col.sortType = Ext.data.SortTypes.none();
 
627
            }
 
628
        }
 
629
    }
 
630
    return Ext.data.Record.create.apply(null, arguments);
 
631
};