=== added file 'modules/system/system.pages.inc'
--- modules/system/system.pages.inc	1970-01-01 00:00:00 +0000
+++ modules/system/system.pages.inc	2007-11-29 06:38:12 +0000
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * The AHAH callback for AHAH callbacks simulating a button press.
+ */
+function system_ahah_update_button() {
+  $form_state = array('storage' => NULL, 'submitted' => FALSE);
+  $form_build_id = $_POST['form_build_id'];
+  $form = form_get_cache($form_build_id, &$form_state);
+  // #parameters has $form_id, $form_state and then whatever was passed to
+  // drupal_get_form.
+  $args = $form['#parameters'];
+  $form_id = array_shift($args);
+  $form['#post'] = $_POST;
+  $form['#redirect'] = FALSE;
+  // This will set up $form_state['clicked_button'] and
+  // $form_state['storage']['mlid'].
+  drupal_process_form($form_id, $form, $form_state);
+  // Recreate and re-cache the form.
+  $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);
+  // Pick up the parents of the pressed button.
+  $array_parents = $form_state['clicked_button']['#array_parents'];
+  // The last parent is the button itself, we need the wrapper instead.
+  $button_index = array_pop($array_parents);
+  while ($array_parents) {
+    $parent = array_shift($array_parents);
+    $form = $form[$parent];
+  }
+  // Remove the button.
+  unset($form[$button_index]);
+  // Render messages and selects.
+  /*
+  $form['#messages'] = theme('status_messages');
+  $output = drupal_render($form);
+  */
+  $output = drupal_render($form) . theme('status_messages');
+  drupal_json(array('status' => TRUE, 'data' => $output));
+}

=== modified file 'includes/common.inc'
--- includes/common.inc	2007-11-26 22:34:08 +0000
+++ includes/common.inc	2007-11-29 06:13:29 +0000
@@ -3048,6 +3048,9 @@ function drupal_common_themes() {
     'file' => array(
       'arguments' => array('element' => NULL),
     ),
+    'drilldown' => array(
+      'arguments' => array('element' => NULL),
+    ),
     'form_element' => array(
       'arguments' => array('element' => NULL, 'value' => NULL),
     ),

=== modified file 'includes/form.inc'
--- includes/form.inc	2007-11-26 16:36:42 +0000
+++ includes/form.inc	2007-11-29 06:13:29 +0000
@@ -103,7 +103,9 @@ function drupal_get_form($form_id) {
       // Caching is done past drupal_process_form so #process callbacks can
       // set #cache. By not sending the form state, we avoid storing
       // $form_state['storage'].
-      form_set_cache($form_build_id, $original_form, NULL);
+      $item = menu_get_item();
+      $form_state_to_store = isset($item['file']) ? array('router_item_file' => $item['file']) : NULL;
+      form_set_cache($form_build_id, $original_form, $form_state_to_store);
     }
   }
 
