PDA

View Full Version : Multiselect Tree & Drag and Drop



Deadmeat
1 Mar 2008, 11:27 PM
Since I'm finally getting aroung to using this code, I've updated the code to be more user friendly (rather than just a basic proof of concept)

The newer code is a separate JS file, so it isn't functional on it's own, refer to the demo page for a working version.

Extra features:

Shift-click for range select
Ctrl-click for unselect
Shows subtree during drag
Multiple drag and drop
Vanilla single-click to drag current selection
A drop in replacement for the default tree component (mostly)


Live version at: http://www.users.on.net/~clear/ext/index.html



/*
* MultiSelectTreePanel v 1.0 (Initial release)
*
* This work is derivative of Ext-JS 2.2. Much of the code is modified versions of default code.
* Refer to Ext-JS 2.2 licencing for more information. http://extjs.com/license
*
* Any and all original code is made available as is for whatever purpose you see fit.
*
* Should be a largely drop in replacement for ordinary TreePanel when you require multiselect
* with drag and drop. Overrides most of the methods and events to pass a nodelist rather than
* a single node.
*
* Note that the code is provided as-is and should be considered experimental and likely to contain
* bugs, especially when combined with other extensions or modifications to the default library.
*
* It has been tested against Ext-JS 2.2 and 2.2.1 with:
*
* Firefox 3, Opera 9.5, Safari 3.1, MSIE 6 & 7.
*
* Usage:
*
* Add the following CSS to make the floating "drag" version of the tree indent prettily..

.x-dd-drag-ghost .x-tree-node-indent,.x-dd-drag-ghost .x-tree-ec-icon {display: inline !important;}

*
* If you are using Ext-JS 2.2.1 or earlier you need to add this override! (reported as a bug)

Ext.override(Ext.tree.TreeDropZone, {
completeDrop : function(de){
var ns = de.dropNode, p = de.point, t = de.target;
if(!Ext.isArray(ns)){
ns = [ns];
}
var n, node, ins = false;
if (p != 'append'){
ins = true;
node = (p == 'above') ? t : t.nextSibling;
}
for(var i = 0, len = ns.length; i < len; i++){
n = ns[i];
if (ins){
t.parentNode.insertBefore(n, node);
}else{
t.appendChild(n);
}
if(Ext.enableFx && this.tree.hlDrop){
n.ui.highlight();
}
}
ns[0].ui.focus();
t.ui.endDrop();
this.tree.fireEvent("nodedrop", de);
}

});

*
* Instantiate like a normal tree (except DD stuff is enabled by default)

var tree = new Ext.ux.MultiSelectTreePanel({
autoScroll:true,
width:400,
height:500,
animate:true,
containerScroll: true,
root: new Ext.tree.AsyncTreeNode({
text: 'A Book',
draggable:false,
id:'node0'
}),
loader: new Ext.tree.TreeLoader({
dataUrl:'bookdata.json'
})
});
tree.render("target");

*
* When listening for DND events look for dragdata.nodes instead of dragdata.node
*
* Use ctrl-click to select multiple nodes.
* Use shift-click to select a range of nodes.
*
* Enjoy
*/

