Captain's Universe Home
Captain's Universe Home
Cosmic Ray Muon DetectorTeleGarden Pages
Time on MarsBryophyllum Plants
Jupiter Radio AstronomyAncient Pages
Salzburg Tourist GuideEarth Magnetometer
  H O M E     AJAX & MORE     LINUX & MORE     RTAI     XENOMAI     ADEOS IPIPE      
    JAVA & BROWSERS     *NIX     ELECTRONICS     REVIEWS     ARTEMIA     FAIRY SHRIMP      


XUL Drag Drop Tree + Adding / Deleting Nodes + Move up / Move down Tree Nodes



The XUL language is very powerful. Powerful enough to forget traditional means of doing web applications with HTML and using the many widget available with XUL. If you add AJAX to this neat tree stuff, it can and will become a bitchin' groovy web application.

DEMO below

In addition to the already publicly available tutorials
XUL tree NEW - Adding/editing nodes
XUL tree - Adding/editing nodes
we have this maximum advanced XUL Drap and Drop example.


Here we have a screenshot of the XUL tree in action (also showing the context-menu with the move up / down and adding functions):
XUL Drag Drop Tree + Adding / Deleting Nodes + Move up / Move down Tree Nodes


You can drag/drap individual tree nodes on other nodes, which will become childs of that node. Move nodes up or down with the context menu - right click on some node and the context menu will appear. You can also add/rename/delete nodes with the context menu.

This is the only complete XUL Tree with DRAG AND DROP tutorial on the web. It took me quite some time to put this together - just to mention one: thanks to the EXE project in Australia for some drag-drop-tree functions. Those XUL functions are not documented at all (at least not with *working* examples) so that EXE project provided a lot to this effort.
Other parts are based on the now closed(?) OS project MozillaVault

TODO
  • When adding a node, the node-id will not be set correctly - when clicking on a node twice (dblclick) a false node-id will be alerted


DOWNLOAD:
xul-drag-drop-tree.tar.gz (V0.1 7kB)

DEMO:
First, before you open the demo, you need to consider this:

Open about:config and check that
signed.applets.codebase_principal_support
is set to "true"

Otherwise Firefox will display something like this
Error: uncaught exception: A script from "http://www.captain.at" 
was denied UniversalXPConnect privileges.


Also make sure you check the checkbox "Remember this decision", when FireFox will display this message
A script from "http://www.captain.at" is requesting enhanced abilities that are
UNSAFE and could be used to compromise your machine or data:

Run or install software on your machine

Allow these abilities only if you trust this source to be free of viruses or malicious
programs.
[ ] Remember this decision

otherwise the browser will hang (actually the screen will be flooded with those little windows when dragging and dropping a tree icon).

click here for the DEMO

Just in case some lost souls are searching drag and drop stuff via some search engine, here is the source code of the drag'n'drop stuff:

captain.js
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");

// ===========================================================================
// eXe
// Copyright 2004-2005, University of Auckland

// Returns the tree item from index which can come from 'tree.getCurrentIndex'
function getOutlineItem(tree, index) {
    // Get the appropriate treeitem element
    // There's a dumb thing with trees in that mytree.currentIndex
    // Shows the index of the treeitem that's selected, but if there is a
    // collapsed branch above that treeitem, all the items in that branch are
    // not included in the currentIndex value, so
    // "var treeitem = mytree.getElementsByTagName('treeitem')[mytree.currentIndex]"
    // doesn't work. We have to do this!
    var mytree = tree
    if (!mytree) { mytree = document.getElementById('outlineTree') }
    var items = mytree.getElementsByTagName('treeitem')
    for (var i=0; i<items.length; i++) {
        if (mytree.contentView.getIndexOfItem(items[i]) == index) {
            return items[i]
        }
    }
    return null // Should never get here
}


// Returns the currently selected tree item.
function currentOutlineItem(tree) {
    // Get the appropriate treeitem element
    var mytree = tree
    if (!mytree) { mytree = document.getElementById('outlineTree') }
    return getOutlineItem(tree, mytree.currentIndex)
}

// Returns the _exe_nodeid attribute of the currently selected row item
function currentOutlineId(index)
{
    var treeitem = currentOutlineItem()
    return treeitem.getElementsByTagName('treerow')[0].getAttribute('_exe_nodeid')
}

