JsTree with dnd plugin, always copy - jstree

I have 2 trees using jsTree and dnd plugin.
I want that each drag operation to be a copy instead of a move.
There is a "copy_modifier" which works Ok when pressing a modifier key, but I want copy to be the default behavior without the modifier.
Any ideas?
Thanks,
Adrian

Found a solution on http://groups.google.com/group/jstree
I added the following section when configuring jsTree:
"crrm": {
"move": { "always_copy": "multitree" }
}
Hope this helps,
Adrian

another solution for the new version. it works, but not fully tested.
"core": {
"check_callback": function (operation, node, node_parent, node_position, more) {
if (more) {
if (more.is_multi) {
more.origin.settings.dnd.always_copy = true;
} else {
more.origin.settings.dnd.always_copy = false;
}
}
return true;
}
}

Adrian's solution won't work with the new versions.
There's that dnd plugins always copy flag
dnd.always_copy
Setting this flag will make all drag and drops copy operations instead of move. But if you are looking for a solution where you need internal tree elements to be moved on dnd but inter tree dnds to be copies than here's a hack:
Keep a global variable flag on your page
Handle copy_node.jstree events and update your global flag from
data.is_multi (data is the second arg of the event function)
Implement check_callback function and if operation is delete_node and your flag is set unset your flag and return false, preventing deletion from the dnd.

Related

MS Word VSTO Addin Find.Execute fires ContentControlOnEnter event

