diff --git a/examples/filters.html b/examples/filters.html
index da9c4235..8340fa2b 100644
--- a/examples/filters.html
+++ b/examples/filters.html
@@ -26,6 +26,9 @@
Filter Builders
Setting allowGroups to false disallows nested logical filters.
+
+
+ Setting allowCQL to true provides to option to specify the filter as CQL.
The above filter builder allows group conditions.
diff --git a/examples/filters.js b/examples/filters.js
index 71d0c3a2..f7e2ab94 100644
--- a/examples/filters.js
+++ b/examples/filters.js
@@ -72,6 +72,29 @@ Ext.onReady(function() {
}]
});
+ var panelcql = new Ext.Panel({
+ title: "Filter Builder (no groups, but with CQL option)",
+ renderTo: "panelcql",
+ width: 320,
+ items: [{
+ xtype: "gxp_filterbuilder",
+ style: "padding: .3em .3em .1em .1em",
+ allowGroups: false,
+ allowCQL: true,
+ filter: filter.clone(),
+ attributes: new GeoExt.data.AttributeStore({
+ url: "data/describe_feature_type.xml",
+ ignore: {name: "the_geom"}
+ })
+ }],
+ bbar: ["->", {
+ text: "View Filter Encoding",
+ handler: function() {
+ showFE(panel1);
+ }
+ }]
+ });
+
var panel2 = new Ext.Panel({
title: "Filter Builder (with groups)",
renderTo: "panel2",
diff --git a/examples/symbolizer-grid.html b/examples/symbolizer-grid.html
index da86cd8e..47ea91b5 100644
--- a/examples/symbolizer-grid.html
+++ b/examples/symbolizer-grid.html
@@ -5,18 +5,17 @@
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/examples/symbolizer-grid.js b/examples/symbolizer-grid.js
index 137d6654..9598eeaf 100644
--- a/examples/symbolizer-grid.js
+++ b/examples/symbolizer-grid.js
@@ -6,10 +6,6 @@ symbolizers.push(new OpenLayers.Symbolizer.Point({
strokeColor: "red",
strokeWidth: 1
}));
-symbolizers.push(new OpenLayers.Symbolizer.Line({
- strokeColor: "#669900",
- strokeWidth: 3
-}));
symbolizers.push(new OpenLayers.Symbolizer.Polygon({
fillColor: "olive",
fillOpacity: 0.25,
@@ -64,6 +60,7 @@ var showSLD = function() {
Ext.onReady(function() {
grid = new gxp.grid.SymbolizerGrid({
symbolizers: symbolizers,
+ symbolType: "Polygon",
height: 375,
width: 400,
renderTo: "grid",
diff --git a/examples/ux/treegrid/TreeGrid.js b/examples/ux/treegrid/TreeGrid.js
deleted file mode 100644
index 0e9ad6fb..00000000
--- a/examples/ux/treegrid/TreeGrid.js
+++ /dev/null
@@ -1,410 +0,0 @@
-/*!
- * Ext JS Library 3.4.0
- * Copyright(c) 2006-2011 Sencha Inc.
- * licensing@sencha.com
- * http://www.sencha.com/license
- */
-/**
- * @class Ext.ux.tree.TreeGrid
- * @extends Ext.tree.TreePanel
- *
- * @xtype treegrid
- */
-Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, {
- rootVisible : false,
- useArrows : true,
- lines : false,
- borderWidth : Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell
- cls : 'x-treegrid',
-
- columnResize : true,
- enableSort : true,
- reserveScrollOffset : true,
- enableHdMenu : true,
-
- columnsText : 'Columns',
-
- initComponent : function() {
- if(!this.root) {
- this.root = new Ext.tree.AsyncTreeNode({text: 'Root'});
- }
-
- // initialize the loader
- var l = this.loader;
- if(!l){
- l = new Ext.ux.tree.TreeGridLoader({
- dataUrl: this.dataUrl,
- requestMethod: this.requestMethod,
- store: this.store
- });
- }else if(Ext.isObject(l) && !l.load){
- l = new Ext.ux.tree.TreeGridLoader(l);
- }
- this.loader = l;
-
- Ext.ux.tree.TreeGrid.superclass.initComponent.call(this);
-
- this.initColumns();
-
- if(this.enableSort) {
- this.treeGridSorter = new Ext.ux.tree.TreeGridSorter(this, this.enableSort);
- }
-
- if(this.columnResize){
- this.colResizer = new Ext.tree.ColumnResizer(this.columnResize);
- this.colResizer.init(this);
- }
-
- var c = this.columns;
- if(!this.internalTpl){
- this.internalTpl = new Ext.XTemplate(
- '',
- ''
- );
- }
-
- if(!this.colgroupTpl) {
- this.colgroupTpl = new Ext.XTemplate(
- ''
- );
- }
- },
-
- initColumns : function() {
- var cs = this.columns,
- len = cs.length,
- columns = [],
- i, c;
-
- for(i = 0; i < len; i++){
- c = cs[i];
- if(!c.isColumn) {
- c.xtype = c.xtype ? (/^tg/.test(c.xtype) ? c.xtype : 'tg' + c.xtype) : 'tgcolumn';
- c = Ext.create(c);
- }
- c.init(this);
- columns.push(c);
-
- if(this.enableSort !== false && c.sortable !== false) {
- c.sortable = true;
- this.enableSort = true;
- }
- }
-
- this.columns = columns;
- },
-
- onRender : function(){
- Ext.tree.TreePanel.superclass.onRender.apply(this, arguments);
-
- this.el.addClass('x-treegrid');
-
- this.outerCt = this.body.createChild({
- cls:'x-tree-root-ct x-treegrid-ct ' + (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines')
- });
-
- this.internalTpl.overwrite(this.outerCt, {columns: this.columns});
-
- this.mainHd = Ext.get(this.outerCt.dom.firstChild);
- this.innerHd = Ext.get(this.mainHd.dom.firstChild);
- this.innerBody = Ext.get(this.outerCt.dom.lastChild);
- this.innerCt = Ext.get(this.innerBody.dom.firstChild);
-
- this.colgroupTpl.insertFirst(this.innerCt, {columns: this.columns});
-
- if(this.hideHeaders){
- this.el.child('.x-grid3-header').setDisplayed('none');
- }
- else if(this.enableHdMenu !== false){
- this.hmenu = new Ext.menu.Menu({id: this.id + '-hctx'});
- if(this.enableColumnHide !== false){
- this.colMenu = new Ext.menu.Menu({id: this.id + '-hcols-menu'});
- this.colMenu.on({
- scope: this,
- beforeshow: this.beforeColMenuShow,
- itemclick: this.handleHdMenuClick
- });
- this.hmenu.add({
- itemId:'columns',
- hideOnClick: false,
- text: this.columnsText,
- menu: this.colMenu,
- iconCls: 'x-cols-icon'
- });
- }
- this.hmenu.on('itemclick', this.handleHdMenuClick, this);
- }
- },
-
- setRootNode : function(node){
- node.attributes.uiProvider = Ext.ux.tree.TreeGridRootNodeUI;
- node = Ext.ux.tree.TreeGrid.superclass.setRootNode.call(this, node);
- if(this.innerCt) {
- this.colgroupTpl.insertFirst(this.innerCt, {columns: this.columns});
- }
- return node;
- },
-
- clearInnerCt : function(){
- if(Ext.isIE){
- var dom = this.innerCt.dom;
- while(dom.firstChild){
- dom.removeChild(dom.firstChild);
- }
- }else{
- Ext.ux.tree.TreeGrid.superclass.clearInnerCt.call(this);
- }
- },
-
- initEvents : function() {
- Ext.ux.tree.TreeGrid.superclass.initEvents.apply(this, arguments);
-
- this.mon(this.innerBody, 'scroll', this.syncScroll, this);
- this.mon(this.innerHd, 'click', this.handleHdDown, this);
- this.mon(this.mainHd, {
- scope: this,
- mouseover: this.handleHdOver,
- mouseout: this.handleHdOut
- });
- },
-
- onResize : function(w, h) {
- Ext.ux.tree.TreeGrid.superclass.onResize.apply(this, arguments);
-
- var bd = this.innerBody.dom;
- var hd = this.innerHd.dom;
-
- if(!bd){
- return;
- }
-
- if(Ext.isNumber(h)){
- bd.style.height = this.body.getHeight(true) - hd.offsetHeight + 'px';
- }
-
- if(Ext.isNumber(w)){
- var sw = Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
- if(this.reserveScrollOffset || ((bd.offsetWidth - bd.clientWidth) > 10)){
- this.setScrollOffset(sw);
- }else{
- var me = this;
- setTimeout(function(){
- me.setScrollOffset(bd.offsetWidth - bd.clientWidth > 10 ? sw : 0);
- }, 10);
- }
- }
- },
-
- updateColumnWidths : function() {
- var cols = this.columns,
- colCount = cols.length,
- groups = this.outerCt.query('colgroup'),
- groupCount = groups.length,
- c, g, i, j;
-
- for(i = 0; i 0 && this.columns[index]) {
- this.setColumnVisible(index, !item.checked);
- }
- }
-
- return true;
- },
-
- setColumnVisible : function(index, visible) {
- this.columns[index].hidden = !visible;
- this.updateColumnWidths();
- },
-
- /**
- * Scrolls the grid to the top
- */
- scrollToTop : function(){
- this.innerBody.dom.scrollTop = 0;
- this.innerBody.dom.scrollLeft = 0;
- },
-
- // private
- syncScroll : function(){
- this.syncHeaderScroll();
- var mb = this.innerBody.dom;
- this.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop);
- },
-
- // private
- syncHeaderScroll : function(){
- var mb = this.innerBody.dom;
- this.innerHd.dom.scrollLeft = mb.scrollLeft;
- this.innerHd.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
- },
-
- registerNode : function(n) {
- Ext.ux.tree.TreeGrid.superclass.registerNode.call(this, n);
- if(!n.uiProvider && !n.isRoot && !n.ui.isTreeGridNodeUI) {
- n.ui = new Ext.ux.tree.TreeGridNodeUI(n);
- }
- }
-});
-
-Ext.reg('treegrid', Ext.ux.tree.TreeGrid);
\ No newline at end of file
diff --git a/examples/ux/treegrid/TreeGridColumnResizer.js b/examples/ux/treegrid/TreeGridColumnResizer.js
deleted file mode 100644
index 1fdc9cbe..00000000
--- a/examples/ux/treegrid/TreeGridColumnResizer.js
+++ /dev/null
@@ -1,119 +0,0 @@
-/*!
- * Ext JS Library 3.4.0
- * Copyright(c) 2006-2011 Sencha Inc.
- * licensing@sencha.com
- * http://www.sencha.com/license
- */
-/**
- * @class Ext.tree.ColumnResizer
- * @extends Ext.util.Observable
- */
-Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, {
- /**
- * @cfg {Number} minWidth The minimum width the column can be dragged to.
- * Defaults to 14.
- */
- minWidth: 14,
-
- constructor: function(config){
- Ext.apply(this, config);
- Ext.tree.ColumnResizer.superclass.constructor.call(this);
- },
-
- init : function(tree){
- this.tree = tree;
- tree.on('render', this.initEvents, this);
- },
-
- initEvents : function(tree){
- tree.mon(tree.innerHd, 'mousemove', this.handleHdMove, this);
- this.tracker = new Ext.dd.DragTracker({
- onBeforeStart: this.onBeforeStart.createDelegate(this),
- onStart: this.onStart.createDelegate(this),
- onDrag: this.onDrag.createDelegate(this),
- onEnd: this.onEnd.createDelegate(this),
- tolerance: 3,
- autoStart: 300
- });
- this.tracker.initEl(tree.innerHd);
- tree.on('beforedestroy', this.tracker.destroy, this.tracker);
- },
-
- handleHdMove : function(e, t){
- var hw = 5,
- x = e.getPageX(),
- hd = e.getTarget('.x-treegrid-hd', 3, true);
-
- if(hd){
- var r = hd.getRegion(),
- ss = hd.dom.style,
- pn = hd.dom.parentNode;
-
- if(x - r.left <= hw && hd.dom !== pn.firstChild) {
- var ps = hd.dom.previousSibling;
- while(ps && Ext.fly(ps).hasClass('x-treegrid-hd-hidden')) {
- ps = ps.previousSibling;
- }
- if(ps) {
- this.activeHd = Ext.get(ps);
- ss.cursor = Ext.isWebKit ? 'e-resize' : 'col-resize';
- }
- } else if(r.right - x <= hw) {
- var ns = hd.dom;
- while(ns && Ext.fly(ns).hasClass('x-treegrid-hd-hidden')) {
- ns = ns.previousSibling;
- }
- if(ns) {
- this.activeHd = Ext.get(ns);
- ss.cursor = Ext.isWebKit ? 'w-resize' : 'col-resize';
- }
- } else{
- delete this.activeHd;
- ss.cursor = '';
- }
- }
- },
-
- onBeforeStart : function(e){
- this.dragHd = this.activeHd;
- return !!this.dragHd;
- },
-
- onStart : function(e){
- this.dragHeadersDisabled = this.tree.headersDisabled;
- this.tree.headersDisabled = true;
- this.proxy = this.tree.body.createChild({cls:'x-treegrid-resizer'});
- this.proxy.setHeight(this.tree.body.getHeight());
-
- var x = this.tracker.getXY()[0];
-
- this.hdX = this.dragHd.getX();
- this.hdIndex = this.tree.findHeaderIndex(this.dragHd);
-
- this.proxy.setX(this.hdX);
- this.proxy.setWidth(x-this.hdX);
-
- this.maxWidth = this.tree.outerCt.getWidth() - this.tree.innerBody.translatePoints(this.hdX).left;
- },
-
- onDrag : function(e){
- var cursorX = this.tracker.getXY()[0];
- this.proxy.setWidth((cursorX-this.hdX).constrain(this.minWidth, this.maxWidth));
- },
-
- onEnd : function(e){
- var nw = this.proxy.getWidth(),
- tree = this.tree,
- disabled = this.dragHeadersDisabled;
-
- this.proxy.remove();
- delete this.dragHd;
-
- tree.columns[this.hdIndex].width = nw;
- tree.updateColumnWidths();
-
- setTimeout(function(){
- tree.headersDisabled = disabled;
- }, 100);
- }
-});
\ No newline at end of file
diff --git a/examples/ux/treegrid/TreeGridColumns.js b/examples/ux/treegrid/TreeGridColumns.js
deleted file mode 100644
index a532bdc6..00000000
--- a/examples/ux/treegrid/TreeGridColumns.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/*!
- * Ext JS Library 3.4.0
- * Copyright(c) 2006-2011 Sencha Inc.
- * licensing@sencha.com
- * http://www.sencha.com/license
- */
-(function() {
- Ext.override(Ext.list.Column, {
- init : function() {
- var types = Ext.data.Types,
- st = this.sortType;
-
- if(this.type){
- if(Ext.isString(this.type)){
- this.type = Ext.data.Types[this.type.toUpperCase()] || types.AUTO;
- }
- }else{
- this.type = types.AUTO;
- }
-
- // named sortTypes are supported, here we look them up
- if(Ext.isString(st)){
- this.sortType = Ext.data.SortTypes[st];
- }else if(Ext.isEmpty(st)){
- this.sortType = this.type.sortType;
- }
- }
- });
-
- Ext.tree.Column = Ext.extend(Ext.list.Column, {});
- Ext.tree.NumberColumn = Ext.extend(Ext.list.NumberColumn, {});
- Ext.tree.DateColumn = Ext.extend(Ext.list.DateColumn, {});
- Ext.tree.BooleanColumn = Ext.extend(Ext.list.BooleanColumn, {});
-
- Ext.reg('tgcolumn', Ext.tree.Column);
- Ext.reg('tgnumbercolumn', Ext.tree.NumberColumn);
- Ext.reg('tgdatecolumn', Ext.tree.DateColumn);
- Ext.reg('tgbooleancolumn', Ext.tree.BooleanColumn);
-})();
diff --git a/examples/ux/treegrid/TreeGridLoader.js b/examples/ux/treegrid/TreeGridLoader.js
deleted file mode 100644
index 62eb417c..00000000
--- a/examples/ux/treegrid/TreeGridLoader.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/*!
- * Ext JS Library 3.4.0
- * Copyright(c) 2006-2011 Sencha Inc.
- * licensing@sencha.com
- * http://www.sencha.com/license
- */
-/**
- * @class Ext.ux.tree.TreeGridLoader
- * @extends Ext.tree.TreeLoader
- */
-Ext.ux.tree.TreeGridLoader = Ext.extend(Ext.tree.TreeLoader, {
- createNode : function(attr) {
- if (!attr.uiProvider) {
- attr.uiProvider = Ext.ux.tree.TreeGridNodeUI;
- }
- return Ext.tree.TreeLoader.prototype.createNode.call(this, attr);
- }
-});
\ No newline at end of file
diff --git a/examples/ux/treegrid/TreeGridNodeUI.js b/examples/ux/treegrid/TreeGridNodeUI.js
deleted file mode 100644
index e12bf950..00000000
--- a/examples/ux/treegrid/TreeGridNodeUI.js
+++ /dev/null
@@ -1,107 +0,0 @@
-/*!
- * Ext JS Library 3.4.0
- * Copyright(c) 2006-2011 Sencha Inc.
- * licensing@sencha.com
- * http://www.sencha.com/license
- */
-/**
- * @class Ext.ux.tree.TreeGridNodeUI
- * @extends Ext.tree.TreeNodeUI
- */
-Ext.ux.tree.TreeGridNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
- isTreeGridNodeUI: true,
-
- renderElements : function(n, a, targetNode, bulkRender){
- var t = n.getOwnerTree(),
- cols = t.columns,
- c = cols[0],
- i, buf, len;
-
- this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
-
- buf = [
- '',
- '',
- '',
- '', this.indentMarkup, "",
- ' ',
- ' ',
- '',
- '', (c.tpl ? c.tpl.apply(a) : a[c.dataIndex] || c.text), '',
- ' | '
- ];
-
- for(i = 1, len = cols.length; i < len; i++){
- c = cols[i];
- buf.push(
- '',
- ' ',
- (c.tpl ? c.tpl.apply(a) : a[c.dataIndex]),
- ' ',
- ' | '
- );
- }
-
- buf.push(
- '
',
- ''
- );
- for(i = 0, len = cols.length; i');
- }
- buf.push(' |
');
-
- if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
- this.wrap = Ext.DomHelper.insertHtml("beforeBegin", n.nextSibling.ui.getEl(), buf.join(''));
- }else{
- this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(''));
- }
-
- this.elNode = this.wrap.childNodes[0];
- this.ctNode = this.wrap.childNodes[1].firstChild.firstChild;
- var cs = this.elNode.firstChild.childNodes;
- this.indentNode = cs[0];
- this.ecNode = cs[1];
- this.iconNode = cs[2];
- this.anchor = cs[3];
- this.textNode = cs[3].firstChild;
- },
-
- // private
- animExpand : function(cb){
- this.ctNode.style.display = "";
- Ext.ux.tree.TreeGridNodeUI.superclass.animExpand.call(this, cb);
- }
-});
-
-Ext.ux.tree.TreeGridRootNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
- isTreeGridNodeUI: true,
-
- // private
- render : function(){
- if(!this.rendered){
- this.wrap = this.ctNode = this.node.ownerTree.innerCt.dom;
- this.node.expanded = true;
- }
-
- if(Ext.isWebKit) {
- // weird table-layout: fixed issue in webkit
- var ct = this.ctNode;
- ct.style.tableLayout = null;
- (function() {
- ct.style.tableLayout = 'fixed';
- }).defer(1);
- }
- },
-
- destroy : function(){
- if(this.elNode){
- Ext.dd.Registry.unregister(this.elNode.id);
- }
- delete this.node;
- },
-
- collapse : Ext.emptyFn,
- expand : Ext.emptyFn
-});
\ No newline at end of file
diff --git a/examples/ux/treegrid/TreeGridSorter.js b/examples/ux/treegrid/TreeGridSorter.js
deleted file mode 100644
index 294a7eb5..00000000
--- a/examples/ux/treegrid/TreeGridSorter.js
+++ /dev/null
@@ -1,137 +0,0 @@
-/*!
- * Ext JS Library 3.4.0
- * Copyright(c) 2006-2011 Sencha Inc.
- * licensing@sencha.com
- * http://www.sencha.com/license
- */
-Ext.ns('Ext.ux.tree');
-
-/**
- * @class Ext.ux.tree.TreeGridSorter
- * @extends Ext.tree.TreeSorter
- * Provides sorting of nodes in a {@link Ext.ux.tree.TreeGrid}. The TreeGridSorter automatically monitors events on the
- * associated TreeGrid that might affect the tree's sort order (beforechildrenrendered, append, insert and textchange).
- * Example usage:
- *
- new Ext.ux.tree.TreeGridSorter(myTreeGrid, {
- folderSort: true,
- dir: "desc",
- sortType: function(node) {
- // sort by a custom, typed attribute:
- return parseInt(node.id, 10);
- }
- });
-
- * @constructor
- * @param {TreeGrid} tree
- * @param {Object} config
- */
-Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, {
- /**
- * @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to ['sort-asc', 'sort-desc'])
- */
- sortClasses : ['sort-asc', 'sort-desc'],
- /**
- * @cfg {String} sortAscText The text displayed in the 'Sort Ascending' menu item (defaults to 'Sort Ascending')
- */
- sortAscText : 'Sort Ascending',
- /**
- * @cfg {String} sortDescText The text displayed in the 'Sort Descending' menu item (defaults to 'Sort Descending')
- */
- sortDescText : 'Sort Descending',
-
- constructor : function(tree, config) {
- if(!Ext.isObject(config)) {
- config = {
- property: tree.columns[0].dataIndex || 'text',
- folderSort: true
- }
- }
-
- Ext.ux.tree.TreeGridSorter.superclass.constructor.apply(this, arguments);
-
- this.tree = tree;
- tree.on('headerclick', this.onHeaderClick, this);
- tree.ddAppendOnly = true;
-
- var me = this;
- this.defaultSortFn = function(n1, n2){
-
- var desc = me.dir && me.dir.toLowerCase() == 'desc',
- prop = me.property || 'text',
- sortType = me.sortType,
- caseSensitive = me.caseSensitive === true,
- leafAttr = me.leafAttr || 'leaf',
- attr1 = n1.attributes,
- attr2 = n2.attributes;
-
- if(me.folderSort){
- if(attr1[leafAttr] && !attr2[leafAttr]){
- return 1;
- }
- if(!attr1[leafAttr] && attr2[leafAttr]){
- return -1;
- }
- }
- var prop1 = attr1[prop],
- prop2 = attr2[prop],
- v1 = sortType ? sortType(prop1) : (caseSensitive ? prop1 : prop1.toUpperCase());
- v2 = sortType ? sortType(prop2) : (caseSensitive ? prop2 : prop2.toUpperCase());
-
- if(v1 < v2){
- return desc ? +1 : -1;
- }else if(v1 > v2){
- return desc ? -1 : +1;
- }else{
- return 0;
- }
- };
-
- tree.on('afterrender', this.onAfterTreeRender, this, {single: true});
- tree.on('headermenuclick', this.onHeaderMenuClick, this);
- },
-
- onAfterTreeRender : function() {
- if(this.tree.hmenu){
- this.tree.hmenu.insert(0,
- {itemId:'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc'},
- {itemId:'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
- );
- }
- this.updateSortIcon(0, 'asc');
- },
-
- onHeaderMenuClick : function(c, id, index) {
- if(id === 'asc' || id === 'desc') {
- this.onHeaderClick(c, null, index);
- return false;
- }
- },
-
- onHeaderClick : function(c, el, i) {
- if(c && !this.tree.headersDisabled){
- var me = this;
-
- me.property = c.dataIndex;
- me.dir = c.dir = (c.dir === 'desc' ? 'asc' : 'desc');
- me.sortType = c.sortType;
- me.caseSensitive === Ext.isBoolean(c.caseSensitive) ? c.caseSensitive : this.caseSensitive;
- me.sortFn = c.sortFn || this.defaultSortFn;
-
- this.tree.root.cascade(function(n) {
- if(!n.isLeaf()) {
- me.updateSort(me.tree, n);
- }
- });
-
- this.updateSortIcon(i, c.dir);
- }
- },
-
- // private
- updateSortIcon : function(col, dir){
- var sc = this.sortClasses,
- hds = this.tree.innerHd.select('td').removeClass(sc);
- hds.item(col).addClass(sc[dir == 'desc' ? 1 : 0]);
- }
-});
\ No newline at end of file
diff --git a/examples/ux/treegrid/treegrid.css b/examples/ux/treegrid/treegrid.css
deleted file mode 100644
index 0f946844..00000000
--- a/examples/ux/treegrid/treegrid.css
+++ /dev/null
@@ -1,57 +0,0 @@
-/*!
- * Ext JS Library 3.4.0
- * Copyright(c) 2006-2011 Sencha Inc.
- * licensing@sencha.com
- * http://www.sencha.com/license
- */
-.x-treegrid-root-table {
- border-right: 1px solid;
-}
-
-.x-treegrid-root-node {
- overflow: auto;
-}
-
-.x-treegrid-hd-hidden {
- visibility: hidden;
- border: 0;
- width: 0;
-}
-
-.x-treegrid-col {
- border-bottom: 1px solid;
- height: 20px;
- overflow: hidden;
- vertical-align: top;
- -o-text-overflow: ellipsis;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.x-treegrid-text {
- padding-left: 4px;
- -moz-user-select: none;
- -khtml-user-select: none;
-}
-
-.x-treegrid-resizer {
- border-left:1px solid;
- border-right:1px solid;
- position:absolute;
- left:0;
- top:0;
-}
-
-.x-treegrid-header-inner {
- overflow: hidden;
-}
-
-.x-treegrid-root-table,
-.x-treegrid-col {
- border-color: #ededed;
-}
-
-.x-treegrid-resizer {
- border-left-color:#555;
- border-right-color:#555;
-}
\ No newline at end of file
diff --git a/examples/viewer-layermanager.html b/examples/viewer-layermanager.html
index d18744c1..2ab8f5a1 100644
--- a/examples/viewer-layermanager.html
+++ b/examples/viewer-layermanager.html
@@ -5,12 +5,22 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/viewer-layermanager.js b/examples/viewer-layermanager.js
index 478ee15f..b363d554 100644
--- a/examples/viewer-layermanager.js
+++ b/examples/viewer-layermanager.js
@@ -63,6 +63,10 @@ Ext.onReady(function() {
ptype: "gxp_layerproperties",
outputTarget: "west",
actionTarget: ["tree.tbar", "tree.contextMenu"]
+ }, {
+ ptype: "gxp_styler",
+ outputTarget: "west",
+ actionTarget: ["tree.tbar", "tree.contextMenu"]
}, {
ptype: "gxp_zoomtoextent",
actionTarget: "map.tbar"
diff --git a/examples/viewer.html b/examples/viewer.html
index 217b0c43..d5303556 100644
--- a/examples/viewer.html
+++ b/examples/viewer.html
@@ -5,12 +5,21 @@
+
+
+
+
+
+
+
+
+
diff --git a/examples/viewer.js b/examples/viewer.js
index c7c1ec64..c3b84316 100644
--- a/examples/viewer.js
+++ b/examples/viewer.js
@@ -54,6 +54,9 @@ Ext.onReady(function() {
}, {
ptype: "gxp_layerproperties",
actionTarget: ["tree.tbar", "tree.contextMenu"]
+ }, {
+ ptype: "gxp_styler",
+ actionTarget: ["tree.tbar", "tree.contextMenu"]
}, {
ptype: "gxp_zoomtoextent",
actionTarget: "map.tbar"
diff --git a/src/script/plugins/AddLayers.js b/src/script/plugins/AddLayers.js
index 288a68f9..030aa0d5 100644
--- a/src/script/plugins/AddLayers.js
+++ b/src/script/plugins/AddLayers.js
@@ -642,7 +642,7 @@ gxp.plugins.AddLayers = Ext.extend(gxp.plugins.Tool, {
rec = store.getAt(index),
source = rec && this.target.layerSources[rec.get("id")];
if (source) {
- if (source.title !== rec.get("title")) {
+ if (source.title !== rec.get("title") && !Ext.isEmpty(source.title)) {
rec.set("title", source.title);
sourceComboBox.setValue(rec.get(valueField));
}
diff --git a/src/script/plugins/CSWCatalogueSource.js b/src/script/plugins/CSWCatalogueSource.js
index 03967a72..ab4c47e7 100644
--- a/src/script/plugins/CSWCatalogueSource.js
+++ b/src/script/plugins/CSWCatalogueSource.js
@@ -10,7 +10,7 @@
* @requires plugins/CatalogueSource.js
* @requires GeoExt/data/CSWRecordsReader.js
* @requires GeoExt/data/ProtocolProxy.js
- * @requires OpenLayers.Protocol.CSW/v2_0_2.js
+ * @requires OpenLayers/Protocol/CSW/v2_0_2.js
*/
/** api: (define)
diff --git a/src/script/plugins/WMSSource.js b/src/script/plugins/WMSSource.js
index df918e83..e144184c 100644
--- a/src/script/plugins/WMSSource.js
+++ b/src/script/plugins/WMSSource.js
@@ -201,7 +201,7 @@ gxp.plugins.WMSSource = Ext.extend(gxp.plugins.LayerSource, {
* Reload the store when the authorization changes.
*/
onAuthorizationChange: function() {
- if (this.store && this.store.url.charAt(0) === "/") {
+ if (this.store && this.url.charAt(0) === "/") {
this.store.reload();
}
},
@@ -558,7 +558,7 @@ gxp.plugins.WMSSource = Ext.extend(gxp.plugins.LayerSource, {
record.json = config;
} else {
- if (window.console && this.store.getCount() > 0) {
+ if (window.console && this.store.getCount() > 0 && config.name !== undefined) {
console.warn("Could not create layer record for layer '" + config.name + "'. Check if the layer is found in the WMS GetCapabilities response.");
}
}
diff --git a/src/script/util.js b/src/script/util.js
index b20ee338..9735a5e2 100644
--- a/src/script/util.js
+++ b/src/script/util.js
@@ -123,6 +123,55 @@ gxp.util = {
return a.href;
},
+ /** api: function[throttle]
+ * :arg func: ``Function``
+ * :arg interval: ``Integer``
+ * :arg scope: ``Object``
+ * :return: ``Function``
+ *
+ * Returns a function, that, when invoked, will only be triggered at
+ * most once during a given window of time.
+ */
+ throttle: (function() {
+ // taken from ExtJS 4.1
+ // TODO remove when we upgrade to ExtJS 4.1 or higher.
+ /**
+ * Creates a throttled version of the passed function which, when called repeatedly and
+ * rapidly, invokes the passed function only after a certain interval has elapsed since the
+ * previous invocation.
+ *
+ * This is useful for wrapping functions which may be called repeatedly, such as
+ * a handler of a mouse move event when the processing is expensive.
+ *
+ * @param {Function} fn The function to execute at a regular time interval.
+ * @param {Number} interval The interval **in milliseconds** on which the passed function is executed.
+ * @param {Object} scope (optional) The scope (`this` reference) in which
+ * the passed function is executed. If omitted, defaults to the scope specified by the caller.
+ * @returns {Function} A function which invokes the passed function at the specified interval.
+ */
+ var createThrottled = function(fn, interval, scope) {
+ var lastCallTime, elapsed, lastArgs, timer, execute = function() {
+ fn.apply(scope || this, lastArgs);
+ lastCallTime = new Date().getTime();
+ };
+
+ return function() {
+ elapsed = new Date().getTime() - lastCallTime;
+ lastArgs = arguments;
+
+ clearTimeout(timer);
+ if (!lastCallTime || (elapsed >= interval)) {
+ execute();
+ } else {
+ timer = setTimeout(execute, interval - elapsed);
+ }
+ };
+ };
+ return function(func, interval, scope) {
+ return createThrottled(func, interval, scope);
+ };
+ })(),
+
/** api: function[md5]
* :arg data: ``String``
* :returns: ``String`` md5 hash
diff --git a/src/script/widgets/CatalogueSearchPanel.js b/src/script/widgets/CatalogueSearchPanel.js
index 3213176b..70ecaf7c 100644
--- a/src/script/widgets/CatalogueSearchPanel.js
+++ b/src/script/widgets/CatalogueSearchPanel.js
@@ -88,7 +88,7 @@ gxp.CatalogueSearchPanel = Ext.extend(Ext.Panel, {
for (var key in this.sources) {
sourceComboData.push([key, this.sources[key].title]);
}
- if (sourceComboData.length === 1) {
+ if (sourceComboData.length >= 1) {
this.selectedSource = sourceComboData[0][0];
}
var filterOptions = [['datatype', 'data type'], ['extent', 'spatial extent'], ['category', 'category']];
@@ -109,6 +109,14 @@ gxp.CatalogueSearchPanel = Ext.extend(Ext.Panel, {
emptyText: this.searchFieldEmptyText,
ref: "../../search",
name: "search",
+ listeners: {
+ specialkey: function(field, e) {
+ if (e.getKey() == e.ENTER) {
+ this.performQuery();
+ }
+ },
+ scope: this
+ },
width: 300
}, {
xtype: "button",
diff --git a/src/script/widgets/FillSymbolizer.js b/src/script/widgets/FillSymbolizer.js
index ff205430..c9f7cf8c 100644
--- a/src/script/widgets/FillSymbolizer.js
+++ b/src/script/widgets/FillSymbolizer.js
@@ -70,7 +70,7 @@ gxp.FillSymbolizer = Ext.extend(Ext.FormPanel, {
border: false,
/** i18n */
- fillText: "Fill",
+ titleText: "Fill",
colorText: "Color",
opacityText: "Opacity",
@@ -86,8 +86,8 @@ gxp.FillSymbolizer = Ext.extend(Ext.FormPanel, {
}
var sliderValue = 100;
- if (this.opacityProperty in this.symbolizer) {
- sliderValue = this.symbolizer[this.opacityProperty];
+ if (this.opacityProperty in this.symbolizer && this.symbolizer[this.opacityProperty] !== undefined) {
+ sliderValue = this.symbolizer[this.opacityProperty]*100;
}
else if (OpenLayers.Renderer.defaultSymbolizer[this.opacityProperty]) {
sliderValue = OpenLayers.Renderer.defaultSymbolizer[this.opacityProperty]*100;
@@ -95,7 +95,7 @@ gxp.FillSymbolizer = Ext.extend(Ext.FormPanel, {
this.items = [{
xtype: "fieldset",
- title: this.fillText,
+ title: this.titleText,
autoHeight: true,
checkboxToggle: this.checkboxToggle,
collapsed: this.checkboxToggle === true &&
diff --git a/src/script/widgets/FilterBuilder.js b/src/script/widgets/FilterBuilder.js
index 1b48a2dc..6240a4e9 100644
--- a/src/script/widgets/FilterBuilder.js
+++ b/src/script/widgets/FilterBuilder.js
@@ -87,6 +87,15 @@ gxp.FilterBuilder = Ext.extend(Ext.Container, {
addConditionText: "add condition",
addGroupText: "add group",
removeConditionText: "remove condition",
+ switchToFilterBuilderText: "Switch back to filter builder",
+ cqlPrefixText: "or ",
+ cqlText: "use CQL filter instead",
+
+ /** api: config[allowCQL]
+ * ``Boolean`` If true, provide an option to create a filter by
+ * providing CQL. Defaults to false
+ */
+ allowCQL: false,
/** api: config[allowGroups]
* ``Boolean``
@@ -97,6 +106,9 @@ gxp.FilterBuilder = Ext.extend(Ext.Container, {
allowGroups: true,
initComponent: function() {
+ if (this.allowCQL) {
+ this.cqlFormat = new OpenLayers.Format.CQL();
+ }
var defConfig = {
defaultBuilderType: gxp.FilterBuilder.ANY_OF
};
@@ -109,6 +121,26 @@ gxp.FilterBuilder = Ext.extend(Ext.Container, {
this.builderType = this.getBuilderType();
this.items = [{
+ xtype: "container",
+ layout: "form",
+ hidden: true,
+ border: false,
+ hideLabels: true,
+ style: "padding-left: 2px",
+ ref: "cqlForm",
+ items: [{
+ xtype: "textarea",
+ anchor: '99%',
+ ref: "../cqlField",
+ width: '100%',
+ growMax: 100
+ }],
+ buttons: [{
+ text: this.switchToFilterBuilderText,
+ handler: this.switchToFilterBuilder,
+ scope: this
+ }]
+ }, {
xtype: "container",
layout: "form",
ref: "form",
@@ -169,9 +201,48 @@ gxp.FilterBuilder = Ext.extend(Ext.Container, {
scope: this
});
}
+ if(this.allowCQL) {
+ bar.push(this.cqlPrefixText);
+ bar.push({
+ text: this.cqlText,
+ handler: this.switchToCQL,
+ scope: this
+ });
+ }
return bar;
},
-
+
+ /** private: method[switchToCQL]
+ * Switch from filter builder to CQL.
+ */
+ switchToCQL: function() {
+ var filter = this.getFilter();
+ var CQL = "";
+ if (filter !== false) {
+ CQL = this.cqlFormat.write(filter);
+ }
+ this.form.hide();
+ this.cqlField.setValue(CQL);
+ this.cqlForm.show();
+ },
+
+ /** private: method[switchToFilterBuilder]
+ * Switch from CQL field to filter builder.
+ */
+ switchToFilterBuilder: function() {
+ var filter = null;
+ // when parsing fails, we keep the previous filter in the filter builder
+ try {
+ filter = this.cqlFormat.read(this.cqlField.getValue());
+ } catch(e) {
+ }
+ this.cqlForm.hide();
+ this.form.show();
+ if (filter !== null) {
+ this.setFilter(filter);
+ }
+ },
+
/** api: method[getFilter]
* :return: ``OpenLayers.Filter``
*
diff --git a/src/script/widgets/LayerUploadPanel.js b/src/script/widgets/LayerUploadPanel.js
index 0aedc751..c697884c 100644
--- a/src/script/widgets/LayerUploadPanel.js
+++ b/src/script/widgets/LayerUploadPanel.js
@@ -361,13 +361,18 @@ gxp.LayerUploadPanel = Ext.extend(Ext.FormPanel, {
records, tasks, task, msg, i,
success = true;
if (obj) {
- tasks = obj.tasks || [obj.task];
- for (i=tasks.length-1; i>=0; --i) {
- task = tasks[i];
- if (task.state !== "READY") {
- success = false;
- msg = "Source " + task.source.file + " is " + task.state;
- break;
+ if (typeof obj === "string") {
+ success = false;
+ msg = obj;
+ } else {
+ tasks = obj.tasks || [obj.task];
+ for (i=tasks.length-1; i>=0; --i) {
+ task = tasks[i];
+ if (task.state !== "READY") {
+ success = false;
+ msg = "Source " + task.source.file + " is " + task.state;
+ break;
+ }
}
}
}
diff --git a/src/script/widgets/PointSymbolizer.js b/src/script/widgets/PointSymbolizer.js
index d103b685..817f684a 100644
--- a/src/script/widgets/PointSymbolizer.js
+++ b/src/script/widgets/PointSymbolizer.js
@@ -46,6 +46,13 @@ gxp.PointSymbolizer = Ext.extend(Ext.Panel, {
symbolText: "Symbol",
sizeText: "Size",
rotationText: "Rotation",
+
+ /** api: config[filter]
+ * ``Integer`` One of gxp.PointSymbolizer.GRAPHIC or
+ * gxp.PointSymbolizer.MARK. If specified, this dialog will only show
+ * those elements relevant to the filter.
+ */
+ filter: null,
/** api: config[pointGraphics]
* ``Array``
@@ -99,12 +106,14 @@ gxp.PointSymbolizer = Ext.extend(Ext.Panel, {
{display: this.graphicTriangleText, value: "triangle", mark: true},
{display: this.graphicStarText, value: "star", mark: true},
{display: this.graphicCrossText, value: "cross", mark: true},
- {display: this.graphicXText, value: "x", mark: true},
- {display: this.graphicExternalText}
+ {display: this.graphicXText, value: "x", mark: true}
];
+ if (this.filter !== gxp.PointSymbolizer.MARK) {
+ this.pointGraphics.push({display: this.graphicExternalText});
+ }
}
- this.external = !!this.symbolizer["externalGraphic"];
+ this.external = !!this.symbolizer["externalGraphic"] || this.filter === gxp.PointSymbolizer.GRAPHIC;
this.markPanel = new Ext.Panel({
border: false,
@@ -141,7 +150,7 @@ gxp.PointSymbolizer = Ext.extend(Ext.Panel, {
name: "url",
fieldLabel: this.urlText,
value: this.symbolizer["externalGraphic"],
- hidden: true,
+ hidden: (this.filter !== gxp.PointSymbolizer.GRAPHIC),
listeners: {
change: function(field, value) {
this.symbolizer["externalGraphic"] = value;
@@ -183,6 +192,7 @@ gxp.PointSymbolizer = Ext.extend(Ext.Panel, {
this.items = [{
xtype: "combo",
name: "mark",
+ hidden: (this.filter === gxp.PointSymbolizer.GRAPHIC),
fieldLabel: this.symbolText,
store: new Ext.data.JsonStore({
data: {root: this.pointGraphics},
@@ -213,11 +223,11 @@ gxp.PointSymbolizer = Ext.extend(Ext.Panel, {
if(value) {
this.urlField.hide();
// this to hide the container - otherwise the label remains
- this.urlField.getEl().up('.x-form-item').setDisplayed(false);
+ //this.urlField.getEl().up('.x-form-item').setDisplayed(false);
this.symbolizer["externalGraphic"] = value;
} else {
this.urlField.show();
- this.urlField.getEl().up('.x-form-item').setDisplayed(true);
+ //this.urlField.getEl().up('.x-form-item').setDisplayed(true);
}
if(!this.external) {
this.external = true;
@@ -295,5 +305,8 @@ gxp.PointSymbolizer = Ext.extend(Ext.Panel, {
});
+gxp.PointSymbolizer.GRAPHIC = 0;
+gxp.PointSymbolizer.MARK = 1;
+
/** api: xtype = gxp_pointsymbolizer */
Ext.reg('gxp_pointsymbolizer', gxp.PointSymbolizer);
diff --git a/src/script/widgets/PolygonSymbolizer.js b/src/script/widgets/PolygonSymbolizer.js
index 035e6256..c23b082d 100644
--- a/src/script/widgets/PolygonSymbolizer.js
+++ b/src/script/widgets/PolygonSymbolizer.js
@@ -74,5 +74,5 @@ gxp.PolygonSymbolizer = Ext.extend(Ext.Panel, {
});
-/** api: xtype = gxp_linesymbolizer */
+/** api: xtype = gxp_polygonsymbolizer */
Ext.reg('gxp_polygonsymbolizer', gxp.PolygonSymbolizer);
diff --git a/src/script/widgets/RulePanel.js b/src/script/widgets/RulePanel.js
index a602089e..8083e4c5 100644
--- a/src/script/widgets/RulePanel.js
+++ b/src/script/widgets/RulePanel.js
@@ -13,6 +13,7 @@
* @include widgets/LineSymbolizer.js
* @include widgets/PointSymbolizer.js
* @include widgets/FilterBuilder.js
+ * @include widgets/grid/SymbolizerGrid.js
*/
/** api: (define)
@@ -28,7 +29,7 @@ Ext.namespace("gxp");
* Create a panel for assembling SLD rules.
*/
gxp.RulePanel = Ext.extend(Ext.TabPanel, {
-
+
/** api: property[fonts]
* ``Array(String)`` List of fonts for the font combo. If not set,
* defaults to the list provided by the .
@@ -127,14 +128,14 @@ gxp.RulePanel = Ext.extend(Ext.TabPanel, {
modifyScaleTipContext: Ext.emptyFn,
/** i18n */
- labelFeaturesText: "Label Features",
- labelsText: "Labels",
- basicText: "Basic",
- advancedText: "Advanced",
+ ruleText: "Rule",
+ symbologyText: "Symbology",
limitByScaleText: "Limit by scale",
- limitByConditionText: "Limit by condition",
- symbolText: "Symbol",
- nameText: "Name",
+ limitByConditionText: "Limit with filters",
+ symbolText: "Preview",
+ nameText: "Label",
+ legendPropertiesText: "Legend properties",
+ propertiesSuffix: "Properties",
/** private */
initComponent: function() {
@@ -157,18 +158,6 @@ gxp.RulePanel = Ext.extend(Ext.TabPanel, {
this.activeTab = 0;
- this.textSymbolizer = new gxp.TextSymbolizer({
- symbolizer: this.getTextSymbolizer(),
- attributes: this.attributes,
- fonts: this.fonts,
- listeners: {
- change: function(symbolizer) {
- this.fireEvent("change", this, this.rule);
- },
- scope: this
- }
- });
-
/**
* The interpretation here is that scale values of zero are equivalent to
* no scale value. If someone thinks that a scale value of zero should have
@@ -196,6 +185,7 @@ gxp.RulePanel = Ext.extend(Ext.TabPanel, {
this.filterBuilder = new gxp.FilterBuilder({
allowGroups: this.nestedFilters,
+ allowCQL: true,
filter: this.rule && this.rule.filter && this.rule.filter.clone(),
attributes: this.attributes,
listeners: {
@@ -208,108 +198,12 @@ gxp.RulePanel = Ext.extend(Ext.TabPanel, {
}
});
- this.items = [{
- title: this.labelsText,
- autoScroll: true,
- bodyStyle: {"padding": "10px"},
- items: [{
- xtype: "fieldset",
- title: this.labelFeaturesText,
- autoHeight: true,
- checkboxToggle: true,
- collapsed: !this.hasTextSymbolizer(),
- items: [
- this.textSymbolizer
- ],
- listeners: {
- collapse: function() {
- OpenLayers.Util.removeItem(this.rule.symbolizers, this.getTextSymbolizer());
- this.fireEvent("change", this, this.rule);
- },
- expand: function() {
- this.setTextSymbolizer(this.textSymbolizer.symbolizer);
- this.fireEvent("change", this, this.rule);
- },
- scope: this
- }
- }]
- }];
if (this.getSymbolTypeFromRule(this.rule) || this.symbolType) {
- this.items = [{
- title: this.basicText,
- autoScroll: true,
- items: [this.createHeaderPanel(), this.createSymbolizerPanel()]
- }, this.items[0], {
- title: this.advancedText,
- defaults: {
- style: {
- margin: "7px"
- }
- },
- autoScroll: true,
- items: [{
- xtype: "fieldset",
- title: this.limitByScaleText,
- checkboxToggle: true,
- collapsed: !(this.rule && (this.rule.minScaleDenominator || this.rule.maxScaleDenominator)),
- autoHeight: true,
- items: [this.scaleLimitPanel],
- listeners: {
- collapse: function() {
- delete this.rule.minScaleDenominator;
- delete this.rule.maxScaleDenominator;
- this.fireEvent("change", this, this.rule);
- },
- expand: function() {
- /**
- * Start workaround for
- * http://projects.opengeo.org/suite/ticket/676
- */
- var tab = this.getActiveTab();
- this.activeTab = null;
- this.setActiveTab(tab);
- /**
- * End workaround for
- * http://projects.opengeo.org/suite/ticket/676
- */
- var changed = false;
- if (this.scaleLimitPanel.limitMinScaleDenominator) {
- this.rule.minScaleDenominator = this.scaleLimitPanel.minScaleDenominator;
- changed = true;
- }
- if (this.scaleLimitPanel.limitMaxScaleDenominator) {
- this.rule.maxScaleDenominator = this.scaleLimitPanel.maxScaleDenominator;
- changed = true;
- }
- if (changed) {
- this.fireEvent("change", this, this.rule);
- }
- },
- scope: this
- }
- }, {
- xtype: "fieldset",
- title: this.limitByConditionText,
- checkboxToggle: true,
- collapsed: !(this.rule && this.rule.filter),
- autoHeight: true,
- items: [this.filterBuilder],
- listeners: {
- collapse: function(){
- delete this.rule.filter;
- this.fireEvent("change", this, this.rule);
- },
- expand: function(){
- var changed = false;
- this.rule.filter = this.filterBuilder.getFilter();
- this.fireEvent("change", this, this.rule);
- },
- scope: this
- }
- }]
- }];
+ this.items = [
+ this.createRulePanel(),
+ this.createSymbolizerPanel()
+ ];
}
- this.items[0].autoHeight = true;
this.addEvents(
/** api: events[change]
@@ -332,51 +226,6 @@ gxp.RulePanel = Ext.extend(Ext.TabPanel, {
gxp.RulePanel.superclass.initComponent.call(this);
},
- /** private: method[hasTextSymbolizer]
- */
- hasTextSymbolizer: function() {
- var candidate, symbolizer;
- for (var i=0, ii=this.rule.symbolizers.length; i", {
- text: this.cancelText,
+ text: this.revertText,
iconCls: "cancel",
handler: function() {
this.saveRule(ruleDlg.rulePanel, origRule);
ruleDlg.destroy();
},
scope: this
- }, {
- text: this.saveText,
- iconCls: "save",
- handler: function() { ruleDlg.destroy(); }
}]
});
this.showDlg(ruleDlg);
diff --git a/src/script/widgets/grid/SymbolizerGrid.js b/src/script/widgets/grid/SymbolizerGrid.js
index 0ecbcd9a..be22b20e 100644
--- a/src/script/widgets/grid/SymbolizerGrid.js
+++ b/src/script/widgets/grid/SymbolizerGrid.js
@@ -30,7 +30,19 @@ gxp.grid.SymbolizerGrid = Ext.ux && Ext.ux.tree && Ext.ux.tree.TreeGrid && Ext.e
*/
symbolizers: null,
+ /** api: config[swatchSize]
+ * ``Array`` Width and height of the swatches / feature renderers.
+ * Defaults to 21 pixels.
+ */
+ swatchSize: [21, 21],
+
+ /** api: config[symbolType]
+ * ``String`` Main symbol type to use, e.g. Point or Polygon.
+ */
+ symbolType: null,
+
/** private overrides */
+ enableDD: true,
enableHdMenu: false,
enableSort: false,
useArrows: false,
@@ -45,19 +57,43 @@ gxp.grid.SymbolizerGrid = Ext.ux && Ext.ux.tree && Ext.ux.tree.TreeGrid && Ext.e
* Initializes the SymbolizerGrid.
*/
initComponent: function() {
+ this.dropConfig = Ext.apply(this.dropConfig || {}, {
+ isValidDropPoint : function(n, pt, dd, e, data){
+ return (pt !== 'append') && (n.node.parentNode === data.node.parentNode);
+ }
+ });
this.on('checkchange', this.onCheckChange, this);
+ this.on('movenode', this.onMoveNode, this);
this.loader = new gxp.tree.SymbolizerLoader({
- symbolizers: this.symbolizers
+ symbolizers: this.symbolizers,
+ symbolType: this.symbolType,
+ listeners: {
+ 'nodeadded': function(loader, node) {
+ this.createSwatches(node);
+ },
+ scope: this
+ }
});
this.columns = [{
header: this.typeTitle,
- dataIndex: 'type',
+ dataIndex: 'text',
width: 200
}, {
header: this.previewTitle,
- width: 100,
+ width: 85,
dataIndex: 'preview'
}];
+ this.addEvents(
+ /**
+ * Event: change
+ * Fires when the filter changes.
+ *
+ * Listener arguments:
+ * grid - {gxp.grid.SymbolizerGrid} This symbolizer grid. Call
+ * ``getSymbolizers`` to get the updated state.
+ */
+ "change"
+ );
gxp.grid.SymbolizerGrid.superclass.initComponent.call(this);
},
@@ -68,23 +104,61 @@ gxp.grid.SymbolizerGrid = Ext.ux && Ext.ux.tree && Ext.ux.tree.TreeGrid && Ext.e
* have any visible children will be filtered out.
*/
getSymbolizers: function() {
- var symbolizers = [];
+ var symbolizers = {};
+ var keys = [];
this.root.eachChild(function(n){
- var childVisible = false;
+ var type = n.attributes.type,
+ i, ii, j,
+ result = [];
+ keys.push(type);
n.eachChild(function(c) {
- var type = c.attributes.type.toLowerCase();
- if (type !== "label") {
- n.attributes.originalSymbolizer[type] = c.attributes.checked;
- }
- if (c.attributes.checked === true) {
- childVisible = true;
+ if (c.getUI().isChecked() === true) {
+ var subType = c.attributes.type;
+ var emptyFound = false, obj;
+ for (i=0, ii=result.length; i=0; --i) {
+ var key = keys[i];
+ if (symbolizers[key].length > 0) {
+ for (j=symbolizers[key].length-1; j>=0; --j) {
+ var s;
+ if (key === "Point") {
+ // every subType should create its own symbolizer
+ var tmp = [];
+ for (s in symbolizers[key][j]) {
+ tmp.push(new OpenLayers.Symbolizer[key](symbolizers[key][j][s]));
+ }
+ result = result.concat(tmp.reverse());
+ } else {
+ var config = {};
+ for (s in symbolizers[key][j]) {
+ if (s !== "Label") {
+ config[s.toLowerCase()] = true;
+ }
+ config = Ext.applyIf(config, symbolizers[key][j][s]);
+ }
+ result.push(new OpenLayers.Symbolizer[key](config));
+ }
+ }
+ }
+ }
+ return result;
},
/** private: method[beforeDestroy]
@@ -100,6 +174,61 @@ gxp.grid.SymbolizerGrid = Ext.ux && Ext.ux.tree && Ext.ux.tree.TreeGrid && Ext.e
gxp.grid.SymbolizerGrid.superclass.onDestroy.call(this);
},
+ /** api: method[updateSwatch]
+ * :arg node: ``Ext.data.Node``
+ * :arg newSymbolizer: ``Object``
+ */
+ updateSwatch: function(node, newSymbolizer) {
+ node.attributes.featureRenderer.drawFeature();
+ if (node.parentNode.attributes.featureRenderer) {
+ node.parentNode.attributes.featureRenderer.drawFeature();
+ } else {
+ node.cascade(function(c) {
+ c.attributes.featureRenderer.drawFeature();
+ });
+ }
+ if (newSymbolizer) {
+ this.fireEvent("change", this);
+ }
+ },
+
+ /** private: method[getNodeIndex]
+ * :arg node: ``Ext.data.Node``
+ * :returns: ``Integer`` The index
+ *
+ * Get the index of the node in the tree, ignore unchecked nodes.
+ */
+ getNodeIndex: function(node) {
+ var p = node.parentNode;
+ var idx = 0;
+ p.eachChild(function(c) {
+ if (c.getUI().isChecked() === true) {
+ if (node === c) {
+ return false;
+ }
+ idx++;
+ }
+ });
+ return idx;
+ },
+
+ /** private: method[onMoveNode]
+ *
+ * Handle the movenode event for drag and drop. Update the symbolizers
+ * and their swatches.
+ */
+ onMoveNode: function(tree, node, oldParent, newParent, index) {
+ var p = node.parentNode;
+ if (p !== this.getRootNode()) {
+ OpenLayers.Util.removeItem(p.attributes.symbolizer, node.attributes.symbolizer);
+ // we cannot use index directly since it takes into account unchecked nodes
+ var idx = this.getNodeIndex(node);
+ p.attributes.symbolizer.splice(idx, 0, node.attributes.symbolizer);
+ this.updateSwatch(node);
+ }
+ this.fireEvent("change", this);
+ },
+
/** private: method[onCheckChange]
* :arg node: ``Ext.data.Node``
* :arg checked: ``Boolean``
@@ -108,43 +237,17 @@ gxp.grid.SymbolizerGrid = Ext.ux && Ext.ux.tree && Ext.ux.tree.TreeGrid && Ext.e
* swatches.
*/
onCheckChange: function(node, checked) {
- var a = node.attributes;
- var r = a.featureRenderer;
- var type = a.type.toLowerCase();
- var symbolizer = a.symbolizer;
- var fullSymbolizer = node.parentNode.attributes.symbolizer;
- if (type !== "label") {
- // special handling for graphic, can only be turned on if label is on
- if (type === 'graphic') {
- var label = node.parentNode.findChild('type', 'Label');
- if (label !== null) {
- var labelChecked = label.attributes.checked;
- if ((labelChecked && checked) || !checked) {
- fullSymbolizer[type] = symbolizer[type] = checked;
- } else {
- node.getUI().toggleCheck(false);
- }
- }
- } else {
- fullSymbolizer[type] = symbolizer[type] = checked;
- }
+ var p = node.parentNode;
+ if (checked === false) {
+ node.attributes.featureRenderer.hide();
+ OpenLayers.Util.removeItem(p.attributes.symbolizer, node.attributes.symbolizer);
} else {
- if (!checked) {
- symbolizer[type] = fullSymbolizer[type] = "";
- var graphic = node.parentNode.findChild('type', 'Graphic');
- if (graphic !== null) {
- graphic.getUI().toggleCheck(false);
- }
- } else {
- symbolizer[type] = fullSymbolizer[type] = "Ab";
- }
- }
- if (node.parentNode.attributes.featureRenderer) {
- node.parentNode.attributes.featureRenderer.update({
- symbolizers: [fullSymbolizer]
- });
+ node.attributes.featureRenderer.show();
+ var idx = this.getNodeIndex(node);
+ p.attributes.symbolizer.splice(idx, 0, node.attributes.symbolizer);
}
- r.update({symbolizers: [symbolizer]});
+ this.updateSwatch(node);
+ this.fireEvent("change", this);
},
/** private: method[afterRender]
@@ -152,19 +255,32 @@ gxp.grid.SymbolizerGrid = Ext.ux && Ext.ux.tree && Ext.ux.tree.TreeGrid && Ext.e
*/
afterRender: function() {
gxp.grid.SymbolizerGrid.superclass.afterRender.call(this);
- this.root.cascade(function(node) {
- if (node.attributes.rendererId) {
- var ct = Ext.get(node.attributes.rendererId);
+ this.createSwatches(this.root);
+ },
+
+ /** private: method[createSwatches]
+ * :arg pNode: ``Ext.data.Node``
+ *
+ * Create the FeatureRenderer instances on a node and all its subnodes.
+ */
+ createSwatches: function(pNode) {
+ pNode.cascade(function(node) {
+ var attr = node.attributes;
+ if (attr.rendererId) {
+ var ct = Ext.get(attr.rendererId);
if (ct) {
- node.attributes.featureRenderer = new GeoExt.FeatureRenderer({
- symbolizers: [node.attributes.symbolizer],
- renderTo: ct,
- width:21,
- height: 21
+ attr.featureRenderer = new GeoExt.FeatureRenderer({
+ labelText: "Ab",
+ hidden: !node.getUI().isChecked(),
+ symbolizers: Ext.isArray(attr.symbolizer) ? attr.symbolizer : [attr.symbolizer],
+ symbolType: attr.symbolType,
+ renderTo: ct,
+ width: this.swatchSize[0],
+ height: this.swatchSize[1]
});
}
}
- });
+ }, this);
}
});
diff --git a/src/script/widgets/tree/SymbolizerLoader.js b/src/script/widgets/tree/SymbolizerLoader.js
index 69010d7b..0a68eaf8 100644
--- a/src/script/widgets/tree/SymbolizerLoader.js
+++ b/src/script/widgets/tree/SymbolizerLoader.js
@@ -19,6 +19,31 @@ Ext.ns("gxp.tree");
gxp.tree.SymbolizerLoader = function(config) {
Ext.apply(this, config);
+ this.addEvents(
+ /**
+ * @event beforeload
+ * Fires before loading the tree nodes.
+ * @param {Object} This TreeLoader object.
+ * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
+ * @param {Object} callback The callback function specified in the {@link #load} call.
+ */
+ "beforeload",
+ /**
+ * @event load
+ * Fires when the node has been successfuly loaded.
+ * @param {Object} This TreeLoader object.
+ * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
+ * @param {Object} response The response object containing the data from the server.
+ */
+ "load",
+ /**
+ * @event nodeadded
+ * Fires when a new node has been added after load.
+ * @param {Object} This TreeLoader object.
+ * @param {Object} node The {@link Ext.tree.TreeNode} object being added.
+ */
+ "nodeadded"
+ );
gxp.tree.SymbolizerLoader.superclass.constructor.call(this);
};
@@ -34,6 +59,16 @@ Ext.extend(gxp.tree.SymbolizerLoader, Ext.util.Observable, {
*/
symbolizers: null,
+ /** private: config[divTpl]
+ * ``Ext.Template`` The template used for the swatches.
+ */
+ divTpl: new Ext.Template(''),
+
+ /** private: config[emptyIconCls]
+ * ``String`` The css class to use on nodes without an icon in front.
+ */
+ emptyIconCls: "gxp-icon-symbolgrid-none",
+
/** private: method[load]
* :param node: ``Ext.tree.TreeNode`` The node to add children to.
* :param callback: ``Function``
@@ -43,85 +78,133 @@ Ext.extend(gxp.tree.SymbolizerLoader, Ext.util.Observable, {
while (node.firstChild) {
node.removeChild(node.firstChild);
}
- var divTpl = new Ext.Template('');
- for (var i=0, ii=this.symbolizers.length;i=0; --i) {
var symbolizer = this.symbolizers[i];
- var key = symbolizer.CLASS_NAME.substring(symbolizer.CLASS_NAME.lastIndexOf(".")+1);
- var fullSymbolizer = symbolizer.clone();
- if (key === 'Text') {
- fullSymbolizer.label = "Ab";
- if (fullSymbolizer.fillColor || fullSymbolizer.graphicName) {
- fullSymbolizer.graphic = true;
+ var className = symbolizer.CLASS_NAME;
+ var type = className.substr(className.lastIndexOf(".")+1);
+ if (OpenLayers.Util.indexOf(typeSeq, type) === -1) {
+ typeSeq.splice(0, 0, type);
+ }
+ split = this.splitSymbolizer(symbolizer);
+ for (s in split) {
+ symbolizers[type][s].push(split[s]);
+ }
+ }
+ var types = {
+ Polygon: ['Polygon', 'Line', 'Point', 'Text'],
+ Line: ['Line', 'Text'],
+ Point: ['Point', 'Text'],
+ Text: ['Text']
+ };
+ // check if we are missing symbolizers, if yes, create one
+ var typesNeeded = types[this.symbolType];
+ for (i=0, ii=typesNeeded.length; i=0; --i) {
+ var key = typeSeq[i];
+ if (symbolizers[key].empty === false) {
+ var id = Ext.id();
+ var text = key;
+ if (key !== 'Text') {
+ text += 's'; // plural
+ }
+ var child = this.createNode({
+ type: key,
+ text: text,
+ symbolizer: [],
+ symbolType: key,
+ expanded: true,
rendererId: id,
- preview: divTpl.applyTemplate({id: id})
- }));
+ iconCls: 'gxp-icon-symbolgrid-' + key.toLowerCase(),
+ preview: this.divTpl.applyTemplate({id: id})
+ });
+ for (var subKey in symbolizers[key]) {
+ for (var j=0, jj=symbolizers[key][subKey].length; j
-
-
-
-
-
-
-
+
+
+
+
+
+
@@ -27,17 +26,17 @@
strokeColor: "red",
strokeWidth: 1
}));
- symbolizers.push(new OpenLayers.Symbolizer.Line({
- strokeColor: "#669900",
- strokeWidth: 3
- }));
symbolizers.push(new OpenLayers.Symbolizer.Polygon({
fillColor: "olive",
- fillOpacity: 0.25,
strokeColor: "#666666",
strokeWidth: 2,
strokeDashstyle: "dot"
}));
+ symbolizers.push(new OpenLayers.Symbolizer.Polygon({
+ fillColor: "blue",
+ strokeColor: "black",
+ strokeWidth: 2
+ }));
symbolizers.push(new OpenLayers.Symbolizer.Text({
label: "${name}",
labelAlign: "cm",
@@ -52,15 +51,15 @@
function test_SymbolizerGrid(t) {
t.plan(1);
- var instance = new gxp.grid.SymbolizerGrid({symbolizers: createSymbolizers()});
+ var instance = new gxp.grid.SymbolizerGrid({symbolizers: createSymbolizers(), symbolType: 'Polygon'});
t.ok(instance instanceof gxp.grid.SymbolizerGrid, "Instance created successfully");
instance.destroy();
}
function test_getSymbolizers(t) {
- t.plan(2);
- var instance = new gxp.grid.SymbolizerGrid({renderTo: Ext.getBody(), symbolizers: createSymbolizers()});
+ t.plan(5);
+ var instance = new gxp.grid.SymbolizerGrid({renderTo: Ext.getBody(), symbolizers: createSymbolizers(), symbolType: 'Polygon'});
instance.render();
t.eq(instance.getSymbolizers().length, 4, "All symbolizers returned");
instance.root.eachChild(function(node) {
@@ -72,6 +71,11 @@
});
});
t.eq(instance.getSymbolizers().length, 3, "Point symbolizer excluded since all children are unchecked");
+ var poly = instance.root.findChild('type', 'Polygon');
+ t.eq(poly.childNodes.length, 6, "The two PolygonSymbolizers are combined in one tree node, and an empty symbolizer was added as well");
+ t.eq(poly.attributes.symbolizer[0].fillColor, "olive", "The olive fill is on top");
+ poly.findChild('checked', false).getUI().toggleCheck(true);
+ t.eq(poly.childNodes.length, 7, "Extra node inserted if last unchecked node gets checked");
instance.destroy();
}
diff --git a/tests/script/widgets/tree/SymbolizerLoader.html b/tests/script/widgets/tree/SymbolizerLoader.html
index 6052a00e..609c221a 100644
--- a/tests/script/widgets/tree/SymbolizerLoader.html
+++ b/tests/script/widgets/tree/SymbolizerLoader.html
@@ -6,13 +6,12 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
@@ -27,10 +26,6 @@
strokeColor: "red",
strokeWidth: 1
}));
- symbolizers.push(new OpenLayers.Symbolizer.Line({
- strokeColor: "#669900",
- strokeWidth: 3
- }));
symbolizers.push(new OpenLayers.Symbolizer.Polygon({
fillColor: "olive",
fillOpacity: 0.25,
@@ -59,8 +54,8 @@
}
function test_load(t) {
- t.plan(15);
- var loader = new gxp.tree.SymbolizerLoader({symbolizers: createSymbolizers()});
+ t.plan(19);
+ var loader = new gxp.tree.SymbolizerLoader({symbolType: "Polygon", symbolizers: createSymbolizers()});
var root = new Ext.tree.TreeNode();
var log = [];
loader.load(root, function() {
@@ -69,21 +64,25 @@
log.push(node);
}
});
- t.eq(log.length, 11, "We expect 11 nodes");
- t.eq(log[0].attributes.type, "Point", "Correct type");
- t.eq(log[1].attributes.type, "Stroke", "Correct subType created");
- t.eq(log[2].attributes.type, "Fill", "Correct subType created");
- t.eq(log[1].attributes.symbolizer.fill, false, "fill set to false for the stroke part of the the point symbolizer");
- t.eq(log[2].attributes.symbolizer.stroke, false, "stroke set to false for the fill part of the the point symbolizer");
- t.eq(log[3].attributes.type, "Line", "Correct type");
+ t.eq(log.length, 14, "We expect 14 nodes");
+ t.eq(log[0].attributes.type, "Text", "Correct type");
+ t.eq(log[1].text, "${name}", "Fieldname used in node text");
+ t.eq(log[1].attributes.type, "Label", "Correct subType created");
+ t.ok(log[2].attributes.type === "Label" && log[2].getUI().isChecked() === false, "Correct subType created, but unchecked");
+ t.eq(log[3].attributes.type, "Polygon", "Correct type");
+ t.eq(log[4].attributes.symbolizer.fill, false, "For Stroke, fill is false");
t.eq(log[4].attributes.type, "Stroke", "Correct subType");
- t.eq(log[5].attributes.type, "Polygon", "Correct type");
- t.eq(log[8].attributes.type, "Text", "Correct type");
- t.eq(log[9].attributes.type, "Label", "Correct subType");
- t.eq(log[9].attributes.symbolizer.graphic, false, "Graphic set to false");
- t.eq(log[9].attributes.symbolizer.label, "Ab", "Label is correct");
- t.eq(log[10].attributes.type, "Graphic", "Correct subType");
- t.eq(log[10].attributes.symbolizer.label, "", "Label is emptied for graphic subType");
+ t.ok(log[5].attributes.type === "Stroke" && log[5].getUI().isChecked() === false, "Correct subType created, but unchecked");
+ t.eq(log[6].attributes.type, "Fill", "Correct subType");
+ t.eq(log[6].attributes.symbolizer.stroke, false, "For Fill, stroke is false");
+ t.ok(log[7].attributes.type === "Fill" && log[7].getUI().isChecked() === false, "Correct subType created, but unchecked");
+ t.eq(log[8].attributes.type, "Point", "Correct type");
+ t.eq(log[9].attributes.type, "Graphic", "Correct subType");
+ t.eq(log[10].attributes.type, "Mark", "Correct subType");
+ t.ok(log[11].attributes.type === "Mark" && log[11].getUI().isChecked() === false, "Correct subType created, but unchecked");
+ t.eq(log[12].attributes.type, "Line", "Correct type");
+ t.eq(log[13].attributes.type, "Stroke", "Correct subType");
+ t.ok(log[13].getUI().isChecked() === false, "Node is unchecked");
});
}