--- nodehierarchy/nodehierarchy.module	2010-09-26 06:03:16.000000000 +0200
+++ nodehierarchy/nodehierarchy.module	2011-06-12 20:44:48.000000000 +0200
@@ -1532,78 +1532,90 @@ function _nodehierarchy_parent_options($
  *
  * @param $result
  *   The database result.
- * @param $parents
- *   An array of the plid values that represent the path from the current page
- *   to the root of the menu tree.
- * @param $depth
- *   The depth of the current menu tree.
+ * @param $exclude
+ *   Menu items for the node with nid=$exclude and descendants thereof are skipped.
+ *   This is used to exclude the currently edited node so you don't try to make it parent of itself.
+ * @param $allowed_types
+ *   Content types to allow in the result.
  * @return
  *   See menu_tree_page_data for a description of the data structure.
  */
-function nodehierarchy_tree_data($result = NULL, $exclude = NULL, $allowed_types, $depth = 1) {
-  list(, $tree) = _nodehierarchy_tree_data($result, $exclude, $allowed_types, $depth);
+function nodehierarchy_tree_data($result = NULL, $exclude = NULL, $allowed_types) {
+  $tree = _nodehierarchy_tree_data_sorted($result, $exclude, $allowed_types, array('depth'=>0));
+  $tree = $tree[0]['children']; //unwrap the extraneous depth=0 layer
   return $tree;
 }
 
 /**
- * Recursive helper function to build the data representing a menu tree.
+ * Recursive helper function to build the sorted data tree representing a menu tree.
+ * Unlike _nodehierarchy_tree_data, this function actually uses a recursive array tree,
+ * which is sorted by weight and then (depth-first) flattened to an array with the nid:mlid
+ * keys that _nodehierarchy_get_parent_selector_value($previous_element) returns.
  *
- * The function is a bit complex because the rendering of an item depends on
- * the next menu item. So we are always rendering the element previously
- * processed not the current one.
- */
-function _nodehierarchy_tree_data($result, $exclude = NULL, $allowed_types, $depth, $previous_element = array()) {
-  $remnant = NULL;
-  $tree = array();
-  $enabled_tree = TRUE;
-  $exclude = NULL;
-
-  while ($item = db_fetch_array($result)) {
-    if ($exclude !== $item['nid']) {
-      $item['format'] = FILTER_FORMAT_DEFAULT;
-      $item['disabled'] = in_array($item['type'], $allowed_types) ? FALSE : TRUE;
-      $item['disabled'] = $item['disabled'] || (!node_access('update', $item) && !user_access('create child of any parent'));
-      $enabled_tree = $enabled_tree || empty($item['disabled']) || (isset($previous_element['disabled']) && empty($previous_element['disabled']));
-
-      // The current item is the first in a new submenu.
-      if ($item['depth'] > $depth) {
-        // _menu_tree returns an item and the menu tree structure.
-        list($item, $below) = _nodehierarchy_tree_data($result, $exclude, $allowed_types, $item['depth'], $item);
-        if ($previous_element && ($below || !$previous_element['disabled'])) {
-          $tree[_nodehierarchy_get_parent_selector_value($previous_element)] = $previous_element;
-        }
-        $tree += $below;
-
-        // We need to fall back one level.
-        if (!isset($item) || $item['depth'] < $depth) {
-          return $enabled_tree ? array($item, $tree) : array($item, array());
-        }
-        // This will be the link to be output in the next iteration.
-        $previous_element = $item;
+ * @example Usage:
+ *   $out = _nodehierarchy_tree_data_sorted($result, $exclude, $parent_types, array('depth'=>0));
+ *   $out = $out[0]['children']; //unwrap the extranous depth=0 layer
+ */
+function _nodehierarchy_tree_data_sorted($result, $exclude = NULL, $allowed_types, $parent) {
+  $here = array('parent'=>$parent);
+  
+  // Get the next item from the database result (ordered as dept-first flattened array)
+  $item = db_fetch_array($result); // while (... ) {
+  while ($item) {
+    //if (!empty($item) && $exclude === $item['nid']) $item = db_fetch_array($result); // exclude a certain nid
+    if (empty($item)) {print "\nBREAKING early\n"; break;} //return $here; // end of $results reached
+    
+    $item['format'] = FILTER_FORMAT_DEFAULT;
+    $item['disabled'] = in_array($item['type'], $allowed_types) ? FALSE : TRUE;
+    $item['disabled'] = $item['disabled'] || (!node_access('update', $item) && !user_access('create child of any parent'));
+    $item['disabled'] = $item['disabled'] || is_null($item['plid']) || ($exclude === $item['nid']); // skip the nodes that are not in menu and one specific node (the currently edited) 
+    
+    if ($item['depth'] > $parent['depth']) {
+      // $item is child of $parent
+      
+      // Get descendants to $item (inner menu items, if any) and the next item
+      list($item_with_tree, $item) = _nodehierarchy_tree_data_sorted($result, $exclude, $allowed_types, $item, $flatten);
+      $p = $item_with_tree['parent'];
+      // Now $p has the old value of the $item, and $item is the new item that was read from the database and returned
+      if (!$p['disabled']) {
+        // Append the processed item and its descendant tree 
+        // Set an unique key, to sort children by later. The order is as in includes/menu.inc#_menu_tree_check_access
+        $sorting_key = (50000 + $p['weight']) .' '. $p['title'] .' '. $p['mlid'];
+        $here['children'][$sorting_key] = $item_with_tree;
+        //print "\n   appending key=$sorting_key";
+        //print "\n-#5-- Back at parent depth={$parent['depth']}@{$parent['title']}, child count=". count($here['children']). ": next=". $item['title'] . " next depth=". $item['depth'] . "\n";
       }
-      // We are at the same depth, so we use the previous element.
-      elseif ($item['depth'] == $depth) {
-        if ($previous_element && !$previous_element['disabled']) {
-          // Only the first time.
-          $tree[_nodehierarchy_get_parent_selector_value($previous_element)] = $previous_element;
+            
+      // The next iteration will look at the returned next $item, 
+      // which may be yet a child of $parent or may cause return to outer level.
+    } else {
+      // There were no more children.
+      break;
+    }
+  }//end while
+  
+  if (isset($here['children'])) {
+    // Sort by the keys (weight . title . menu_link_id), as in includes/menu.inc#_menu_tree_check_access
+    ksort($here['children']);
+    // Now, flatten the inner tree (which has been sorted) and change the keys to
+    // the nid:mlid format the Nodehierarchy menu position form uses. 
+    $flattened_children = array();
+    foreach ($here['children'] as $already_flat_child) {
+      foreach ($already_flat_child as $type => $inner) { // $type is 'parent' or 'children'
+        if ($type == 'children') {
+          foreach ($inner as $grandchild)
+            $flattened_children[_nodehierarchy_get_parent_selector_value($grandchild)] = $grandchild;
+        } else {
+          $flattened_children[_nodehierarchy_get_parent_selector_value($inner)] = $inner;
         }
-        // This will be the link to be output in the next iteration.
-        $previous_element = $item;
-      }
-      // The submenu ended with the previous item, so pass back the current item.
-      else {
-        $remnant = $item;
-        break;
       }
     }
+    $here['children'] = $flattened_children;
   }
-  if ($previous_element && !$previous_element['disabled']) {
-    // We have one more link dangling.
-    $tree[_nodehierarchy_get_parent_selector_value($previous_element)] = $previous_element;
-  }
-  return $enabled_tree ? array($remnant, $tree) : array($remnant, array());
+  return array($here, $item); // $item is the unprocessed item that made the recursion fall out one level
 }
 
+
 /**
  * Get the title of the given item to display in a pulldown.
  */