@@ -205,6 +207,9 @@ function drupal_rebuild_form($form_id, &
 function form_get_cache($form_build_id, &$form_state) {
   if ($cached = cache_get('form_'. $form_build_id, 'cache_form')) {
     $form = $cached->data;
+    if (!empty($cached->headers)) {
+      $form_state['router_item_file'] = $cached->headers;
+    }
     if ($cached = cache_get('storage_'. $form_build_id, 'cache_form')) {
       $form_state['storage'] = $cached->data;
     }
@@ -218,7 +223,7 @@ function form_get_cache($form_build_id, 
 function form_set_cache($form_build_id, $form, $form_state) {
   $expire = max(ini_get('session.cookie_lifetime'), 86400);
 
-  cache_set('form_'. $form_build_id, $form, 'cache_form', $expire);
+  cache_set('form_'. $form_build_id, $form, 'cache_form', $expire, isset($form_state['router_item_file']) ? $form_state['router_item_file'] : NULL);
   if (!empty($form_state['storage'])) {
     cache_set('storage_'. $form_build_id, $form_state['storage'], 'cache_form', $expire);
   }
@@ -305,6 +310,9 @@ function drupal_retrieve_form($form_id, 
   array_shift($args);
   if (isset($form_state)) {
     array_shift($args);
+    if (!empty($form_state['router_item_file'])) {
+      require_once($form_state['router_item_file']);
+    }
   }
 
   // We first check to see if there's a function named after the $form_id.
@@ -1715,7 +1723,7 @@ function form_expand_ahah($element) {
       'effect'   => empty($element['#ahah']['effect']) ? 'none' : $element['#ahah']['effect'],
       'method'   => empty($element['#ahah']['method']) ? 'replace' : $element['#ahah']['method'],
       'progress' => empty($element['#ahah']['progress']) ? array('type' => 'throbber') : $element['#ahah']['progress'],
-      'button'   => $element['#type'] == 'submit' ? array($element['#name'] => $element['#value']) : FALSE,
+      'button'   => isset($element['#executes_submit_callback']) ? array($element['#name'] => $element['#value']) : FALSE,
     );
 
     // Convert a simple #ahah[progress] type string into an array.
@@ -1819,6 +1827,55 @@ function expand_checkboxes($element) {
   return $element;
 }
 
+function form_process_drilldown($form) {
+  $form['#type'] = 'markup';
+  $form['wrapper'] = array(
+    '#type' => 'item',
+    '#title' => $form['#title'],
+    '#prefix' => '<div id="'. $form['#id'] .'-wrapper" class="drilldown-select clear-block">',
+    '#suffix' => '</div>',
+  );
+  $form['wrapper']['submit'] = array(
+    '#type' => 'button',
+    '#value' => $form['#button_value'],
+    '#ahah' => array(
+      'path' => 'js/ahah_update_button',
+      'wrapper' => $form['#id'] .'-wrapper',
+      'selector' => '#'. $form['#id'] .'-wrapper select',
+      'event' => 'change',
+      'progress' => 'none',
+    ),
+  );
+  return $form;
+}
+
+function form_process_drilldown_select($form, $edit, &$form_state) {
+  $parents = $form['#parents'];
+  $depth = array_pop($parents);
+  // Remove 'select'
+  array_pop($parents);
+  // Remove 'wrapper'
+  array_pop($parents);
+  $id = implode('-', $parents);
+  $form_state['drilldown_temp'][$id][$depth] = $form['#value'];
+  if ($form['#default_value'] != $form['#value']) {
+    if (!isset($form_state['drilldown'][$id]) || $form_state['drilldown'][$id]['depth'] < $depth) {
+      // We need to find the first non-zero value.
+      do {
+        $value = $form_state['drilldown_temp'][$id][$depth--];
+      } while ($depth && !$value);
+      $form_state['storage']['drilldown'][$id] = array('depth' => $depth + 1, 'value' => $value);
+    }
+  }
+  return $form;
+}
+
+function theme_drilldown($form) {
+  // No need to show the button if we have javascript.
+  drupal_add_js('if (Drupal.jsEnabled) { $(document).ready(function() { $("#'. $form['wrapper']['submit']['#id'] .'").css("display", "none"); }); }', 'inline');
+  return drupal_render($form);
+}
+
 function theme_submit($element) {
   return theme('button', $element);
 }

=== modified file 'includes/menu.inc'
--- includes/menu.inc	2007-11-26 16:19:37 +0000
+++ includes/menu.inc	2007-11-29 06:13:29 +0000
@@ -2210,3 +2210,75 @@ function _menu_site_is_offline() {
   }
   return FALSE;
 }
+
+function _menu_parent_select_recurse($tree, $original_item, $form_0 = NULL) {
+  static $form, $depth = 1;
+  $select = array(
+    '#type' => 'select',
+    '#default_value' => 0,
+    '#size' => 6,
+    '#multiple' => FALSE,
+    // We are running ----- through t() to make it easy to change.
+    '#options' => array(t('------')),
+    '#process' => array('form_process_drilldown_select'),
+  );
+  if (isset($form_0)) {
+    $form = array();
+    $form[0] = $form_0 + $select;
+    $form[1] = $select;
+    // Options are check_plain'd automatically.
+    $form[1]['#options'][0] = t('<Root>');
+  }
+  foreach ($tree as $item) {
+    if (isset($item['link'])) {
+      $link = $item['link'];
+      $depth = $link['depth'];
+      $mlid = $link['mlid'];
+      // Skip the current element and all its children and parents that
+      // would move the element too deep.
+      if ($mlid == $original_item['mlid'] || $depth > $original_item['limit']) {
+        continue;
+      }
+      if (!$link['hidden']) {
+        if (!isset($form[$depth])) {
+          $form[$depth] = $select;
+        }
+        $form[$depth]['#options'][$mlid] = $link['title'];
+        if (empty($original_item['fake']) && $link['in_active_trail']) {
+          $form[$depth]['#default_value'] = $mlid;
+        }
+      }
+    }
+    if (!empty($item['below'])) {
+      $depth++;
+      _menu_parent_select_recurse($item['below'], $original_item);
+      $depth--;
+    }
+  }
+  if ($depth == 1 && count($form[1]['#options']) == 1) {
+    unset($form[1]);
+  }
+  return $form;
+}
+
+function _menu_parent_submit($form) {
+  $plid = 0;
+  $max_depth = 0;
+  foreach (element_children($form) as $depth) {
+    $element = $form[$depth];
+    if ($element['#value'] && $depth > $max_depth) {
+      $plid = $element['#value'];
+      $max_depth = $depth;
+    }
+  }
+  $menu_name = $form[0]['#value'];
+  return array($menu_name, $plid);
+}
+
+/**
+ * Find the depth limit for items in the parent select.
+ */
+function _menu_parent_depth_limit($item) {
+  return MENU_MAX_DEPTH - 1 - ($item['mlid'] ? menu_link_children_relative_depth($item) : 0);
+}
+

=== modified file 'modules/menu/menu.admin.inc'
--- modules/menu/menu.admin.inc	2007-11-26 16:36:42 +0000
+++ modules/menu/menu.admin.inc	2007-11-29 06:13:29 +0000
@@ -222,7 +222,6 @@ function theme_menu_overview_form($form)
  * Menu callback; Build the menu link editing form.
  */
 function menu_edit_item(&$form_state, $type, $item, $menu) {
-
   $form['menu'] = array(
     '#type' => 'fieldset',
     '#title' => t('Menu settings'),
@@ -293,19 +292,12 @@ function menu_edit_item(&$form_state, $t
     '#description' => t('If selected and this menu item has children, the menu will always appear expanded.'),
   );
 
-  // Generate a list of possible parents (not including this item or descendants).
-  $options = menu_parent_options(menu_get_menus(), $item);
-  $default = $item['menu_name'] .':'. $item['plid'];
-  if (!isset($options[$default])) {
-    $default = 'navigation:0';
-  }
   $form['menu']['parent'] = array(
-    '#type' => 'select',
-    '#title' => t('Parent item'),
-    '#default_value' => $default,
-    '#options' => $options,
-    '#description' => t('The maximum depth for an item and all its children is fixed at !maxdepth. Some menu items may not be available as parents if selecting them would exceed this limit.', array('!maxdepth' => MENU_MAX_DEPTH)),
-    '#attributes' => array('class' => 'menu-title-select'),
+    '#type' => 'drilldown',
+    '#title' => t('Parent'),
+    '#button_value' => t('Update menu parent'),
+    '#item' => $item,
+    '#process' => array('form_process_drilldown', 'menu_parent_select'),
   );
   $form['menu']['weight'] = array(
     '#type' => 'weight',
@@ -354,7 +346,7 @@ function menu_item_delete_submit($form, 
 function menu_edit_item_submit($form, &$form_state) {
   $item = $form_state['values']['menu'];
   $item['options']['attributes']['title'] = $item['description'];
-  list($item['menu_name'], $item['plid']) = explode(':', $item['parent']);
+  list($item['menu_name'], $item['plid']) = _menu_parent_submit($form['menu']['parent']['wrapper']['select']);
   if (!menu_link_save($item)) {
     drupal_set_message(t('There was an error saving the menu link.'), 'error');
   }

=== modified file 'modules/menu/menu.module'
--- modules/menu/menu.module	2007-11-26 16:36:42 +0000
+++ modules/menu/menu.module	2007-11-29 13:26:24 +0000
@@ -136,7 +136,6 @@ function menu_menu() {
     'type' => MENU_CALLBACK,
     'file' => 'menu.admin.inc',
   );
-
   return $items;
 }
 
@@ -190,64 +189,6 @@ function menu_load($menu_name) {
 }
 
 /**
- * Return a list of menu items that are valid possible parents for the given menu item.
- *
- * @param $menus
- *   An array of menu names and titles, such as from menu_get_menus().
- * @param $item
- *   The menu item for which to generate a list of parents.
- *   If $item['mlid'] == 0 then the complete tree is returned.
- * @return
- *   An array of menu link titles keyed on the a string containing the menu name
- *   and mlid. The list excludes the given item and its children.
- */
-function menu_parent_options($menus, $item) {
-  // The menu_links table can be practically any size and we need a way to
-  // allow contrib modules to provide more scalable pattern choosers.
-  // hook_form_alter is too late in itself because all the possible parents are
-  // retrieved here, unless menu_override_parent_selector is set to TRUE.
-  if (variable_get('menu_override_parent_selector', FALSE)) {
-    return array();
-  }
-  // If the item has children, there is an added limit to the depth of valid parents.
-  if (isset($item['parent_depth_limit'])) {
-    $limit = $item['parent_depth_limit'];
-  }
-  else {
-    $limit = _menu_parent_depth_limit($item);
-  }
-
-  foreach ($menus as $menu_name => $title) {
-    $tree = menu_tree_all_data($menu_name, NULL);
-    $options[$menu_name .':0'] = '<'. $title .'>';
-    _menu_parents_recurse($tree, $menu_name, '--', $options, $item['mlid'], $limit);
-  }
-  return $options;
-}
-
-/**
- * Recursive helper function for menu_parent_options().
- */
-function _menu_parents_recurse($tree, $menu_name, $indent, &$options, $exclude, $depth_limit) {
-  foreach ($tree as $data) {
-    if ($data['link']['depth'] > $depth_limit) {
-      // Don't iterate through any links on this level.
-      break;
-    }
-    if ($data['link']['mlid'] != $exclude && $data['link']['hidden'] >= 0) {
-      $title = $indent .' '. truncate_utf8($data['link']['title'], 30, TRUE, FALSE);
-      if ($data['link']['hidden']) {
-        $title .= ' ('. t('disabled') .')';
-      }
-      $options[$menu_name .':'. $data['link']['mlid']] = $title;
-      if ($data['below']) {
-        _menu_parents_recurse($data['below'], $menu_name, $indent .'--', $options, $exclude, $depth_limit);
-      }
-    }
-  }
-}
-
-/**
  * Reset a system-defined menu item.
  */
 function menu_reset_item($item) {
@@ -307,6 +248,9 @@ function menu_nodeapi(&$node, $op) {
             drupal_set_message(t('There was an error saving the menu link.'), 'error');
           }
         }
+        elseif ($item['plid']) {
+          drupal_set_message(t('You have picked a menu parent but the title is empty. Menu item was not saved.'), 'error');
+        }
       }
       break;
     case 'delete':
@@ -344,13 +288,6 @@ function menu_nodeapi(&$node, $op) {
 }
 
 /**
- * Find the depth limit for items in the parent select.
- */
-function _menu_parent_depth_limit($item) {
-  return MENU_MAX_DEPTH - 1 - (($item['mlid'] && $item['has_children']) ? menu_link_children_relative_depth($item) : 0);
-}
-
-/**
  * Implementation of hook_form_alter(). Adds menu item fields to the node form.
  */
 function menu_form_alter(&$form, $form_state, $form_id) {
@@ -392,19 +329,12 @@ function menu_form_alter(&$form, $form_s
       '#description' => t('The link text corresponding to this item that should appear in the menu. Leave blank if you do not wish to add this post to the menu.'),
       '#required' => FALSE,
     );
-    // Generate a list of possible parents (not including this item or descendants).
-    $options = menu_parent_options(menu_get_menus(), $item);
-    $default = $item['menu_name'] .':'. $item['plid'];
-    if (!isset($options[$default])) {
-      $default = 'navigation:0';
-    }
     $form['menu']['parent'] = array(
-      '#type' => 'select',
-      '#title' => t('Parent item'),
-      '#default_value' => $default,
-      '#options' => $options,
-      '#description' => t('The maximum depth for an item and all its children is fixed at !maxdepth. Some menu items may not be available as parents if selecting them would exceed this limit.', array('!maxdepth' => MENU_MAX_DEPTH)),
-      '#attributes' => array('class' => 'menu-title-select'),
+      '#type' => 'drilldown',
+      '#title' => t('Parent'),
+      '#button_value' => t('Update menu parent'),
+      '#item' => $item,
+      '#process' => array('form_process_drilldown', 'menu_parent_select'),
     );
     $form['#submit'][] = 'menu_node_form_submit';
 
@@ -421,7 +351,7 @@ function menu_form_alter(&$form, $form_s
  * Decompose the selected menu parent option into the menu_name and plid.
  */
 function menu_node_form_submit($form, &$form_state) {
-  list($form_state['values']['menu']['menu_name'], $form_state['values']['menu']['plid']) = explode(':', $form_state['values']['menu']['parent']);
+  list($form_state['values']['menu']['menu_name'], $form_state['values']['menu']['plid']) = _menu_parent_submit($form['menu']['parent']['wrapper']['select']);
 }
 
 /**
@@ -481,3 +411,56 @@ function menu_valid_path($form_item) {
   $menu_admin = FALSE;
   return $item && $item['access'];
 }
+
+/**
+ * Creates a drilldown selector for a given menu link.
+ */
+function menu_parent_select($form, $edit, &$form_state) {
+  $original_item = array(
+    'mlid' => $form['#item']['mlid'],
+    'menu_name' => $form['#item']['menu_name'],
+    'limit' => _menu_parent_depth_limit($form['#item']),
+  );
+  $parents = $form['#parents'];
+  $id = implode('-', $parents);
+  if (isset($form_state['storage']['drilldown'][$id])) {
+    $plid = $form_state['storage']['drilldown'][$id]['value'];
+    unset($form_state['storage']['drilldown'][$id]);
+    if (empty($form_state['storage']['drilldown'])) {
+      unset($form_state['storage']['drilldown']);
+    }
+    $form['#collapsed'] = FALSE;
+    // If plid is not numeric, then it's a menu name.
+    if (!is_numeric($plid)) {
+      // We record the menu name in case it's an empty menu.
+      $original_item['menu_name'] = $plid;
+      $plid = 0;
+    }
+  }
+  else {
+    $plid = $form['#item']['plid'];
+  }
+  if ($plid) {
+    $item = menu_link_load($plid);
+  }
+  elseif ($mlid = db_result(db_query("SELECT mlid FROM {menu_links} WHERE plid = 0 AND menu_name = '%s'", $original_item['menu_name']))) {
+    $item = menu_link_load($mlid);
+    // We show the root items and yet we do not want to show this fake item
+    // as selected.
+    $original_item['fake'] = TRUE;
+  }
+  else {
+    $item = NULL;
+  }
+  if ($item) {
+    $original_item['menu_name'] = $item['menu_name'];
+    $tree = menu_tree_all_data($item['menu_name'], $item);
+  }
+  else {
+    $tree = array();
+    $limit = 0;
+  }
+  $form_0 = array('#options' => menu_get_menus(), '#default_value' => $original_item['menu_name']);
+  $form['wrapper']['select'] = _menu_parent_select_recurse($tree, $original_item, $form_0);
+  return $form;
+}

=== modified file 'modules/system/system-rtl.css'
--- modules/system/system-rtl.css	2007-11-27 12:09:25 +0000
+++ modules/system/system-rtl.css	2007-11-29 06:13:29 +0000
@@ -91,3 +91,7 @@ input.password-confirm {
   margin-left: 10px;
   margin-right: 0;
 }
+.drilldown-select select,
+.drilldown-select input {
+  float: right;
+}

=== modified file 'modules/system/system.css'
--- modules/system/system.css	2007-11-20 10:18:42 +0000
+++ modules/system/system.css	2007-11-29 06:13:29 +0000
@@ -538,3 +538,7 @@ span.password-confirm {
 span.password-confirm span {
   font-weight: normal;
 }
+.drilldown-select select,
+.drilldown-select input {
+  float: left;
+}

=== modified file 'modules/system/system.module'
--- modules/system/system.module	2007-11-26 16:36:42 +0000
+++ modules/system/system.module	2007-11-29 06:13:29 +0000
@@ -149,6 +149,7 @@ function system_elements() {
   $type['weight'] = array('#input' => TRUE, '#delta' => 10, '#default_value' => 0, '#process' => array('process_weight', 'form_expand_ahah'));
   $type['date'] = array('#input' => TRUE, '#process' => array('expand_date'), '#element_validate' => array('date_validate'));
   $type['file'] = array('#input' => TRUE, '#size' => 60);
+  $type['drilldown'] = array('#process' => array('form_process_drilldown'), '#input' => TRUE, '#tree' => TRUE, '#collapsible' => TRUE, '#collapsed' => FALSE, '#theme' => 'drilldown');
 
   // Form structure
   $type['item'] = array('#value' => '');
@@ -157,6 +158,7 @@ function system_elements() {
   $type['markup'] = array('#prefix' => '', '#suffix' => '');
   $type['fieldset'] = array('#collapsible' => FALSE, '#collapsed' => FALSE, '#value' => NULL, '#process' => array('form_expand_ahah'));
   $type['token'] = array('#input' => TRUE);
+
   return $type;
 }
 
@@ -456,6 +458,13 @@ function system_menu() {
     'type' => MENU_CALLBACK,
     'file' => 'system.admin.inc',
   );
+  $items['js/ahah_update_button'] = array(
+    'page callback' => 'system_ahah_update_button',
+    // Safe, as the callback acts on the form_build_id, which is a random.
+    'access callback' => TRUE,
+    'type' => MENU_CALLBACK,
+    'file' => 'system.pages.inc',
+  );
   return $items;
 }
 