Ext.ux.FixedMultiSelectionModel = Ext.extend(Ext.tree.MultiSelectionModel, {
// disabled tracking of mouse clicks because it doubles up drag selection...
onNodeClick : function(node, e){
if (e.shiftKey) e.preventDefault();
// this.select(node);
},

// private
sortSelNodes: function() {
if (this.selNodes.length > 0) {
if (!this.selNodes[0].ui.elNode)
// sort nodes into document order.. (taken from quirksmode)
if (this.selNodes[0].ui.elNode.sourceIndex) {
// IE source index method
this.selNodes.sort(function (a,b) {
return a.ui.elNode.sourceIndex - b.ui.elNode.sourceIndex;
});
} else if (this.selNodes[0].ui.elNode.compareDocumentPosition) {
// W3C DOM lvl 3 method (Gecko)
this.selNodes.sort(function (a,b) {
return 3 - (a.ui.elNode.compareDocumentPosition(b.ui.elNode) & 6);
});
}
}
},

// overwritten from MultiSelectionModel to fix unselecting...
select : function(node, e, keepExisting){
// Add in setting an array as selected... (for multi-selecting D&D nodes)
if(node instanceof Array){
for (var c=0;c<node.length;c++) {
this.selMap[node[c].id] = node[c];
this.selNodes.push(node[c]);
node[c].ui.onSelectedChange(true);
}
this.sortSelNodes();
this.fireEvent("selectionchange", this, this.selNodes, this.lastSelNode);
return node;
}
// Shift Select to select a range
// NOTE: Doesn't change lastSelNode
// EEK has to be a prettier way to do this
if (e && e.shiftKey && this.selNodes.length > 0) {
this.lastSelNode = this.lastSelNode || this.selNodes[0];
var before = false;
if (this.lastSelNode == node) {
// check dom node ordering (from ppk of quirksmode.org)
} else if (node.ui.elNode.sourceIndex) {
// IE source index method
before = (this.lastSelNode.ui.elNode.sourceIndex - node.ui.elNode.sourceIndex) > 0;
} else if (node.ui.elNode.compareDocumentPosition) {
// W3C DOM lvl 3 method (Gecko)
var rel = this.lastSelNode.ui.elNode.compareDocumentPosition(node.ui.elNode);
before = !!(rel & 2);
} else {
// Safari doesn't support compareDocumentPosition or sourceIndex
// from http://code.google.com/p/doctype/wiki/ArticleNodeCompareDocumentOrder

var range1 = document.createRange();
range1.selectNode(this.lastSelNode.ui.elNode);
range1.collapse(true);

var range2 = document.createRange();
range2.selectNode(node.ui.elNode);
range2.collapse(true);

before = range1.compareBoundaryPoints(Range.START_TO_END, range2) > 0;
}
this.clearSelections(true);
var cont = true;
var inside = false;
var parent = this.lastSelNode;
// ummm... yeah don't read this bit...
do {
for (var next=parent;next!=null;next=(before?next.previousSibling:next.nextSibling)) {
// hack to make cascade work the way I want it to
inside = inside || (before && (next == node || next.contains(node)));
if (next.isExpanded()) {
next.cascade(function(n) {
if (cont != inside) {
this.selNodes.push(n);
this.selMap[n.id] = n;
n.ui.onSelectedChange(true);
}
cont = (cont && n != node);
return true;
}, this);
} else {
this.selNodes.push(next);
this.selMap[next.id] = next;
next.ui.onSelectedChange(true);
cont = (next != node);
}
if (!cont) break;
}
if (!cont) break;
while ((parent = parent.parentNode) != null) {
if (before) {
this.selNodes.push(parent);
this.selMap[parent.id] = parent;
parent.ui.onSelectedChange(true);
}
cont = (cont && parent != node);
if (before && parent.previousSibling) {
parent = parent.previousSibling;
break;
}
if (!before && parent.nextSibling) {
parent = parent.nextSibling;
break;
}
}
if (!cont) break;
} while (parent != null);
if (!node.isSelected()) {
this.selNodes.push(node);
this.selMap[node.id] = node;
node.ui.onSelectedChange(true);
}
this.sortSelNodes();
this.fireEvent("selectionchange", this, this.selNodes, node);
e.preventDefault();
return node;
} else if(keepExisting !== true) {
this.clearSelections(true);
}
if(this.isSelected(node)) {
// handle deselect of node...
if (keepExisting === true) {
this.unselect(node);
if (this.lastSelNode === node) {
this.lastSelNode = this.selNodes[0];
}
return node;
}
this.lastSelNode = node;
return node;
}
// save a resort later on...
this.selNodes.push(node);
this.selMap[node.id] = node;
node.ui.onSelectedChange(true);
this.sortSelNodes();
this.lastSelNode = node;
this.fireEvent("selectionchange", this, this.selNodes, this.lastSelNode);
return node;
},
// returns selected nodes precluding children of other selected nodes...
// used for multi drag and drop...
getUniqueSelectedNodes: function() {
var ret = [];
for (var c=0;c<this.selNodes.length;c++) {
var parent = this.selNodes[c];
ret.push(parent);
// nodes are sorted(?) so skip over subsequent nodes inside this one..
while ((c+1)<this.selNodes.length && parent.contains(this.selNodes[c+1])) c++;
}
return ret;
}
});
/*
Enhanced to support dragging multiple nodes...

for extension refer to data.nodes instead of data.node

*/
Ext.ux.MultiSelectTreeDragZone = Ext.extend(Ext.tree.TreeDragZone, {
onBeforeDrag : function(data, e){
if (data.nodes && data.nodes.length > 0) {
for (var c=0;c<data.nodes.length;c++) {
n = data.nodes[c];
if (n.draggable === false || n.disabled) return false
}
return true;
}
return false;

},
// what a mess!!!
// fixed to handle multiSelectionModel, however the result is very hacky
getDragData : function(e) {
// use tree selection model..
var selModel = this.tree.getSelectionModel();
// get event target
var target = Ext.dd.Registry.getHandleFromEvent(e);
// if no target (die)
if (target == null) return;
if (target.node.isSelected() && e.ctrlKey) {
selModel.unselect(target.node);
return;
}
var selNodes = [];
if (!selModel.getSelectedNodes) {
// if not multiSelectionModel.. just use the target...
selNodes = [target.node];
} else {
// if target not selected select it...
if (!target.node.isSelected() || e.shiftKey) {
selModel.select(target.node, e, e.ctrlKey);
}
// get selected nodes - nested nodes...
selNodes = selModel.getUniqueSelectedNodes();
}
// if no nodes selected stop now...
if (!selNodes || selNodes.length < 1) return;
var dragData = { nodes: selNodes };
// create a container for the proxy...
var div = document.createElement('ul'); // create the multi element drag "ghost"
// add classes to keep is pretty...
div.className = 'x-tree-node-ct x-tree-lines';
// add actual dom nodes to div (instead of tree nodes)
//var height = 0;
for(var i = 0, len = selNodes.length; i < len; i++) {
// height += Ext.fly(selNodes[i].ui.elNode.parentNode).getHeight();
// add entire node to proxy
div.appendChild(selNodes[i].ui.elNode.parentNode.cloneNode(true));
// limit proxy height to around 150px (need setting for this really)
// removed because the height varies so much anyways...
//if (height>150 && (i+1)<selNodes.length) {
// var elipsis = document.createElement("div");
// elipsis.innerHTML = "<b>...</b>";
// div.appendChild(elipsis);
// break;
//}
}
// fix extra indenting by removing extra spacers
// should really modify UI rendering code to render a duplicate subtree but this is simpler...
// no idea if this really gets all nodes or not...
var nodes = Ext.query(".x-tree-node-el", div);
for (var c=0;c<nodes.length;c++) {
// remove highlighting...
Ext.fly(nodes[c]).removeClass(['x-tree-selected','x-tree-node-over']);
// start at 1 to leave in folder/user icon
var depth = 1;
// calculate indenting required in proxy
for (var node=nodes[c].parentNode.parentNode;node!=null && node.parentNode!=null;node=node.parentNode.parentNode) {
depth++;
}
var spacers = Ext.query("img", nodes[c]);
for (var r=0;r<spacers.length&&r<spacers.length-depth;r++) {
spacers[r].parentNode.removeChild(spacers[r]);
}
}
dragData.ddel = div;
return dragData;
},
// fix from TreeDragZone (references dragData.node instead of dragData.nodes)
onInitDrag : function(e){
var data = this.dragData;
this.tree.eventModel.disable();
this.proxy.update("");
this.proxy.ghost.dom.appendChild(data.ddel);
this.tree.fireEvent("startdrag", this.tree, data.nodes, e);
},
// Called from TreeDropZone (looks like hack for handling multiple tree nodes)
getTreeNode: function() {
return this.dragData.nodes;
},
// fix from TreeDragZone (refers to data.node instead of data.nodes)
// Don't know what this does, so leaving as first node.
getRepairXY : function(e, data){
return data.nodes[0].ui.getDDRepairXY();
},

// fix from TreeDragZone (refers to data.node instead of data.nodes)
onEndDrag : function(data, e){
this.tree.eventModel.enable.defer(100, this.tree.eventModel);
this.tree.fireEvent("enddrag", this.tree, data.nodes, e);
},

// fix from TreeDragZone (refers to dragData.node instead of dragData.nodes)
onValidDrop : function(dd, e, id){
this.tree.fireEvent("dragdrop", this.tree, this.dragData.nodes, dd, e);
this.hideProxy();
},

// fix for invalid Drop
beforeInvalidDrop : function(e, id){
// this scrolls the original position back into view
var sm = this.tree.getSelectionModel();
sm.clearSelections();
sm.select(this.dragData.nodes, e, true);
}

});

/*

MultiSelectTreeDropZone

Contains following fixups

- modified functions to handle multiple nodes in dd operation
isValidDropPoint
afterRepair
- modified getDropPoint such that isValidDropPoint can simulate leaf style below inserting.
Overriding isValidDropPoint affects getDropPoint affects onNodeOver and onNodeDrop

Refer to data.nodes instead of data.node for events..

*/
Ext.ux.MultiSelectTreeDropZone = Ext.extend(Ext.tree.TreeDropZone, {

// fix from TreeDropZone (referred to data.node instead of data.nodes)
isValidDropPoint : function(n, pt, dd, e, data){
if(!n || !data) { return false; }
var targetNode = n.node;
var dropNodes = data.nodes?data.nodes:[data.node];
// default drop rules
if(!(targetNode && targetNode.isTarget && pt)){
return false;
}
if(pt == "append" && targetNode.allowChildren === false){
return false;
}
if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
return false;
}
// don't allow dropping a treenode inside itself...
for (var c=0;c<dropNodes.length;c++) {
if(dropNodes[c] && (targetNode == dropNodes[c] || dropNodes[c].contains(targetNode))){
return false;
}
}
// reuse the object
var overEvent = this.dragOverData;
overEvent.tree = this.tree;
overEvent.target = targetNode;
overEvent.data = data;
overEvent.point = pt;
overEvent.source = dd;
overEvent.rawEvent = e;
overEvent.dropNode = dropNodes;
overEvent.cancel = false;
var result = this.tree.fireEvent("nodedragover", overEvent);
return overEvent.cancel === false && result !== false;
},

// override to allow insert "below" when leaf != true...
getDropPoint : function(e, n, dd, data){
var tn = n.node;
if(tn.isRoot){
return this.isValidDropPoint(n, "append", dd, e, data)? "append" : false;
}
var dragEl = n.ddel;
var t = Ext.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
var y = Ext.lib.Event.getPageY(e);
var noAppend = tn.allowChildren === false || tn.isLeaf() || !this.isValidDropPoint(n, "append", dd, e, data);
if(!this.appendOnly && tn.parentNode.allowChildren !== false){
var noBelow = false;
if(!this.allowParentInsert){
noBelow = tn.hasChildNodes() && tn.isExpanded();
}
var q = (b - t) / (noAppend ? 2 : 3);
if(y >= t && y < (t + q) && this.isValidDropPoint(n, "above", dd, e, data)){
return "above";
}else if(!noBelow && (noAppend || y >= b-q && y <= b) && this.isValidDropPoint(n, "below", dd, e, data)){
return "below";
}
}
return noAppend? false: "append";
},

// Override because it calls getDropPoint and isValidDropPoint
onNodeOver : function(n, dd, e, data){
var pt = this.getDropPoint(e, n, dd, data);
var node = n.node;


if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
this.queueExpand(node);
}else if(pt != "append"){
this.cancelExpand();
}

var returnCls = this.dropNotAllowed;
if(pt){
var el = n.ddel;
var cls;
if(pt == "above"){
returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
cls = "x-tree-drag-insert-above";
}else if(pt == "below"){
returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
cls = "x-tree-drag-insert-below";
}else{
returnCls = "x-tree-drop-ok-append";
cls = "x-tree-drag-append";
}
if(this.lastInsertClass != cls){
Ext.fly(el).replaceClass(this.lastInsertClass, cls);
this.lastInsertClass = cls;
}
}
return returnCls;
},