function treeDragGesture(event) {
    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect")
    var tree = document.getElementById('outlineTree') 
    var treeitem = currentOutlineItem(tree)
    // Only allow dragging of treeitems below main
  //  if (tree.view.getIndexOfItem(treeitem) <= 1) { alert("nodrag"); return }
    // Don't start drag because they are moving the scroll bar!
    var row = { }
    var col = { }
    var child = { }
    tree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child)
    if (!col.value) {  return }
    // CRAPINESS ALERT!
    // If they're moving, (without ctrl down) the target node becomes our sibling
    // above us. If copying, the source node becomes the first child of the target node
    var targetNode = getOutlineItem(tree, row.value)
    // Start packaging the drag data (Which we don't use but have to do anyway)
    var data = new Array(treeitem)
    var ds = Components.classes["@mozilla.org/widget/dragservice;1"].
       getService(Components.interfaces.nsIDragService);
    var trans = Components.classes["@mozilla.org/widget/transferable;1"].
       createInstance(Components.interfaces.nsITransferable);
    trans.addDataFlavor("text/plain");
    var textWrapper = Components.classes["@mozilla.org/supports-string;1"].
       createInstance(Components.interfaces.nsISupportsString);
    textWrapper.data = currentOutlineId(); // Get the id of the node bieng dragged
    // double byte data
    trans.setTransferData("text/plain", textWrapper, textWrapper.data.length);  
    // create an array for our drag items, though we only have one this time
    var transArray = Components.classes["@mozilla.org/supports-array;1"].
       createInstance(Components.interfaces.nsISupportsArray);
    // Put it into the list as an |nsISupports|
    //var data = trans.QueryInterface(Components.interfaces.nsISupports);
    transArray.AppendElement(trans);
    // Actually start dragging
    ds.invokeDragSession(treeitem, transArray, null, ds.DRAGDROP_ACTION_COPY + 
       ds.DRAGDROP_ACTION_MOVE);
    event.preventBubble(); // This line was in an example, will test if we need it later...
}


function treeDragEnter(event) {
window.status = "treeDragEnter";
    event.preventBubble(); // This line was in an example, will test if we need it later...
    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect")
    var ds = Components.classes["@mozilla.org/widget/dragservice;1"].
       getService(Components.interfaces.nsIDragService);
    var ses = ds.getCurrentSession()
    var tree = document.getElementById('outlineTree')
    //tree.treeBoxObject.onDragEnter(event)
    if (ses) { ses.canDrop = 'true' }
}


function DragOverContentArea ( event )
{
  var validFlavor = false;
  var dragSession = null;


    var tree = document.getElementById('outlineTree')
    var row = { }
    var col = { }
    var child = { }
    tree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child)
    var targetNode = getOutlineItem(tree, row.value)
	 document.getElementById("myout").value = targetNode.id;
	 targetNode.style.backgroundColor = "red";
 	 targetNode.style.color = "red";
  
	try {
		netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
	} catch (e) {
		alert("Permission to save file was denied.");
	}
/*	var file = Components.classes["@mozilla.org/file/local;1"]
		.createInstance(Components.interfaces.nsILocalFile);
	var outputStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
		.createInstance( Components.interfaces.nsIFileOutputStream );
*/  
  

//  var dragService =
/*    Components.classes["component://netscape/widget/dragservice"].
         getService(Components.interfaces.nsIDragService);
      Components.classes["@mozilla.org/netscape/widget/dragservice"].
         getService(Components.interfaces.nsIDragService);
*/
	var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
		getService().QueryInterface(Components.interfaces.nsIDragService);
  

  if ( dragService ) {
    dragSession = dragService.getCurrentSession();
    if ( dragSession ) {
      if ( dragSession.isDataFlavorSupported("moz/toolbaritem") )
        validFlavor = true;
      else if ( dragSession.isDataFlavorSupported("text/plain") )
        validFlavor = true;
      //XXX other flavors here...such as files from the desktop?

      if ( validFlavor ) {
        // XXX do some drag feedback here, set a style maybe???

        dragSession.canDrop = true;
        event.preventBubble();
      }
    }
  }
} // DragOverContentArea



function treeDragExit(event) {
    // This line was in an example, will test if we need it later...
    event.preventBubble(); 
    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect")
    var ds = Components.classes["@mozilla.org/widget/dragservice;1"].
      getService(Components.interfaces.nsIDragService);
    var ses = ds.getCurrentSession()
    var tree = document.getElementById('outlineTree')
}