It seems that if Find.Execute finds a result inside a ContentControl, it will cause the ContentControlOnEnter and ContentControlOnExit events to fire. It's particularly annoying because the exit event fires even if the selection is still in the content control, so any code which sets the states of buttons dependent upon a content control being active will appear to be in the incorrect state.
Given a document containing a single content control with the word "test", and the following code:
// In setup
Application.ActiveDocument.ContentControlOnEnter += ActiveDocument_ContentControlOnEnter;
private void ActiveDocument_ContentControlOnEnter(Word.ContentControl ContentControl)
{
var selRange = _Application.Selection.Range;
_logger.Debug(m => m("Selection: {0}-{1}", selRange.Start, selRange.End));
}
//Later in another method
var finder = _Application.ActiveDocument.Range().Find;
_logger.Debug("Find.Execute start");
finder.Execute("test);
_logger.Debug("Find.Execute end");
The following gets logged:
38137 [VSTA_Main] DEBUG - Find.Execute start
38141 [VSTA_Main] DEBUG - Selection: 1-5
38149 [VSTA_Main] DEBUG - Find.Execute end
We have a lot of code that handles ContentControlOnEnter and ContentControlOnExit events, and having the find operation cause them to be called is really causing problems!
Is there any way to use Find.Execute without having it trigger these events? Failing that, is there a good way to distinguish between the Find-triggered ones and the genuine user ones? I have tried using the time between the enter and exit events, but this is not reliable.
I had similar problems in Word, though it was about the Selection event. I tried many solutions, but only one helped. In your case, make a new field bool _skipEnterAndExitEvents and set it true before calling
finder.Execute("test) and false after calling. And in the enter and exit event handlers check this field, if the field is true then just skip. This solutions is not beautiful, looks like a hack, but other solutions are even uglier and don't really work.
I think I found a decent solution:
private bool _doIgnoreNextExit = false;
private void ActiveDocument_ContentControlOnEnter(Word.ContentControl ContentControl)
{
if (Application.Selection.Find.Found)
{
_logger.Debug("Ignoring CC enter event caused by Find operation");
_doIgnoreNextExit = true;
return;
}
// Do things
}
private void ActiveDocument_ContentControlOnExit(Word.ContentControl ContentControl)
{
if(_doIgnoreNextExit)
{
_logger.Debug("Ignoring fake exit");
_doIgnoreNextExit = false;
return;
}
// Do things
}

Dragula Copy and removeOnSpill

I'm trying to use the Dragula Drag & Drop library to Clone elements into a target container AND allow the user to remove cloned elements from the Target Container by drag & dropping them outside of the target container (spilling).
Using the examples provided I have this:
dragula([$('left-copy-1tomany'), $('right-copy-1tomany')], {
copy: function (el, source) {
return source === $('left-copy-1tomany');
},
accepts: function (el, target) {
return target !== $('left-copy-1tomany');
}
});
dragula([$('right-copy-1tomany')], { removeOnSpill: true });
Which does not work - it seems that 'removeOnSpill' simply doesn't work if the container accepts a copy.
Does anybody know what I am not doing, doing wrong or if there is a work-around?
TIA!
I came here after looking for a while for a solution to a similar issue using the ng2-dragula for angular2.
dragulaService.setOptions('wallet-bag', {
removeOnSpill: (el: Element, source: Element): boolean => {
return source.id === 'wallet';
},
copySortSource: false,
copy: (el: Element, source: Element): boolean => {
return source.id !== 'wallet';
},
accepts: (el: Element, target: Element, source: Element, sibling: Element): boolean => {
return !el.contains(target) && target.id === 'wallet';
}
});
I've got 4 divs that can all drag into one which has the id of wallet
They are all part of the wallet-bag
using this code, they can all copy into the wallet, not copy between each other, and you can remove them from the wallet using the spill but not from the others.
I'm posting my solution as it may also help someone.
Ok, so the general answer I came trough is that:
you can have 'removeOnSpill' working - even with 'copy' option set to true - , only if you set the 'copy' option applying ONLY when the 'source' container IS NOT the one you are trying to remove elements from.
In my case I had 3 containers from which I can drag in another one called 'to_drop_to'.
Those container have all id starting with 'drag'.
So I set:
var containers = [document.querySelector('#drag1'),
document.querySelector('#drag2'),
document.querySelector('#drag3'),
document.querySelector('#to_drop_to')];
dragula(containers, {
accepts: function (el, target, source, sibling) {
return $(target).attr('id')=="gadget_drop"; // elements can be dropped only in 'to_drop_to' container
},
copy: function(el,source){
return $(source).attr('id').match('drag'); //elements are copied only if they are not already copied ones. That enables the 'removeOnSpill' to work
},
removeOnSpill: true
}
and this worked for me.
Hope it helps.
From the dragula documentation
options.removeOnSpill
By default, spilling an element outside of any containers will move
the element back to the drop position previewed by the feedback
shadow. Setting removeOnSpill to true will ensure elements dropped
outside of any approved containers are removed from the DOM. Note that
remove events won't fire if copy is set to true.

Extjs4 how can I disable, the rowediting plugin in a grid?

I want to disable the rowediting plugin depends on a combo selection, I have the grid reference. How can I disable it? (I try to destroy it, but then when I close the window, i get a "Uncaught TypeError: Cannot call method 'getView' of undefined ".
Return false from the RowEditing beforeedit event.
listeners: {
beforeedit: function(editor, context){
if(comboBox.getValue() === /* whatever conditions you have */){
return false;
}
}
}
You can also set context.cancel = true to achieve the same effect, but I don't really see the point in it since returning false from a beforexyz event is a standard idiom in the Ext JS 4 library.

jstree move, drag and drop

I want to implement the move functionality for a node in jstree. Is it the move that needs to be implemented or the drag and drop? Also, it would be nice to have working code for binding the container to event and the event code.
You only need to use the dnd plugin if you don't need to enforce any move rules(don't allow certain nodes to be moved to other nodes etc)
If you need to enforce move rules, you can add the crrm plugin.
See the Reorder only demo of the dnd pluign documentation for an example of this. The documentation is very poor, so you will have to use the developer tool on your browser to see what the properties of the parameter for the check_move callback are. For the example in the documentation, m.o refers to your dragged node and m.r refers to your destination node.
You will also likely need to be notified when a node is moved, so bind to the move_node.jstree event when you initialize the tree:
$("#treeHost").jstree({
// ...
}).bind("move_node.jstree", function (e, data) {
// data.rslt.o is a list of objects that were moved
// Inspect data using your fav dev tools to see what the properties are
});
$("#demo1").jstree({
....
.bind("move_node.jstree", function (e, data) {
/*
requires crrm plugin
.o - the node being moved
.r - the reference node in the move
.ot - the origin tree instance
.rt - the reference tree instance
.p - the position to move to (may be a string - "last", "first", etc)
.cp - the calculated position to move to (always a number)
.np - the new parent
.oc - the original node (if there was a copy)
.cy - boolen indicating if the move was a copy
.cr - same as np, but if a root node is created this is -1
.op - the former parent
.or - the node that was previously in the position of the moved node */
var nodeType = $(data.rslt.o).attr("rel");
var parentType = $(data.rslt.np).attr("rel");
if (nodeType && parentType) {
// TODO!
}
})
});
The above approaches do not work with the latest versions of jstree (3.3.7 as of today).
The first line of Bojin's answer still holds true. To implement rules, you can use core.check_callback or possibly, the types plugin; the crrm plugin doesn't exist anymore. Bind to move_node.jstree to perform some action on completion of move (drop). By default, the dnd plugin allows re-ordering (dropping between two nodes) and copying (Ctrl + drag), in addition to moving a node. The code snippet below shows how to disable these additional behaviors.
$('#treeElement').jstree({
'core': {
'check_callback': CheckOperation,
...
},
'plugins': ['dnd']
})
.bind("move_node.jstree", function(e, data) {
//data.node was dragged and dropped on data.parent
});
function CheckOperation(operation, node, parent, position, more) {
if (operation == "move_node") {
if (more && more.dnd && more.pos !== "i") { // disallow re-ordering
return false;
}
... more rules if needed ...
else {
return true;
}
}
else if (operation == "copy_node") {
return false;
}
return true;
}

Wanted: Directions/ideas for a custom tree-to-tree drag'n'drop implementation in ExtJS

I need some combined functionality regarding drag'n'drop between two trees in ExtJS.
The first required feature is very simple and is just the builtin drag'n'drop features isolated to a single tree only.
The second required feature is that I wan't the user to be able to drag a node from the left tree and drop it at any node in the right tree.
The action should not remove the node from the left tree, thus creating the possibility of dragging the same node from the left tree to multiple places in the right tree.
My question is: Which approach should I take to combine these two functionalities, utilizing the existing possibilities in the TreePanel object without inventing the wheel again? I am not looking for a complete solution (it would be nice though ;-) ), but rather how to handle drag/drop-zones, events and so on.
Okay. I have been thinking about this some time now, and the following approach seems to work for me :)
I have configured the left tree as this:
listeners:
{
beforenodedrop: function (dropEvent) {
// Does this node come from the right tree?
if (dropEvent.source.tree.id !== dropEvent.tree.id) {
// The node should be discarded.
dropEvent.dropNode.parentNode.removeChild(dropEvent.dropNode, true);
// The node has been discarded, return drop succeeded.
dropEvent.dropStatus = true;
return false;
}
return true;
},
nodedragover: function (dragevent) {
// If the node comes from the right tree, it is allowed to be dropped here.
if (dragevent.source.tree.id !== dragevent.tree.id) {
return true;
}
// A node from this tree is not allowed to be dropped.
return false;
}
}
The right tree is configured like this:
listeners:
{
beforenodedrop: function (dropEvent) {
// Does this node come from the left tree?
if (dropEvent.source.tree.id !== dropEvent.tree.id) {
// The node should be cloned and inserted in the right tree.
// Copy the node.
var node = dropEvent.dropNode; // the node that was dropped
var nodeCopy = new Ext.tree.TreeNode( // copy it
Ext.apply({}, node.attributes)
);
// Match the id's.
nodeCopy.id = Ext.id(null,'newnode') + '_' + node.id;
// Find the right place to put it.
if (dropEvent.target.parentNode === dropEvent.tree.getRootNode()) {
// The node is placed on a folder, thus drop it there.
dropEvent.target.appendChild(nodeCopy);
} else {
// The node is placed inside a folder, thus place it in there.
dropEvent.target.parentNode.appendChild(nodeCopy);
}
// The node has been dropped, return okay and stop further process.
dropEvent.dropStatus = true;
return false;
}
// Just use the normal builtin drag and drop.
return true;
}
}
Both trees has been set to enable Drag'n'Drop:
enableDD: true
All leaf nodes have the following configuration:
allowDrop: true,
draggable: true
All folders are set to:
allowDrop: true,
draggable: false
The conclusion is, that I have chosen to override some of the builtin drag'n'drop methods in the treepanel while still maintaining the builtin functionality.