// Override because it calls getDropPoint and isValidDropPoint
onNodeDrop : function(n, dd, e, data){
var point = this.getDropPoint(e, n, dd, data);
var targetNode = n.node;
targetNode.ui.startDrop();
if(point === false) {
targetNode.ui.endDrop();
return false;
}

var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
var dropEvent = {
tree : this.tree,
target: targetNode,
data: data,
point: point,
source: dd,
rawEvent: e,
dropNode: dropNode,
cancel: !dropNode,
dropStatus: false
};
var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
targetNode.ui.endDrop();
return dropEvent.dropStatus;
}

targetNode = dropEvent.target;
if(point == "append" && !targetNode.isExpanded()){
targetNode.expand(false, null, function(){
this.completeDrop(dropEvent);
}.createDelegate(this));
}else{
this.completeDrop(dropEvent);
}
return true;
},

// fix from TreeDropZone (referred to data.node instead of data.nodes)
afterRepair : function(data){
if(data && Ext.enableFx){
for (var c=0;c<data.nodes.length;c++) {
data.nodes[c].ui.highlight();
}
}
this.hideProxy();
}

});

/*

MultiSelectTreePanel

sets up using FixedMultiSelectionModel
and initing with extended DragZone and DropZone by default

*/

Ext.ux.MultiSelectTreePanel = Ext.extend(Ext.tree.TreePanel, {
enableDD: true,

getSelectionModel : function(){
if(!this.selModel){
this.selModel = new Ext.ux.FixedMultiSelectionModel();
}
return this.selModel;
},

initEvents: function() {
this.dragZone = new Ext.ux.MultiSelectTreeDragZone(this, {
ddGroup: this.ddGroup || "TreeDD",
scroll: this.ddScroll
});
this.dropZone = new Ext.ux.MultiSelectTreeDropZone(this, this.dropConfig || {
ddGroup: this.ddGroup || "TreeDD",
appendOnly: this.ddAppendOnly === true
});
Ext.ux.MultiSelectTreePanel.superclass.initEvents.apply(this, arguments);

}
});

Ext.reg('multiselecttreepanel', Ext.ux.MultiSelectTreePanel);



Usage:

1. Add the following CSS to make the floating "drag" version of the tree indent prettily..



.x-dd-drag-ghost .x-tree-node-indent,.x-dd-drag-ghost .x-tree-ec-icon {display: inline !important;}


1a. If you are using Ext-JS 2.2.1 or earlier you need to add this override! (reported as a bug should be fixed in the next version)



Ext.override(Ext.tree.TreeDropZone, {
completeDrop : function(de) {
var ns = de.dropNode, p = de.point, t = de.target;
if(!Ext.isArray(ns)) {
ns = [ns];
}
var n, node, ins = false;
if (p != 'append') {
ins = true;
node = (p == 'above') ? t : t.nextSibling;
}
for(var i = 0, len = ns.length; i < len; i++) {
n = ns[i];
if (ins) {
t.parentNode.insertBefore(n, node);
} else {
t.appendChild(n);
}
if(Ext.enableFx && this.tree.hlDrop) {
n.ui.highlight();
}
}
ns[0].ui.focus();
t.ui.endDrop();
this.tree.fireEvent("nodedrop", de);
}
});

2. Instantiate like a normal tree (except DD stuff is enabled by default)



var tree = new Ext.ux.MultiSelectTreePanel({
autoScroll:true,
width:400,
height:500,
animate:true,
containerScroll: true,
ddGroup: 'tree1',
root: new Ext.tree.AsyncTreeNode({
text: 'A Book',
draggable:false,
id:'node0'
}),
loader: new Ext.tree.TreeLoader({
dataUrl:'bookdata.json'
})
});
tree.render("target");


Notes:

Don't set a custom Selection Model as only the default supports multiple drag and drop
Don't set a custom TreeDragZone as only the default supports multiple drag and drop
Don't set a custom TreeDropZone as only the default supports multiple drag and drop
enableDD config attr is basically ignored.
When reacting to DND events use dragdata.nodes (array) instead of dragdata.node


Instructions:

Shift-Click to range select (May not work properly on some platforms)
Ctrl-Click to multi select (or equivalent combination)


Recent Changes:

Cleaned code and added rough comments
Added an actual MultiSelectTreePanel object instead of hacking at a TreePanel
Example code includes override for a bug in node insertion order
Fixed range selection in safari (doesn't support sourceIndex or documentComparePosition())


Known Bugs & Limitations:

Vanilla single click on a selected node doesn't change the selection.
Expanding a selected node in a range, doesn't select the child nodes. (deliberate)
Standard tree events need modifying to access dragdata.nodes (array) instead of dragdata.node
Defaults to multiselect with enableDD set, rather than being configured (use the default tree if you need this)
Not fully tested when dropping to or from vanilla trees.


I hope this comes in handy. It's highly derivative of ExtJS-2.2. I assume a fully featured tree with all these features will land in the default dist at some point. This code still shows several gaps in my understanding of Ext tools and methods.

I will continue to improve the code as I use it for my actual projects. Comments, improvements and bug reports are welcome.

wm003
6 Mar 2008, 4:06 AM
Good start! Thanks for sharing.

The most odd thing i discovered within the first tests: I cannot drag a selected row again before i unselected and reselected it again...go ahead! ;)

Here's a simple JSON-Data for quick testing:
Save this into some file like "multisel-data.json" and change the line of deadmeat's example code above as follows



//dataUrl:'../JSON/findGroups.jsp'
dataUrl:'multisel-data.json'




[{
text:'Multiselection Example',
id:'0',
cls:'master-text',
iconCls:'text-folder',
children:[{
text:'Abstract rendering in TreeNodeUI',
leaf:true,
iconCls:'text'
},{
text:'Create TreeNodeUI with column knowledge',
leaf:true,
iconCls:'text'
},{
text:'Create TreePanel to render and lock headers',
leaf:true,
iconCls:'text'
},{
text:'Add CSS to make it look fly',
leaf:true,
iconCls:'text'
},{
text:'Test and make sure it works',
leaf:true,
iconCls:'text'
}]
}]

galdaka
6 Mar 2008, 4:25 AM
Hi,

Your example not work in local.

Any live example?

Thanks in advance,

wm003
6 Mar 2008, 4:29 AM
Hi,

Your example not work in local.



:-/mmh, it works very well local. Just create the separate JSON i posted above, change the codeline for the URL (what i also posted above) and , of course, change the paths to the ext-library on the top of the sample code. And be sure you use the latest Ext 2.0.2
It will work. Thats all i did some minutes ago.:)

galdaka
6 Mar 2008, 7:21 AM
IE6 and FF2. N error but the tree not load. View image.

wm003
6 Mar 2008, 10:32 AM
Have you saved the multisel-data.json file in the same directory as the example page?

You also need to run it through a local webserver and not directly via filesystem, because of the XHR-request that does not work outside a webserver

so try:

http://localhost/myexample.htm

instead of

c:\documents and settings\ext\blabla\myexample.htm

Deadmeat
7 Mar 2008, 3:55 AM
Good start! Thanks for sharing.