function treeDragDrop(event) {
  
    event.preventBubble(); // This line was in an example, will test if we need it later...
    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect")
    var ds = Components.classes["@mozilla.org/widget/dragservice;1"].
       getService(Components.interfaces.nsIDragService);
    var ses = ds.getCurrentSession()
    var sourceNode = ses.sourceNode
    var tree = document.getElementById('outlineTree')
    // We'll just get the node id from the source element
    var nodeId = sourceNode.firstChild.getAttribute('_exe_nodeid')
    // Get the new parent node
    var row = { }
    var col = { }
    var child = { }
    tree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child)
    // CRAPINESS ALERT!
    // If they're moving, (without ctrl down) the target node becomes our sibling
    // above us. If copying, the source node becomes the first child of the target node
    var targetNode = getOutlineItem(tree, row.value)
    if (ses.dragAction && ses.DRAGDROP_ACTION_COPY) {
        // Target node is our parent, sourceNode becomes first child
        var parentItem = targetNode
        var sibling = null  // Must be worked out after we get 'container' (treeitems)
        var before = true
    } else {
        // Target node is our sibling, we'll be inserted below (vertically)
        //  it on the same tree level
        var parentItem = targetNode.parentNode.parentNode
        var sibling = targetNode
        var before = false
    }

    // Do some sanity checking
    if ((sourceNode == parentItem) || (sourceNode == targetNode)) return;
    var parentItemId = parentItem.firstChild.getAttribute('_exe_nodeid')
    if (sibling && (tree.view.getIndexOfItem(sibling) <= 1)) { return } 
    try { if ((parentItem.getElementsByTagName('treechildren')[0].firstChild == sourceNode) 
        && before) { return } // Can't drag into same position
    } catch(e) { } // Ignore when parentItem has no treechildren node
    // Check for recursion
    var node = targetNode.parentNode
    while (node) {
        if (node == sourceNode) { return } // Can't drag into own children
        node = node.parentNode
    }
    // Re-organise the tree...
    // See if parent is a container
    var isContainer = parentItem.getAttribute('container')
    if ((!isContainer) || (isContainer == 'false')) {
        // Make it one
        var container = parentItem.appendChild(document.createElement('treechildren'))
        parentItem.setAttribute('container', 'true')
        parentItem.setAttribute('open', 'true')
    } else {
        var container = parentItem.getElementsByTagName('treechildren')[0]
        // If still haven't got a 'treechildren' node, then make one
        if (!container) {
            var container = parentItem.appendChild(document.createElement('treechildren'))
        }
    }
    // Now we can work out our sibling if we don't already have it
    if (before) { sibling = container.firstChild }
    // Move the node
    var oldContainer = sourceNode.parentNode
      // For some reason works, but still raises exception!
    try { oldContainer.removeChild(sourceNode) } catch(e) { } 
    if (sibling) {  // If the container has children
        // Insert either before or after the sibling
        if (before) {
            if (sibling) {
                container.insertBefore(sourceNode, sibling)
            } else {
                container.appendChild(sourceNode)
            }
        } else {
            // Append after target node
            if (sibling.nextSibling) {
                container.insertBefore(sourceNode, sibling.nextSibling)
            } else {
                container.appendChild(sourceNode)
            }
        }
    } else {
        // Otherwise, just make it be the only child
        container.appendChild(sourceNode)
    }
    // See if the old parent node is no longer a container
    if (oldContainer.childNodes.length == 0) {
        oldContainer.parentNode.setAttribute('container', 'false')
        oldContainer.parentNode.removeChild(oldContainer) // Remove the treechildren node
    }
    // Tell the server what happened
    var nextSiblingNodeId = null
    var sibling = sourceNode.nextSibling
    if (sibling) {
        nextSiblingNodeId = sibling.firstChild.getAttribute('_exe_nodeid')
    }
    nevow_clientToServerEvent('outlinePane.handleDrop', this, '', 
            sourceNode.firstChild.getAttribute('_exe_nodeid'), parentItemId, nextSiblingNodeId)
}


Last-Modified: Sat, 04 Feb 2006 16:02:59 GMT

Google
 
Web www.captain.at
go to top
© 1996-2010 . All rights reserved.
No reproduction, distribution, publishing or transmission of the copyrighted materials at this site is permitted. Policy
go to top