Index: dojo-release-1.3.2-src/dijit/Tree.js
===================================================================
--- dojo-release-1.3.2-src.orig/dijit/Tree.js 2009-09-16 13:19:31.000000000 -0700
+++ dojo-release-1.3.2-src/dijit/Tree.js 2009-11-10 01:38:19.000000000 -0800
@@ -39,6 +39,11 @@
// This node is currently expanded (ie, opened)
isExpanded: false,
+ // isSelectable: Boolean
+ // If false, node will only toggle expanded when clicked and the onClick
+ // will not fire.
+ isSelectable: true,
+
// state: [private] String
// Dynamic loading-related stuff.
// When an empty folder node appears, it is "UNCHECKED" first,
@@ -267,6 +272,7 @@
item: item,
tree: tree,
isExpandable: model.mayHaveChildren(item),
+ isSelectable: tree.getSelectable(item),
label: tree.getLabel(item),
indent: this.indent + 1
});
@@ -294,19 +300,6 @@
// change expando to/from dot or + icon, as appropriate
this._setExpando(false);
}
-
- // On initial tree show, make the selected TreeNode as either the root node of the tree,
- // or the first child, if the root node is hidden
- if(this == tree.rootNode){
- var fc = this.tree.showRoot ? this : this.getChildren()[0];
- if(fc){
- fc.setSelected(true);
- tree.lastFocused = fc;
- }else{
- // fallback: no nodes in tree so focus on Tree
itself
- tree.domNode.setAttribute("tabIndex", "0");
- }
- }
},
removeChild: function(/* treeNode */ node){
@@ -340,7 +333,6 @@
// tags:
// private
dojo.addClass(this.labelNode, "dijitTreeLabelFocused");
- this.tree._onNodeFocus(this);
},
_onLabelBlur: function(evt){
@@ -355,6 +347,11 @@
},
setSelected: function(/*Boolean*/ selected){
+ dojo.deprecated("setSelected"+selected+"() is deprecated. Use attr('selected', "+selected+" instead.", "", "2.0");
+ this.attr('selected', selected);
+ },
+
+ _setSelectedAttr: function(/*Boolean*/ selected){
// summary:
// A Tree has a (single) currently selected node.
// Mark that this node is/isn't that currently selected node.
@@ -362,9 +359,12 @@
// In particular, setting a node as selected involves setting tabIndex
// so that when user tabs to the tree, focus will go to that node (only).
var labelNode = this.labelNode;
+
labelNode.setAttribute("tabIndex", selected ? "0" : "-1");
dijit.setWaiState(labelNode, "selected", selected);
dojo.toggleClass(this.rowNode, "dijitTreeNodeSelected", selected);
+
+ this.selected = selected;
},
_onMouseEnter: function(evt){
@@ -432,6 +432,10 @@
// If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
openOnDblClick: false,
+ // selectedItem: dojo.data.Item
+ // Optional initially selected item
+ selectedItem: null,
+
templatePath: dojo.moduleUrl("dijit", "templates/Tree.html"),
// isExpandable: [private deprecated] Boolean
@@ -540,17 +544,17 @@
this.cookieName = this.id + "SaveStateCookie";
}
+ // Create glue between store and Tree, if not specified directly by user
+ if(!this.model){
+ this._store2model();
+ }
+
// TODO: this.inherited(arguments)
},
postCreate: function(){
this._initState();
- // Create glue between store and Tree, if not specified directly by user
- if(!this.model){
- this._store2model();
- }
-
// monitor changes to items
this.connect(this.model, "onChange", "_onItemChange");
this.connect(this.model, "onChildrenChange", "_onItemChildrenChange");
@@ -614,6 +618,7 @@
item: item,
tree: this,
isExpandable: true,
+ isSelectable: this.getSelectable(item),
label: this.label || this.getLabel(item),
indent: this.showRoot ? 0 : -1
});
@@ -702,6 +707,87 @@
// extension
},
+ getSelectable: function(/*dojo.data.Item*/ item){
+ // summary:
+ // Overridable function to determine if the item's node is selectable.
+ // returns:
+ // Boolean
+ // tags:
+ // extension
+ return this.model.getSelectable(item);
+ },
+
+ _itemsEqual: function(/*dojo.data.Item[]*/){
+ // summary:
+ // Compares all argument's model identity against each other
+ // description:
+ // Returns true if all items are null or have the same identity
+ if(arguments.length < 2){
+ return false;
+ }
+ var items = Array.prototype.slice.call(arguments);
+ if(dojo.every(items, function(item){return item === null})){
+ return true;
+ }
+ if(dojo.some(items, function(item){return item === null})){
+ return false;
+ }
+ var firstIdentity = this.model.getIdentity(items.shift());
+ return dojo.every(items, function(item){
+ return this.model.getIdentity(item) == firstIdentity;
+ }, this);
+ },
+
+ _setSelectedItemAttr: function(/*dojo.data.Item*/ item){
+ // summary:
+ // Set the selected item.
+ // description:
+ // If the item's node widget is not instantiated yet it will do nothing.
+ // If item is null the current selection will be cleared.
+ if(this.attr("selectedItem") !== null
+ && this._itemsEqual(this.attr("selectedItem"), item)){
+ return;
+ }
+ if(this.attr("selectedNode") !== undefined
+ && !this._itemsEqual(this.attr("selectedItem"), item)){
+ this.attr("selectedNode").attr("selected", false);
+ }
+ if(item === null){
+ // deselect current selection
+ this.selectedItem = null;
+ return;
+ }
+ var node = this._item2node(item);
+ if(node === undefined){
+ // node not created yet, cannot select item. Will be selected when the
+ // parent node is selected.
+ }
+ else{
+ node.attr("selected", true);
+ this.focusNode(node);
+ this.selectedItem = item;
+ }
+ },
+
+ _getSelectedItemAttr: function(){
+ // summary:
+ // Get the selected item.
+ return this.selectedItem;
+ },
+
+ _setSelectedNodeAttr: function(node){
+ // summary:
+ // Set the selected item via a node.
+ this.attr("selectedItem", node.item);
+ },
+
+ _getSelectedNodeAttr: function(){
+ // summary:
+ // Get the selected node.
+ return this._item2node(this.attr("selectedItem"));
+ },
+
+
/////////// Keyboard and Mouse handlers ////////////////////
_onKeyPress: function(/*Event*/ e){
@@ -741,8 +827,9 @@
},
_onEnterKey: function(/*Object*/ message){
- this._publish("execute", { item: message.item, node: message.node} );
- this.onClick(message.item, message.node);
+ if(this._execute(message.item)){
+ this.onClick(message.item, message.node);
+ }
},
_onDownArrow: function(/*Object*/ message){
@@ -871,17 +958,16 @@
return;
}
- if( (this.openOnClick && nodeWidget.isExpandable) ||
+ if( (this.openOnClick && nodeWidget.isExpandable) || !nodeWidget.isSelectable ||
(domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText) ){
// expando node was clicked, or label of a folder node was clicked; open it
if(nodeWidget.isExpandable){
this._onExpandoClick({node:nodeWidget});
}
- }else{
- this._publish("execute", { item: nodeWidget.item, node: nodeWidget} );
- this.onClick(nodeWidget.item, nodeWidget);
- this.focusNode(nodeWidget);
+ }else if(this._execute(nodeWidget.item)){
+ this.onClick(nodeWidget.item, nodeWidget.node);
}
+ this.focusNode(nodeWidget);
dojo.stopEvent(e);
},
_onDblClick: function(/*Event*/ e){
@@ -901,11 +987,10 @@
if(nodeWidget.isExpandable){
this._onExpandoClick({node:nodeWidget});
}
- }else{
- this._publish("execute", { item: nodeWidget.item, node: nodeWidget} );
- this.onDblClick(nodeWidget.item, nodeWidget);
- this.focusNode(nodeWidget);
+ }else if(this._execute(nodeWidget.item)){
+ this.onDblClick(nodeWidget.item, nodeWidget.node);
}
+ this.focusNode(nodeWidget);
dojo.stopEvent(e);
},
@@ -1016,15 +1101,48 @@
case "UNCHECKED":
// need to load all the children, and then expand
node.markProcessing();
- var _this = this;
- model.getChildren(item, function(items){
+ model.getChildren(
+ item,
+ dojo.hitch(this, function(items){
node.unmarkProcessing();
node.setChildItems(items);
- _this._expandNode(node);
- },
- function(err){
- console.error(_this, ": error loading root children: ", err);
- });
+ this._expandNode(node);
+ if(node !== this.rootNode){
+ // Check to see if the selected item is a child of this newly expanded node.
+ if(dojo.some(items, function(i){return i === this.attr("selectedItem")}, this)){
+ this.attr("selectedNode").attr("selected", true);
+ }
+ }
+ else{
+ // On initial tree show, make the selected TreeNode as either:
+ // the node that matches this.selectedItem,
+ // or the root node of the tree,
+ // or the first child, if the root node is hidden
+ if(this.attr("selectedItem") !== null){
+ if(this.attr("selectedNode") === undefined){
+ // Default selection is set but cannot select it because the
+ // node is not created yet, most likely due to not being a
+ // direct child of the root.
+ return;
+ }
+ else{
+ this.attr("selectedNode").attr("selected", true);
+ return;
+ }
+ }
+ var fc = this.tree.showRoot ? node : node.getChildren()[0];
+ if(fc){
+ this.attr('selectedNode', fc);
+ return;
+ }
+ // fallback: no nodes in tree so focus on Tree
itself
+ this.domNode.setAttribute("tabIndex", "0");
+ }
+ }),
+ dojo.hitch(this, function(err){
+ console.error(this, ": error loading root children: ", err);
+ })
+ );
break;
default:
@@ -1051,26 +1169,6 @@
node.labelNode.focus();
},
- _onNodeFocus: function(/*Widget*/ node){
- // summary:
- // Called when a TreeNode gets focus, either by user clicking
- // it, or programatically by arrow key handling code.
- // description:
- // It marks that the current node is the selected one, and the previously
- // selected node no longer is.
-
- if (node){
- if(node != this.lastFocused){
- // mark that the previously selected node is no longer the selected one
- this.lastFocused.setSelected(false);
- }
-
- // mark that the new node is the currently selected one
- node.setSelected(true);
- this.lastFocused = node;
- }
- },
-
_onNodeMouseEnter: function(/*Widget*/ node){
// summary:
// Called when mouse is over a node (onmouseenter event)
@@ -1086,9 +1184,7 @@
_onItemChange: function(/*Item*/ item){
// summary:
// Processes notification of a change to an item's scalar values like label
- var model = this.model,
- identity = model.getIdentity(item),
- node = this._itemNodeMap[identity];
+ var node = this._item2node(item);
if(node){
node.setLabelNode(this.getLabel(item));
@@ -1099,9 +1195,7 @@
_onItemChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
// summary:
// Processes notification of a change to an item's children
- var model = this.model,
- identity = model.getIdentity(parent),
- parentNode = this._itemNodeMap[identity];
+ var parentNode = this._item2node(parent);
if(parentNode){
parentNode.setChildItems(newChildrenList);
@@ -1196,6 +1290,26 @@
// of just specifying a widget for the label, rather than one that contains
// the children too.
return new dijit._TreeNode(args);
+ },
+
+ _item2node: function(item){
+ // summary:
+ // Get the node that represents the item in the tree. Returns undefined
+ // if the item does not have a node created for it.
+ return item === null ? undefined : this._itemNodeMap[this.model.getIdentity(item)];
+ },
+
+ _execute: function(/*dojo.data.Item*/item){
+ // summary:
+ // This is what gets called when the 'execute' happens. Usually from
+ // clicking or pressing enter on a selectable item.
+ var node = this._item2node(item);
+ if(!node.isSelectable){
+ return false;
+ }
+ this.attr("selectedItem", item);
+ this._publish("execute", { item: item, node: node} );
+ return true;
}
});
Index: dojo-release-1.3.2-src/dijit/tree/TreeStoreModel.js
===================================================================
--- dojo-release-1.3.2-src.orig/dijit/tree/TreeStoreModel.js 2009-09-16 13:19:31.000000000 -0700
+++ dojo-release-1.3.2-src/dijit/tree/TreeStoreModel.js 2009-10-02 17:27:31.000000000 -0700
@@ -21,6 +21,10 @@
// If specified, get label for tree node from this attribute, rather
// than by calling store.getLabel()
labelAttr: "",
+
+ // selectableAttr: String
+ // Default attribute used to determine if items are selectable
+ selectableAttr: "selectable",
// root: [readonly] dojo.data.Item
// Pointer to the root item (read only, not a parameter)
@@ -157,6 +161,13 @@
}
},
+ getSelectable: function(/*dojo.data.Item*/ item){
+ // summary:
+ // Get the selectability of an item
+ var selectable = this.store.getValue(item,this.selectableAttr);
+ return selectable === undefined ? true : selectable; // Boolean
+ },
+
// =======================================================================
// Write interface
Index: dojo-release-1.3.2-src/dijit/tree/model.js
===================================================================
--- dojo-release-1.3.2-src.orig/dijit/tree/model.js 2009-09-16 13:19:31.000000000 -0700
+++ dojo-release-1.3.2-src/dijit/tree/model.js 2009-11-10 01:12:20.000000000 -0800
@@ -64,6 +64,13 @@
// tags:
// extension
},
+
+ getSelectable: function(/*dojo.data.Item*/ item){
+ // summary:
+ // Get the selectability of an item
+ // tags:
+ // extension
+ },
// =======================================================================
// Write interface