The most odd thing i discovered within the first tests: I cannot drag a selected row again before i unselected and reselected it again...go ahead! ;)



Whoops, that's a bug.
On around line 50 or so, in the select() function there is a line missing...



if(node instanceof Array){
for (var c=0;c<node.length;c++) {
this.selMap[node[c].id] = node[c];
this.selNodes.push(node[c]); /* ADD THIS LINE */
node[c].ui.onSelectedChange(true);
}
this.sortSelNodes();
this.fireEvent("selectionchange", this, this.selNodes, this.lastSelNode);
return node;
}


Thanks for the feedback.

Deadmeat
7 Mar 2008, 4:14 AM
I've uploaded a live example temporarily at least. I don't have proper hosting, so it's not the fastest website on the planet. The page is basically the same as the posted example with some basic css fixes.

http://www.users.on.net/~clear/ext/treetest.html

Thanks to wm003 for the json idea.

k2_4_u
7 May 2008, 3:50 AM
Very useful piece!
Works nicely for me in FF.

In IE7 it bombs. The event object is not getting passed to select(...) function and hence throws error "shiftkey is not defined" when dragging the selection. This is only during first time. Later selections work fine.

k2_4_u
7 May 2008, 9:45 PM
Also, Shift key selection fails to work in IE with the following error -
"a.ui.elNode.sourceIndex is undefined"

jcwatson11
5 Aug 2008, 4:32 PM
Hi Deadmeat,

This is a great start! Thanks for sharing! The select problem discussed above is because the caller doesn't always pass an event to the select() function. Event e and and Boolean keepExisting are optional parameters, which means your select() implementation should account for that by not assuming they are present.

Again, great start, and I hope you are able to bring it to completion.

Jon

fxlacroix
12 Sep 2008, 5:03 AM
on the FixedMultiSelectionModel,
go around line 58,

change
before = (this.lastSelNode.ui.elNode.sourceIndex - node.ui.elNode.sourceIndex) < 0;

TO
before = (this.lastSelNode.ui.elNode.sourceIndex - node.ui.elNode.sourceIndex) > 0;

This explain why selection was inversed

+++

vladnech
10 Nov 2008, 11:19 PM
Hi all

Thanks for the good point to fix the MultiSelect TreeView, but I have the problem with select event - when there is a parameter 'e' is underfined. I tried to fix it, by checking if(e), but multi-selection does not work. Can anyone give me a clue, how to fix this control?
Thanks in advance for your help.

Deadmeat
12 Nov 2008, 2:11 AM
Sorry for the inactivity on my part.

I've updated the live version at http://www.users.on.net/~clear/ext/treetest.html with fixes for the inverted multiselect in IE and added a check for the missing event var in the select code.

It seems to work okayish for me in IE6-8b. Selection and highlighting seems to be messed up post drag, but that's not a huge issue. The reason for so many IE bugs is that I have to run a virtual machine to test in IE. I hate IE.

I'll be getting back to this code soonish, the project I wrote it for has been de-prioritised. However I still want something like this. Though I'll probably tweak the multiselect to be limited to a single parent folder rather than trying to span across folders like it does currently.

Thanks for the feedback and bug reports and sorry for being so slack updating it. Please post more bugs.

vladnech
12 Nov 2008, 2:05 PM
Thanks, mate.
It really helped me. I checked it for the IE6 and IE7 - works!:D
I have to adjust it in order to prevent Drag&Drop parent nodes.

vladnech
12 Nov 2008, 10:33 PM
Hi again

Is it possible to hide the node in the source tree during/after the Drag&Drop instead of dropping/deleting?
Thanks in advance for quick reply

Thomas_K
13 Nov 2008, 12:47 AM
I'm working too on something like this, i think it shuld highlight the subtrees too when you hit a folder. I will past this code part soon. :)

Deadmeat
13 Nov 2008, 10:01 PM
Hi again

Is it possible to hide the node in the source tree during/after the Drag&Drop instead of dropping/deleting?
Thanks in advance for quick reply

It is possible. From memory the source nodes don't actually move until the drop is complete. The nodes being "dragged" are just a graphical clone. Only after a successful drop are the nodes moved (so they are not copied, and then later deleted)

I'm not sure if you could just style the nodes with "display: none" or if would have to remove them entirely and temporarily store them (and their data) in a whole other tree. Technically you could make the graphical clone an actual tree I guess, I'm not sure if that's a good idea or not.

Deadmeat
13 Nov 2008, 10:07 PM
I'm working too on something like this, i think it shuld highlight the subtrees too when you hit a folder. I will past this code part soon. :)

Should be pretty easy to do. There is a bit in the middle where it cascades down the tree selecting nodes. All you would need to do it modify that slightly.

I did it this way for consistency sake. I was thinking my final version would only allow multiple/range selects to occur amongst siblings, which would do this too. However I'm still undecided, there are merits to both approaches.

Deadmeat
3 Feb 2009, 1:13 AM
I noticed that when working with a largish tree that was very deep that selection performance was abysmal. So I tweaked the select function not to recurse down unexpanded branches (since childnodes get filtered for drag drop anyways)

This was a significant performance improvement in a somewhat common unlikely case. It won't fix every case though.

Deadmeat
3 Feb 2009, 1:22 AM
Just noticed that the node ordering seems wonky in Safari. :((

You can multiselect downwards easily enough, just not upwards.

Deadmeat
3 Feb 2009, 7:26 PM
There is a bug in Ext-2.2 regarding the insertion of nodes.

in the TreeDropZone under completeDrop if you insert where the insertion point is "below" the target it will insert nodes in reverse order.

Of course this only shows in an unsorted tree where you are dropping more than one node.

This is the code to workaround the issue:



Ext.override(Ext.tree.TreeDropZone, {
completeDrop : function(de){
var ns = de.dropNode, p = de.point, t = de.target;
if(!Ext.isArray(ns)){
ns = [ns];
}
var n, before = (p != 'append') ? (p == 'above' ? t : t.nextSibling) : null;
for(var i = 0, len = ns.length; i < len; i++){
n = ns[i];
if (before){
t.parentNode.insertBefore(n, before);
}else{
t.appendChild(n);
}
}
n.ui.focus();
if(Ext.enableFx && this.tree.hlDrop){
n.ui.highlight();
}
t.ui.endDrop();
this.tree.fireEvent("nodedrop", de);
}
});


I'm not adding it to the sample page or the first post because that tree is appendOnly and sorted which makes the point moot. I guess this code is really only if you use this code with a shared ext and don't want to edit the source to overwrite the completeDrop function in Ext.ux.MultiSelectTreeDropZone.

I guess it'll be fixed in the next version of Ext.

favac
5 Feb 2009, 10:42 AM
thanks!

for all these posts and updates, all are really usefull!

Deadmeat
18 Feb 2009, 11:02 PM
I've updated the original message, the demo page and of course the code.

Just mentioning that it contains a bunch of fixes, including one for range selecting upwards in Safari (it doesn't have compareDocumentPosition or sourceIndex, but I found a workaround on googlecode)

flinz
2 Mar 2009, 7:00 AM
hey deadmeat,
i'm implementing your plugin with a slightly extended treepanel (showing columns) roughly the same as in the example for NodeUi (http://extjs.com/deploy/dev/examples/tree/column-tree.html).

it seems selection with your method is working fine as long as one clicks on the main text entry (the left most column), but no selection is happening when clicking on any of the other columns (like clicking on any minute entry at the example). to recreate this basically take the ColumnTree example and extend it with your extension.

I understand you override the multiselectionmodel with your own method (i didnt go too deep into it), and if i not use your extension the selection works fine. still i'd love to keep your multiselect d&d, since IMHO its plainly missing in ext.

Any idea what could cause this? Want me to post some code?

Cheers, alex

Deadmeat
2 Mar 2009, 11:06 PM
If you could post some code that would be good, although I could probably just experiment by making my own.

If I had to guess I'd say that since I removed the "click" event and used "mousedown" for everything (default tree uses mousedown for drag selection and click for selection model selection) and the column model maybe uses click rather than mousedown.

My code is based mostly of Ext-2.0 so there may have been changes which I haven't included.

Have you tried changing the ColumnNodeUI example to extend my tree? I probably would have done it that way around.

I have been meaning to go back and make my code more compatible, and make examples like the default that use my modified tree. I can probably look at doing that.

Deadmeat
3 Mar 2009, 2:57 AM
Okay the problem is not specific to my code. If you enable DD on the default example as is, then you have the same problem.

It appears to be related to how elements are registered for dragging.

My code works because handleMouseDown calls getDragData() in the DragZone. This is getting called for every click, however..

Ext.dd.Registry.getHandleFromEvent(e);

is returning null unless you are over either the icon or the link.

so... In ColumnNodeUI.js (or equiv) you need override getDDHandles() and return a full list of nodes that are drag handles. As far as I can tell, this needs to include whatever is the target element being clicked on, because it doesn't bubble up. So the subdiv of each column needs to be added.

So add this to your equivalent of ColumnNodeUI.js (or alter depending on your html)



getDDHandles: function() {
var ret = [this.iconNode, this.textNode, this.elNode.firstChild, this.elNode];
for (var col=this.elNode.firstChild.nextSibling;col;col = col.nextSibling) {
if (col.firstChild) ret.push(col.firstChild);
}
return ret;
}


I hacked together an example at:

http://www.users.on.net/~clear/ext/column-tree.html

Hope this helps...

flinz
3 Mar 2009, 3:06 AM
You man, are the man, man.
Works fine now, as far as i can tell!
Thanks a lot!

Deadmeat
3 Mar 2009, 5:52 AM
Since I was looking at code I figured I'd update the demo page (http://www.users.on.net/~clear/ext/).

There are more examples (two trees, and tree column) with notes.

I've added a coming soon and notes about two coming soon features (allowContainerDrop and Copying Nodes)

Unfortunately I'm working on another (non Ext) project now, so these updates will be spread out over a couple of months. I'll also need to do an update for ExtJS-3.0.

:s

I'll still read any comments, especially bugs and fixes and respond if I can.

Deadmeat
7 Mar 2009, 6:01 AM
So I totally broke all the examples in IE when I was cleaning up my webpage. (it was the node name equivalent to id) issue. :(( It was spewing random errors related to inserting adjacent html. It seems to work now.

I've fixed it and updated to v1.1 to fix issues with custom TreeNodeUI, lingering selection issues, out of date key events. Some of the really bad code has been removed and default code for allowContainerDrop code was included.

Significantly the tree requires Drag and Drop to be explicitly enabled. You can do this with the same config attributes, enabledDD, enableDrag and enableDrop.

I've also changed my page to use a patched version of ExtJS to demonstrate allowContainerDrop as it would work.

aj3423
20 Apr 2009, 7:39 AM
It seems that a bug exists in IE6

I tried the example at http://www.users.on.net/~clear/ext/index.html

see the width of the dragging nodes

Deadmeat
21 Apr 2009, 3:38 AM
It seems that a bug exists in IE6

I tried the example at http://www.users.on.net/~clear/ext/index.html

see the width of the dragging nodes


IE6 is one big bug.

I honestly have no idea what the cause is. It doesn't do it in ie7. I assume it's because I use a ul inside the drag proxy instead of a div like the default. There's probably some ie6 hack to keep the width down, or some class I'm missing.

Unfortunately it's a really difficult one to track down. I'll have to look at it later.

Thanks for the report though. I'll add it to my todo list.

triptych1
23 Apr 2009, 1:59 PM
Anyone try and successfully get this working with a TreeEditor?

I'm struggling.

Deadmeat
23 Apr 2009, 11:53 PM
Anyone try and successfully get this working with a TreeEditor?

I'm struggling.

I haven't. Is there a specific issue. The changes made shouldn't affect how TreeEditor works. A quick glance at the TreeEditor source indicates it should work identically for a simple tree.

I made a very minimal quick test at: http://www.users.on.net/~clear/ext/treeeditortest.html and it seems to work at least on a superficial level. (in Opera at least)

triptych1
24 Apr 2009, 4:17 AM
As you can see from your example, the editor enters on a single click with no delay. It ignores the Delay property on the TreeEditor, and I'm trying to remove that behavior and set it up to only edit on a double click; which is the default behavior for a TreePanel with a TreeEditor.

I'm not sure why exactly, I haven't dug that far down into it yet. I was hoping someone else had tried it. The main thing is overriding the beforenodeclick event, and when I do that I mess up your extension.


new Ext.tree.TreeEditor(yourTree, field, {
beforeNodeClick: Ext.emptyFn
});

Deadmeat
25 Apr 2009, 12:41 AM
As you can see from your example, the editor enters on a single click with no delay. It ignores the Delay property on the TreeEditor, and I'm trying to remove that behavior and set it up to only edit on a double click; which is the default behavior for a TreePanel with a TreeEditor.

I'm not sure why exactly, I haven't dug that far down into it yet. I was hoping someone else had tried it. The main thing is overriding the beforenodeclick event, and when I do that I mess up your extension.


new Ext.tree.TreeEditor(yourTree, field, {
beforeNodeClick: Ext.emptyFn
});

you can try something like this:



new Ext.tree.TreeEditor(tree, { },
{
cancelOnEsc: true,
beforeNodeClick: Ext.emptyFn,
onNodeDblClick : function(node, e){
return this.triggerEdit(node);
}
});


but it's not ideal...

My selection model uses mousedown to trigger to select. This is because the drag operation kicks into gear on mousedown. The default implementation of tree node dragging also runs on mousedown, but selection runs on click. So it fudges the drag target in mousedown. It uses mousedown to select the target and build a drag object. Later the click event fires and selects the node. Mine moves the selection code into mousedown to handle the complexities of move+select in the one (messy) process.

This means that when the click event is fired (after mousedown), the node is already selected, which the TreeEditor interprets as a trigger to start editing. It normally relies on intercepting the click before the selection model does and checking if it's selected already, which by default it isn't, because it's fired before selection is processed.

TreeEditor merely picks up clicks on already selected nodes, regardless of the delay. In fact a true dblclick doesn't trigger edit mode by default either. It stops edit mode starting up.

So far this is the first major incompatibility that has shown up. Other components may use a similar mechanism though.

So the above works. It's just not ideal because it messes up how TreeEditor works by default. It really does require a dblclick event now, which is probably used elsewhere. I don't really have time to nut out a fix right now, but I'll probably have to override beforeNodeClick to check something else besides just node selection.

Suggestions are welcome.

Ciary
3 Jun 2009, 3:40 AM
hey deadmeat,

first of all. thank you for this code. it's great and it helped me a lot. i don't really use it for multiselection though. but i needed a way to change selection before drag.

although, when implementing, i noticed a small problem. you probably implemented it somewhere but i couldnt find it. how can i get the path of the selected node(s).

meaning:
in a normal ext.tree.treePanel you could do this:

var path = myTree.getSelectionModel().getSelectedNode().getPath("text");
but how can this be done with a multiselection tree (it's definitaly not the same. i checked:) )

grtz

ciary

broutard
3 Jun 2009, 12:54 PM
Thanks for this code !!

I have a problem yet :

I want to remove all selected nodes.



var nodes = this.getSelectionModel().getSelectedNodes();
Ext.each(nodes, function(node){
node.remove();
});


But, after each remove(), nodes array length is decremented... and all nodes are not removed.

Is any way to do that ?


Best regards

Deadmeat
4 Jun 2009, 1:09 AM
hey deadmeat,

first of all. thank you for this code. it's great and it helped me a lot. i don't really use it for multiselection though. but i needed a way to change selection before drag.

although, when implementing, i noticed a small problem. you probably implemented it somewhere but i couldnt find it. how can i get the path of the selected node(s).

meaning:
in a normal ext.tree.treePanel you could do this:

var path = myTree.getSelectionModel().getSelectedNode().getPath("text");
but how can this be done with a multiselection tree (it's definitaly not the same. i checked:) )

grtz

ciary

Hey ciary,

I'm glad its useful. Wish I had more time to work with/on it.

MultiSelection (both standard and my variant) uses getSelectedNodes() which returns an array instead of getSelectedNode().

If you know there will only be one selected node then you could probably do:


var path = myTree.getSelectionModel().getSelectedNodes()[0].getPath("text");


or some variation:


var ret = [];
var arr = myTree.getSelectionModel().getSelectedNodes();
for (var c=0,len=arr.length;c<len;c++) {
ret.push(arr[c].getPath("text"));
}


Hope this helps :)

Deadmeat
4 Jun 2009, 1:27 AM
Thanks for this code !!

I have a problem yet :

I want to remove all selected nodes.



var nodes = this.getSelectionModel().getSelectedNodes();
Ext.each(nodes, function(node){
node.remove();
});


But, after each remove(), nodes array length is decremented... and all nodes are not removed.

Is any way to do that ?


Best regards

Hey broutard,

I think you need to use getUniqueSelectedNodes()



var nodes = this.getSelectionModel().getUniqueSelectedNodes();
Ext.each(nodes, function(node){
node.remove();
});


I did have an example of this but I seem to have changed it. So I can't test it right now.

getUniqueSelectedNodes() returns the subset of selected nodes that still encompasses all the selected nodes. IE. if a node and descendent are both selected, the descendent won't be in the list.

So what could be happening is that you might be removing a node which has already been removed (courtesy of an ancestor being removed).

If you specifically need to remove each selected node (for event reasons) you could try iterating over the list of all selected nodes in reverse order (by default the selected nodes should be listed in tree/document order.) This would theoretically remove child nodes before their parent nodes were removed.

If this doesn't fix the problem, just reply and I'll take a closer look.

Ciary
4 Jun 2009, 1:36 AM
you're either an angel, god, or a wonderful dream.
are you for real? ;)

thank you so much :) really, you saved my life

i've been searching for a way to drag nodes to a non-tree element for two weeks. it seems like nobody knew how it could be done, but now it works.

since i cant actually drag the node, i added a dragzone to the tree. it drags the whole tree but only shows the dragged nodes. when i drop, the script will ask the path of the first selected node and add it to the inner html of the droptarget. the only problem i had was that nodes aren't selected when dragged. but now that problem is solved.

so really, thank you very much

grtz

ciary

Deadmeat
4 Jun 2009, 8:18 PM
i've been searching for a way to drag nodes to a non-tree element for two weeks. it seems like nobody knew how it could be done, but now it works.

since i cant actually drag the node, i added a dragzone to the tree. it drags the whole tree but only shows the dragged nodes. when i drop, the script will ask the path of the first selected node and add it to the inner html of the droptarget. the only problem i had was that nodes aren't selected when dragged. but now that problem is solved.


Okay I'm glad I was able to help, however there is an example of what you want here: http://examples.extjs.eu/?ex=tree2divdrag

Saki is a genius (jsakalos?? on the forums). He has heaps of extensions and examples.

The main trick is configuring the DropTarget with the right ddGroup. Then you just need code to handle the drop action.

Antjac
5 Jun 2009, 1:33 AM
Hi,

Thanks for your component.
I've a little question, I want to make a drag and drop between 2 treepanels. But when i copy the leaf of the first treepanel to the second, i want that the tree diagram of this leaf will be show to the second treepanel.

Do you know how can i do it ?

Thx

Deadmeat
5 Jun 2009, 4:26 AM
Hi,

Thanks for your component.
I've a little question, I want to make a drag and drop between 2 treepanels. But when i copy the leaf of the first treepanel to the second, i want that the tree diagram of this leaf will be show to the second treepanel.

Do you know how can i do it ?

Thx

Hey Antjac

I'm not quite sure what you mean by tree diagram.

If you are looking to change what is being dropped, you can do this with a beforenodedrop listener. By default the node will of course be moved, but in beforenodedrop listener you can always make entirely new TreeNodes. You'll just have to make sure you perform a deep copy of all the nodes.

Something vaguely like this (I think):




// basic tree node duplicator..
function duplicateNode(node, deep) {
var ret = new Ext.tree.TreeNode(Ext.apply({}, node.attributes, { expanded: node.isExpanded() }));
if (deep && node.hasChildNodes()) {
for (var c=0,len=nodes.length;c<len;c++) {
if (node.childNodes[c]) {
ret.appendChild(this.duplicateNode(node.childNodes[c], true));
}
}
}
return ret;
};

// manipulate event.dropNode before actual drop..
tree.on("beforenodedrop", function(e) {
var nodes = e.dropNode || e.data.nodes || [e.data.node];
if (!Ext.isArray(nodes)) nodes = [nodes];
tmpNodes = [];
for (var c=0;c<nodes.length;c++) {
tmpNodes.push(duplicateNode(nodes[c], true));
}
e.dropNode = tmpNodes;
}



One of the features I plan to (eventually) put in is the ability to set a variable/callback for controlling if a drop operation should copy a node instead of moving it. I just never get time. :(

Janos
9 Jul 2009, 3:40 AM
Hey Deadmeat!

Do you any plans to update your plugin to support ExtJS 3.0 as well?

Deadmeat
9 Jul 2009, 8:59 PM
Hey Deadmeat!

Do you any plans to update your plugin to support ExtJS 3.0 as well?

I did some basic testing with the RC releases and it seemed to work as is. The tree component hadn't seen many updates. A few notable caveats.

1) You don't need to override the complete drop function anymore. Multiple nodes are inserted in the right order by default.

2) allowContainerDrop should now work.

I was waiting until the final release version, but I haven't had time to look at it recently. I'll be getting back to it soon. I have a some bugs to fix.

If you come across specific issues, please post and I'll fix them asap.

Cheers.

Janos
10 Jul 2009, 12:19 AM
Thanks for your quick answer! I'm writing an application right now in which multiselect trees with drag & drop support will play quite an important role, so I will test the component thoroughly with Ext 3.0 in the coming weeks. I will get back to you with my results!

robpi
16 Sep 2009, 3:31 AM
It seems that a bug exists in IE6

I tried the example at http://www.users.on.net/~clear/ext/index.html

see the width of the dragging nodes

In my application i've the same issue with ie7 and ie8 (didn't test ie6). But it's working on your site. How did you solve it?

Deadmeat
16 Sep 2009, 9:54 PM
In my application i've the same issue with ie7 and ie8 (didn't test ie6). But it's working on your site. How did you solve it?

Sounds like it could be a quirksmode/standardsmode issue. Do you have a doctype set.

My demo page is standardsmode (which is meaningless in IE6 and hence the problem) I've haven't had time to work out what I need to do to get it working properly in quirksmode (or to fix the bugs with it)

Hope this helps.

robpi
17 Sep 2009, 9:32 AM
Sounds like it could be a quirksmode/standardsmode issue. Do you have a doctype set.

My demo page is standardsmode (which is meaningless in IE6 and hence the problem) I've haven't had time to work out what I need to do to get it working properly in quirksmode (or to fix the bugs with it)

Hope this helps.

I found out that it can be solved by changing the doctype setting, but this isn't suitable for me.

So i searched for another solution and found it in adding some css.
.x-dd-drag-proxy {
width:100px;
}

This works pefekt in ie6, ie7 and ie8 (and of course firefox)!

By the way, you have done great work =D>. The only thing i don't like is, that a multiselection can not be deselected by clicking on a selected node. It can only be deselected by a click on a different node. What was your motivation for this solution?

Deadmeat
18 Sep 2009, 3:28 AM
I found out that it can be solved by changing the doctype setting, but this isn't suitable for me.

So i searched for another solution and found it in adding some css.
.x-dd-drag-proxy {
width:100px;
}

This works pefekt in ie6, ie7 and ie8 (and of course firefox)!


This is a sensible trade off. I must dig deeper and work out how the default gets around this problem. The transient instance is so hard to debug in Firebug though.



By the way, you have done great work =D>. The only thing i don't like is, that a multiselection can not be deselected by clicking on a selected node. It can only be deselected by a click on a different node. What was your motivation for this solution?

This annoyed me too, but I'm yet to fathom a clean fix. I hope this explanation makes sense.

It comes from select & unselect both firing on mousedown. What happens is that you select a bunch of nodes, and then do a second mousedown and hold on one node, with the intent to start a drag. That normal mousedown would fire unselect on all the other nodes. When the drag starts, those nodes are not selected and don't drag.

I could either fix it by capturing the drag selection before handling a normal (no modifier keys) mousedown on a selected node, but after handling any other type of mousedown and then redrawing the highlighting only on mouseup (assuming I didn't drag).

Or I could attempt to check for a normal click, and then trigger a reselect (unselect) on mouseup.

Either of which turns out to be very complicated with how I manage things. I plan to clean up the select code at some point. I just haven't had any time to work on it lately. As a curiosity I just checked and OS X Finder has a noticeable delay after the mouseup for clearing a multiselect. This is so a double click doesn't cause an unselect. I'm not near a windows box to find out what it does right now, probably something complete different.

Make sense? no? :(

Deadmeat
18 Sep 2009, 6:33 AM
This annoyed me too, but I'm yet to fathom a clean fix. I hope this explanation makes sense.



Okay I lied. It turns out I was just too lazy to spend a few hours working on it.

I was wondering what would be needed, and then reading the code, and then doing some tests, and well I hacked something together. This just proves the concept, I think it works for normal stuff, but for anything fancy I'd put odds on breaking something. It still won't work with node editing for example.

It's available here: http://www.users.on.net/~clear/ext/dev-1.2-alpha/

I've commented out the delay code for dblclick because the dblclick listeners don't support multiselect yet and it looks weird to see a single node in the tree open up in the middle of a selection.

If you use this and find any bugs, please post them here. I only had time to try it out in Firefox 3.5. YMMV with anything else.

Found a limitation already. Hold the mouse for 350ms and the unselect doesn't fire.

robpi
19 Sep 2009, 8:00 AM
I've tested the new 1.2 Alpha-Version on ie6,7,8 and Firefox 3.5. I like the new deselect-solution. It works now the way, that users expect.

One thing looks a little strange: when you click on a node and hold the left button for some time (maybe more than the mentioned 350ms), but do not move the node, then the tree flickers when you release the button. For me it's something i could live with, not a big issue.

Deadmeat
19 Sep 2009, 9:37 PM
One thing looks a little strange: when you click on a node and hold the left button for some time (maybe more than the mentioned 350ms), but do not move the node, then the tree flickers when you release the button.

The flickering will be the cleanup of the drag proxy. Regardless of whether or not you move the mouse, it calls startdrag after about 350ms. This creates the proxy floating tree. It's actually hidden until you move the mouse, but flickers on mouseup. The reason the select cleanup doesn't happen is because I'm checking to see that startdrag is called.

I have a workaround for the selection issue, but it's not as nice as I'd like. I'm messing with something more complicated, but hopefully better.

Deadmeat
20 Sep 2009, 8:35 PM
I've done some more testing and...

You can delay the start of a drag longer by setting Ext.dd.DragDropMgr.clickTimeThresh to a bigger number. Currently its at 350ms (despite what the API docs say)

In older versions it was at 1000 (1 second).

I assume the reason for clickTimeThresh at all is because some browsers don't support nice movement detection?? I've posted a documentation bug and asked the question.

You can change the value anywhere (this affects all drag/drop operations) by doing this.



Ext.dd.DragDropMgr.clickTimeThresh = 1000;


or...

I have a fix that does an extra call from the TreeDragZone, but it's kinda hacky. Add this function to Ext.ux.MultiSelectTreeDragZone



alignElWithMouse: function(el, iPageX, iPageY) {
Ext.ux.MultiSelectTreeDragZone.superclass.alignElWithMouse.apply(this, arguments);
// test if the proxy object is visible (indicating a drag)
// Not sure why this works here, but not elsewhere.
if (Ext.fly(el).isVisible()) {
var selModel = this.tree.getSelectionModel();
if (selModel && selModel.onDrag) {
selModel.onDrag.call(selModel);
}
}
},


and this function to Ext.ux.FixedMultSelectionModel



onDrag: function() {
this.normalClick = false;
},


and remove this from the init function.



tree.on("startdrag", function(){ this.normalClick=false; }, this)


Just download from http://www.users.on.net/~clear/ext/dev-1.2-alpha/.

It would be nice if it was possible to do this check once on(invalid)drop, but there are other issues at work.

Honestly, because of the flicker caused by the InvalidDrop function, I think it's better to just up the clickTimeThresh and/or if they hold the button down for too long, too bad.

I'm looking for a fix for the flickering and if I find something I'll add my normal select triggering in the same place. There must be some way to stop the repair routine from running. However it's looking that requires overrides for Ext.dd.DragSource. (Basically the flicker is the offscreen drag drop proxy/ghost animating in from -10000,-10000 to the selected nodes).

Fun and biscuits. Both of these are on my todo list for 1.2.

rzvikas
4 Feb 2010, 5:53 AM
Hi

Could someone tell me when the MutilSelect Drag and Drop feature would be part of EXTJS library itself ?

Thanks
Vikas

Deadmeat
5 Feb 2010, 4:32 PM
Hi

Could someone tell me when the MutilSelect Drag and Drop feature would be part of EXTJS library itself ?

Thanks
Vikas

This is probably the wrong place to ask. You should try one of the official ExtJS or feature request threads.

The Tree component virtually hasn't changed since the 2.x series (which this extension was written for.)

However there is a revision planned for the upcoming 3.2 release which will focus on mapping tree records to data stores. It was mentioned in the 3.1 release announcement and the obliquely in the roadmap as relational functionality for stores (I assume that's it.)

This will surely require some changes to the core tree code and I expect my extension to need major surgery. That is probably your best chance to get new features added before ExtJS 4.0.

I've been too busy to work on this lately, and even then I've been tempted to redesign the tree internals completely, rather than hack around the edges. I'll wait and see what 3.2 brings though.

jsakalos
1 Apr 2010, 2:33 PM
Sorry that I haven't read the whole thread, anyway, I'd like to ask what is the status of this extension. I'm interested in two main things: 1) Is it maintained and do plan to maintain it in the future and 2) Is it Ext 3.2.0 compatible?

What I need is dragging multiple tree nodes and I'm too busy to write that.

Deadmeat
3 Apr 2010, 12:03 AM
This thread is a bit meandering, most of the issues raised are to do with offbeat problems, and bugs that area already fixed.

1) Is this maintained. That's hard to say. I'll say yes, but then I've been too busy to do much for almost a year now. I originally wrote it against version 2.1. I haven't had to change it since, because the tree components have barely changed in that time. The only notable changes have been my bugfixes to the core. (drop order, and allow container drop bugs)

2) Does it work. It works with 2.x, 3.0, 3.1. So it should work with 3.2 unless they've changed the tree significantly. Which it doesn't look like they have. If it doesn't work, post here, and I'll fix it. I do (mostly) fix bugs that are reported.

My policy on bug fixes currently is a pragmatic mixture of available time, and how critical the bug is. If it breaks with 3.2 I'll fix it.

This certainly allows for selecting and dragging multiple nodes. The status as of 3.1 is best summed up by the demo page. http://www.users.on.net/~clear/ext/index.html which shows what it does.

This extension started as part of a major project I was working on. The priority of which was almost immediately downgraded not long after I'd written this. I'm still hoping that the multiple drag and drop features will be added to the core, but I'm not holding my breath.

I will be doing more work on this component in the future; hopefully the near future. For the moment I'm keeping it up to date, and answering questions when people post. Unfortunately I haven't had time to make this a proper project with well documented source. If you have problems, I'm happy to help out.



Sorry that I haven't read the whole thread, anyway, I'd like to ask what is the status of this extension. I'm interested in two main things: 1) Is it maintained and do plan to maintain it in the future and 2) Is it Ext 3.2.0 compatible?

What I need is dragging multiple tree nodes and I'm too busy to write that.

Deadmeat
3 Apr 2010, 3:12 AM
I've just updated the demo page to use ExtJS 3.2.0 so you can test it yourself.

See the extension thread here (http://www.extjs.com/forum/showthread.php?t=75055)

It seems to work.

Deadmeat
3 Apr 2010, 4:27 AM
How odd.. where did my previous post go.

jsakalos
3 Apr 2010, 8:35 AM
Super! Good to hear. I'll contact you when needed.

Thank you.

Deadmeat
4 Apr 2010, 4:20 AM
I've posted an updated beta version for anyone interested.

You get it here: http://www.users.on.net/~clear/ext/index-beta.html

The selection change is nice. If you've ever hated how single click on a selected node doesn't deselect all the other selected nodes, you should like it.

Also fixes a minor flickering issue.

Ebpo
4 May 2010, 7:19 AM
Hi!

I'm thinking about using your extension, but I got to ask if it's going to work if I use a treeEditor?

Thanks

Deadmeat
4 May 2010, 10:35 PM
Short answer is no. Long answer is that you can work around it.

The reason it doesn't work is because of a gotcha with the way the multiselect tree does node selection. The default tree uses the "click" event, whereas for a variety of reasons, my version effectively uses "mousedown" (directly triggered by the drag and drop api).

The tree node editor also listens for the "click" event, and importantly it relies on the node not being selected at the time it receives the event. But because I select the node on mousedown (which is always before click) it's always selected, which means the editor always pops up when you click on a node.

There are a couple workarounds if you wanted to implement this functionality. One would be to remove the listener from the the node editor altogether (via subclassing) and then trigger node editing from a double click, or keypress (eg return) event listener, or possible even a ui button.

Unfortunately there is no easy fix; Changing the "click" listener in the node editor to be a "mousedown" listener doesn't work because the tree doesn't by default listen for mousedown events. Adding mousedown events to the tree (in the normal way) doesn't work either, because the drag and drop mousedown listeners always execute first.

I do have some other possible ideas but I never quite get around to working on them.

If you try one of the workarounds, and have difficulties I can maybe help out.



Hi!

I'm thinking about using your extension, but I got to ask if it's going to work if I use a treeEditor?

Thanks

Deadmeat
4 May 2010, 10:41 PM
Oops duplicate post

wm003
4 May 2010, 10:48 PM
Thanks for the update. Works very well until now :)

Fallen Zen
11 May 2010, 1:50 AM
How about a Tree to Tree-grid drag? Had problems creating that.

Deadmeat
12 May 2010, 1:16 AM
How about a Tree to Tree-grid drag? Had problems creating that.

What sort of problems?

I'm not familiar with the updated tree grid example extension. It seems superficially similar to the old tree with columns example though. Dragging to the treegrid shouldn't be too much of a problem provided you have the right tree node attributes and access the right properties of the DragEvent.

Implementing multiselect drag and drop on the treegrid is likely to be more difficult. You'd have to edit all the treegrid stuff to extend the multiselect equivalents (where applicable). It's quite likely you'll need to manually merge some of the feature sets. The rendering of the drag ghost is likely to be fiddly too.

adam.jimenez
28 Oct 2010, 7:23 AM
I'm not familiar with the updated tree grid example extension. It seems superficially similar to the old tree with columns example though.


Tree-grid is much better than the tree columns example. Tree-grid columns are sortable / resizable and don't overlap.

Would be great to see better tree-grid support. It works well but I think there are a few minor bugs. Maybe it can be added to the demo page..

michel222
29 Oct 2010, 3:32 AM
I was looking for this information. Hope it will provide help for everyone.I need some more information about Drag and drop extension???



.

h.wagner@telekom.de
1 Mar 2011, 8:17 AM
Any news about updates for this extension? The most "actual" one still seems to be 1.2beta :(.

Thanks in advance.

Deadmeat
2 Mar 2011, 5:16 PM
Unfortunately not. The code is still available, and it's GPL so if anyone wants to pick it up and work on it, they're welcome to. If there are bugs (and I hear about them) I still try and fix them promptly. It should still work for the current Ext release.

The reasons for this are many, some of the things I wanted to add/fix required a complete rewrite of the entire tree and then, by association, all the related components. This just wasn't practical, given all the other tree extensions out there. Also it appears that it may not be necessary.

I understand that the Tree component for Ext 4.0 will be based on the Grid component, and is effectively a complete rewrite. However I haven't looked into this yet. I don't believe it's in the Developer Preview. I *assume* that the tree will inherit the drag and drop capabilities of the Grid component. As far as I know, this would make my extension effectively obsolete.

I might change my mind once the final release of Ext 4.0 is out. However I'm hoping the base tree covers everything.


Any news about updates for this extension? The most "actual" one still seems to be 1.2beta :(.

Thanks in advance.

h.wagner@telekom.de
14 Apr 2011, 11:50 PM
I understand that the Tree component for Ext 4.0 will be based on the Grid component, and is effectively a complete rewrite. However I haven't looked into this yet. I don't believe it's in the Developer Preview. I *assume* that the tree will inherit the drag and drop capabilities of the Grid component. As far as I know, this would make my extension effectively obsolete.

I might change my mind once the final release of Ext 4.0 is out. However I'm hoping the base tree covers everything.
Ext4 Beta3 (almost final - at least featurely) is there, but it doesn't seems to support your functionality :(.

tobiu
15 Apr 2011, 12:42 AM
impossible to use ext3 ux with ext4 without porting them over (ext.define etc.).


best regards
tobiu

Deadmeat
15 Apr 2011, 9:00 PM
Ext4 Beta3 (almost final - at least featurely) is there, but it doesn't seems to support your functionality :(.

No it's very disappointing. Even worse it looks like I need to make changes to get it working again.

The bad news is that I'm not sure when I will get time to work on it. Since they're still adding features I figure I'll look at it after the final releases. :(

There may be a better way to do stuff with the new plugins/addons/mixins or whatever. I haven't had time to look. I am also considering not using the default tree at all, and forking into my own standalone tree component. If nothing else it makes it easier to maintain.

Deadmeat
15 Apr 2011, 9:04 PM
impossible to use ext3 ux with ext4 without porting them over (ext.define etc.).

Sorry I haven't even looked at Ext4 extensions yet. I've only tried a few demos. So far I'm not impressed at all by their tree "improvements".

Paul Ellis
20 Apr 2011, 6:04 AM
Hi,

Your example not work in local.

Any live example?

Thanks in advance,

I'd like to ask the same

Paul Ellis

Chris.s
25 Apr 2011, 4:45 AM
Live example please.

Thank You
Chris

Deadmeat
26 Apr 2011, 6:18 AM
There is a link to a live example in the post. The code in the first post is outdated, and was mostly a proof of concept anyway. All the updates were posted on the demo page.

There is no updates to Ext 4.0 yet. I don't know when I will have time.