Index: wp-includes/template-functions-category.php
===================================================================
--- wp-includes/template-functions-category.php	(revision 3671)
+++ wp-includes/template-functions-category.php	(working copy)
@@ -1,5 +1,17 @@
 <?php
 
+function walk_category_tree() {
+	$walker = new Walker_Category;
+	$args = func_get_args();
+	return call_user_func_array(array(&$walker, 'walk'), $args);
+}
+
+function walk_category_dropdown_tree() {
+	$walker = new Walker_CategoryDropdown;
+	$args = func_get_args();
+	return call_user_func_array(array(&$walker, 'walk'), $args);
+}
+
 function get_the_category($id = false) {
 global $post, $category_cache;
 
@@ -185,7 +197,7 @@
 		else
 			$depth = -1; // Flat.
 
-		$output .= walk_category_tree($categories, $depth, '_category_dropdown_element', '', '', '', $selected, $r);
+		$output .= walk_category_tree($categories, $depth, $selected, $r);
 		$output .= "</select>\n";
 	}
 
@@ -197,26 +209,6 @@
 	return $output;
 }
 
-function _category_dropdown_element($output, $category, $depth, $selected, $args) {
-	$pad = str_repeat('&nbsp;', $depth * 3);
-
-	$cat_name = apply_filters('list_cats', $category->cat_name, $category);
-	$output .= "\t<option value=\"".$category->cat_ID."\"";
-	if ( $category->cat_ID == $selected )
-		$output .= ' selected="selected"';
-	$output .= '>';
-	$output .= $cat_name;
-	if ( $args['show_count'] )
-		$output .= '&nbsp;&nbsp;('. $category->category_count .')';
-	if ( $args['show_last_update'] ) {
-		$format = 'Y-m-d';
-		$output .= '&nbsp;&nbsp;' . gmdate($format, $category->last_update_timestamp);
-	}
-	$output .= "</option>\n";
-
-	return $output;
-}
-
 function wp_list_categories($args = '') {
 	if ( is_array($args) )
 		$r = &$args;
@@ -251,7 +243,7 @@
 		else
 			$depth = -1; // Flat.
 
-		$output .= walk_category_tree($categories, $depth, '_category_list_element_start', '_category_list_element_end', '_category_list_level_start', '_category_list_level_end', $current_category, $r);
+		$output .= walk_category_tree($categories, $depth, $current_category, $r);
 	}
 
 	if ( $title_li && $list )
@@ -260,88 +252,6 @@
 	echo apply_filters('list_cats', $output);
 }
 
-function _category_list_level_start($output, $depth, $cat, $args) {
-	if ( 'list' != $args['style'] )
-		return $output;
-
-	$indent = str_repeat("\t", $depth);
-	$output .= "$indent<ul class='children'>\n";
-	return $output;
-}
-
-function _category_list_level_end($output, $depth, $cat, $args) {
-	if ( 'list' != $args['style'] )
-		return $output;
-
-	$indent = str_repeat("\t", $depth);
-	$output .= "$indent</ul>\n";
-	return $output;
-}
-
-function _category_list_element_start($output, $category, $depth, $current_category, $args) {
-	extract($args);
-
-	$link = '<a href="' . get_category_link($category->cat_ID) . '" ';
-	if ( $use_desc_for_title == 0 || empty($category->category_description) )
-		$link .= 'title="'. sprintf(__("View all posts filed under %s"), wp_specialchars($category->cat_name)) . '"';
-	else
-		$link .= 'title="' . wp_specialchars(apply_filters('category_description',$category->category_description,$category)) . '"';
-	$link .= '>';
-	$link .= apply_filters('list_cats', $category->cat_name, $category).'</a>';
-
-	if ( (! empty($feed_image)) || (! empty($feed)) ) {
-		$link .= ' ';
-
-		if ( empty($feed_image) )
-			$link .= '(';
-
-		$link .= '<a href="' . get_category_rss_link(0, $category->cat_ID, $category->category_nicename) . '"';
-
-		if ( !empty($feed) ) {
-			$title = ' title="' . $feed . '"';
-			$alt = ' alt="' . $feed . '"';
-			$name = $feed;
-			$link .= $title;
-		}
-
-		$link .= '>';
-
-		if ( !empty($feed_image) )
-			$link .= "<img src='$feed_image' $alt$title" . ' />';
-		else
-			$link .= $name;
-		$link .= '</a>';
-		if (empty($feed_image))
-			$link .= ')';
-	}
-
-	if ( $show_count )
-		$link .= ' ('.intval($category->category_count).')';
-
-	if ( $show_date ) {
-		$link .= ' ' . gmdate('Y-m-d', $category->last_update_timestamp);
-	}
-
-	if ( 'list' == $args['style'] ) {
-		$output .= "\t<li";
-		if ( ($category->cat_ID == $current_category) && is_category() )
-			$output .=  ' class="current-cat"';
-		$output .= ">$link\n";
-	} else {
-		$output .= "\t$link<br />\n";
-	}
-
-	return $output;
-}
-
-function _category_list_element_end($output, $category, $depth, $cat, $args) {
-	if ( 'list' != $args['style'] )
-		return $output;
-
-	$output .= "</li>\n";
-	return $output;
-}
-
 function in_category($category) { // Check if the current post is in the given category
 	global $category_cache, $post;
 
Index: wp-includes/template-functions-post.php
===================================================================
--- wp-includes/template-functions-post.php	(revision 3671)
+++ wp-includes/template-functions-post.php	(working copy)
@@ -278,6 +278,11 @@
 Pages
 */
 
+function walk_page_tree() {
+	$walker = new Walker_Page;
+	$args = func_get_args();
+	return call_user_func_array(array(&$walker, 'walk'), $args);
+}
 
 function &get_page_children($page_id, $pages) {
 	global $page_cache;
@@ -380,7 +385,7 @@
 
 	if ( ! empty($pages) ) {
 		$output = "<select name='$name'>\n";
-		$output .= walk_page_tree($pages, $depth, '_page_dropdown_element', '', '', '', $selected);
+		$output .= walk_page_tree($pages, $depth, $selected);
 		$output .= "</select>\n";
 	}
 
@@ -427,7 +432,7 @@
 
 		global $wp_query;
 		$current_page = $wp_query->get_queried_object_id();
-		$output .= walk_page_tree($pages, $depth, '_page_list_element_start', '_page_list_element_end', '_page_list_level_start', '_page_list_level_end', $current_page, $r['show_date'], $r['date_format']);
+		$output .= walk_page_tree($pages, $depth, $current_page, $r['show_date'], $r['date_format']);
 
 		if ( $r['title_li'] )
 			$output .= '</ul></li>';
Index: wp-includes/walk-tree.php
===================================================================
--- wp-includes/walk-tree.php	(revision 0)
+++ wp-includes/walk-tree.php	(revision 0)
@@ -0,0 +1,262 @@
+<?php
+
+// A class for displaying various tree-like structures. Extend the Walker class to use it, see examples at the bottom
+
+class Walker {	
+	var $tree_type;
+	var $db_fields;
+	
+	//abstract callbacks
+	function start_lvl() { }
+	function end_lvl()   { }
+	function start_el()  { }
+	function end_el()    { }
+	
+	function walk($elements, $to_depth) {
+		$args = array_slice(func_get_args(), 2); $parents = array(); $depth = 1; $previous_element = ''; $output = '';
+	
+		//padding at the end
+		$last_element->post_parent = 0;
+		$last_element->post_id = 0;
+		$elements[] = $last_element;
+	
+		$id_field = $this->db_fields['id'];
+		$parent_field = $this->db_fields['parent'];
+	
+		$flat = ($to_depth == -1) ? true : false;
+	
+		foreach ( $elements as $element ) {
+			// If flat, start and end the element and skip the level checks.
+			if ( $flat) {
+				// Start the element.
+				if ( $element->$id_field != 0 ) {
+					$cb_args = array_merge( array($output, $element, $depth), $args);
+					$output = call_user_func_array(array(&$this, 'start_el'), $cb_args);
+				}
+	
+				// End the element.
+				if ( $element->$id_field != 0 ) {
+					$cb_args = array_merge( array($output, $element, $depth), $args);
+					$output = call_user_func_array(array(&$this, 'end_el'), $cb_args);
+				}
+	
+				continue;	
+			}
+	
+			// Walk the tree.
+			if ( !empty($previous_element) && ($element->$parent_field == $previous_element->$id_field) ) {
+				// Previous element is my parent. Descend a level.
+				array_unshift($parents, $previous_element);
+				$depth++; //always do this so when we start the element further down, we know where we are
+				if ( !$to_depth || ($depth < $to_depth) ) { //only descend if we're below $to_depth
+					$cb_args = array_merge( array($output, $depth), $args);
+					$output = call_user_func_array(array(&$this, 'start_lvl'), $cb_args);
+				}
+			} else if ( $element->$parent_field == $previous_element->$parent_field) {
+				// On the same level as previous element.
+				if ( !$to_depth || ($depth <= $to_depth) ) {
+					$cb_args = array_merge( array($output, $previous_element, $depth), $args);
+					$output = call_user_func_array(array(&$this, 'end_el'), $cb_args);
+				}
+			} else if ( $depth > 1 ) {
+				// Ascend one or more levels.
+				if ( !$to_depth || ($depth <= $to_depth) ) {
+					$cb_args = array_merge( array($output, $previous_element, $depth), $args);
+					$output = call_user_func_array(array(&$this, 'end_el'), $cb_args);
+				}
+	
+				while ( $parent = array_shift($parents) ) {
+					$depth--;
+					if ( !$to_depth || ($depth < $to_depth) ) {
+						$cb_args = array_merge( array($output, $depth), $args);
+						$output = call_user_func_array(array(&$this, 'end_lvl'), $cb_args);
+						$cb_args = array_merge( array($output, $parent, $depth), $args);
+						$output = call_user_func_array(array(&$this, 'end_el'), $cb_args);
+					}
+					if ( $element->$parent_field == $parents[0]->$id_field ) {
+						break;
+					}
+				}
+			} else if ( !empty($previous_element) ) {
+				// Close off previous element.
+				if ( !$to_depth || ($depth <= $to_depth) ) {
+					$cb_args = array_merge( array($output, $previous_element, $depth), $args);
+					$output = call_user_func_array(array(&$this, 'end_el'), $cb_args);
+				}
+			}
+	
+			// Start the element.
+			if ( !$to_depth || ($depth <= $to_depth) ) {
+				if ( $element->$id_field != 0 ) {
+					$cb_args = array_merge( array($output, $element, $depth), $args);
+					$output = call_user_func_array(array(&$this, 'start_el'), $cb_args);
+				}
+			}
+	
+			$previous_element = $element;
+		}
+		
+		return $output;
+	}
+}
+
+class Walker_Page extends Walker {
+	var $tree_type = 'page';
+	var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID'); //TODO: decouple this
+	
+	function start_lvl($output, $depth) {
+		$indent = str_repeat("\t", $depth);
+		$output .= "$indent<ul>\n";
+		return $output;
+	}
+	
+	function end_lvl($output, $depth) {
+		$indent = str_repeat("\t", $depth);
+		$output .= "$indent</ul>\n";
+		return $output;
+	}
+	
+	function start_el($output, $page, $depth, $current_page, $show_date, $date_format) {
+		if ( $depth )
+			$indent = str_repeat("\t", $depth);
+
+		$css_class = 'page_item';
+		if ( $page->ID == $current_page )
+			$css_class .= ' current_page_item';
+
+		$output .= $indent . '<li class="' . $css_class . '"><a href="' . get_page_link($page->ID) . '" title="' . wp_specialchars($page->post_title) . '">' . $page->post_title . '</a>';
+	
+		if ( !empty($show_date) ) {
+			if ( 'modified' == $show_date )
+				$time = $page->post_modified;
+			else
+				$time = $page->post_date;
+	
+			$output .= " " . mysql2date($date_format, $time);
+		}
+
+		return $output;
+	}
+	
+	function end_el($output, $page, $depth) {
+		$output .= "</li>\n";
+
+		return $output;
+	}
+
+}
+
+class Walker_Category extends Walker {
+	var $tree_type = 'category';
+	var $db_fields = array ('parent' => 'category_parent', 'id' => 'cat_ID'); //TODO: decouple this
+	
+	function start_lvl($output, $depth, $args) {
+		if ( 'list' != $args['style'] )
+			return $output;
+	
+		$indent = str_repeat("\t", $depth);
+		$output .= "$indent<ul class='children'>\n";
+		return $output;
+	}
+	
+	function end_lvl($output, $depth, $args) {
+		if ( 'list' != $args['style'] )
+			return $output;
+	
+		$indent = str_repeat("\t", $depth);
+		$output .= "$indent</ul>\n";
+		return $output;
+	}
+	
+	function start_el($output, $category, $depth, $current_category, $args) {
+		extract($args);
+	
+		$link = '<a href="' . get_category_link($category->cat_ID) . '" ';
+		if ( $use_desc_for_title == 0 || empty($category->category_description) )
+			$link .= 'title="'. sprintf(__("View all posts filed under %s"), wp_specialchars($category->cat_name)) . '"';
+		else
+			$link .= 'title="' . wp_specialchars(apply_filters('category_description',$category->category_description,$category)) . '"';
+		$link .= '>';
+		$link .= apply_filters('list_cats', $category->cat_name, $category).'</a>';
+	
+		if ( (! empty($feed_image)) || (! empty($feed)) ) {
+			$link .= ' ';
+	
+			if ( empty($feed_image) )
+				$link .= '(';
+	
+			$link .= '<a href="' . get_category_rss_link(0, $category->cat_ID, $category->category_nicename) . '"';
+	
+			if ( !empty($feed) ) {
+				$title = ' title="' . $feed . '"';
+				$alt = ' alt="' . $feed . '"';
+				$name = $feed;
+				$link .= $title;
+			}
+	
+			$link .= '>';
+	
+			if ( !empty($feed_image) )
+				$link .= "<img src='$feed_image' $alt$title" . ' />';
+			else
+				$link .= $name;
+			$link .= '</a>';
+			if (empty($feed_image))
+				$link .= ')';
+		}
+	
+		if ( $show_count )
+			$link .= ' ('.intval($category->category_count).')';
+	
+		if ( $show_date ) {
+			$link .= ' ' . gmdate('Y-m-d', $category->last_update_timestamp);
+		}
+	
+		if ( 'list' == $args['style'] ) {
+			$output .= "\t<li";
+			if ( ($category->cat_ID == $current_category) && is_category() )
+				$output .=  ' class="current-cat"';
+			$output .= ">$link\n";
+		} else {
+			$output .= "\t$link<br />\n";
+		}
+	
+		return $output;
+	}
+	
+	function end_el($output, $page, $depth) {
+		if ( 'list' != $args['style'] )
+			return $output;
+	
+		$output .= "</li>\n";
+		return $output;
+	}
+
+}
+
+class Walker_CategoryDropdown extends Walker {
+	var $tree_type = 'category';
+	var $db_fields = array ('parent' => 'category_parent', 'id' => 'cat_ID'); //TODO: decouple this
+	
+	function start_el($output, $category, $depth, $current_category, $args) { 
+		$pad = str_repeat('&nbsp;', $depth * 3);
+		
+		$cat_name = apply_filters('list_cats', $category->cat_name, $category);
+		$output .= "\t<option value=\"".$category->cat_ID."\"";
+		if ( $category->cat_ID == $selected )
+			$output .= ' selected="selected"';
+		$output .= '>';
+		$output .= $cat_name;
+		if ( $args['show_count'] )
+			$output .= '&nbsp;&nbsp;('. $category->category_count .')';
+		if ( $args['show_last_update'] ) {
+			$format = 'Y-m-d';
+			$output .= '&nbsp;&nbsp;' . gmdate($format, $category->last_update_timestamp);
+		}
+		$output .= "</option>\n";
+		
+		return $output;
+	}
+}
+
+?>
\ No newline at end of file
Index: wp-includes/functions.php
===================================================================
--- wp-includes/functions.php	(revision 3671)
+++ wp-includes/functions.php	(working copy)
@@ -705,127 +705,6 @@
 	}
 }
 
-function walk_tree($tree_type, $elements, $to_depth, $start_element_callback, $end_element_callback = '', $start_level_callback = '', $end_level_callback = '') {
-	$args = array_slice(func_get_args(), 7);
-	$parents = array();
-	$depth = 0;
-	$previous_element = '';
-	$output = '';
-
-	$last_element->post_parent = 0;
-	$last_element->post_id = 0;
-	$elements[] = $last_element;
-
-	if ( 'page' == $tree_type ) {
-		$parent_field = 'post_parent';
-		$id_field = 'ID';	
-	} else {
-		$parent_field = 'category_parent';
-		$id_field = 'cat_ID';			
-	}
-
-	$flat = false;
-	if ( $to_depth == -1 )
-		$flat = true;
-
-	foreach ( $elements as $element ) {
-		// If flat, start and end the element and skip the level checks.
-		if ( $flat) {
-			// Start the element.
-			if ( !empty($start_element_callback) && ($element->$id_field != 0) ) {
-				$cb_args = array_merge( array($output, $element, $depth), $args);
-				$output = call_user_func_array($start_element_callback, $cb_args);
-			}
-
-			// End the element.
-			if ( !empty($end_element_callback) && ($element->$id_field != 0) ) {
-				$cb_args = array_merge( array($output, $element, $depth), $args);
-				$output = call_user_func_array($end_element_callback, $cb_args);
-			}
-
-			continue;	
-		}
-
-		// Walk the tree.
-		if ( !empty($previous_element) && ($element->$parent_field == $previous_element->$id_field) ) {
-			// Previous element is my parent. Descend a level.
-			array_unshift($parents, $previous_element);
-			$depth++;
-			if ( !$to_depth || ($depth < $to_depth) )
-				if ( !empty($start_level_callback) ) {
-					$cb_args = array_merge( array($output, $depth), $args);
-					$output = call_user_func_array($start_level_callback, $cb_args);
-				}
-		} else if ( $depth && ($element->$parent_field == $previous_element->$parent_field) ) {
-			// On the same level as previous element.
-			if ( !$to_depth || ($depth < $to_depth) ) {
-				if ( !empty($end_element_callback) ) {
-					$cb_args = array_merge( array($output, $previous_element, $depth), $args);
-					$output = call_user_func_array($end_element_callback, $cb_args);
-				}
-			}
-		} else if ( $depth ) {
-			// Ascend one or more levels.
-			if ( !$to_depth || ($depth < $to_depth) ) {
-				if ( !empty($end_element_callback) ) {
-					$cb_args = array_merge( array($output, $previous_element, $depth), $args);
-					$output = call_user_func_array($end_element_callback, $cb_args);
-				}
-			}
-
-			while ( $parent = array_shift($parents) ) {
-				$depth--;
-				if ( !$to_depth || ($depth < $to_depth) ) {
-					if ( !empty($end_level_callback) ) {
-						$cb_args = array_merge( array($output, $depth), $args);
-						$output = call_user_func_array($end_level_callback, $cb_args);
-					}
-					if ( !empty($end_element_callback) ) {
-						$cb_args = array_merge( array($output, $parent, $depth), $args);
-						$output = call_user_func_array($end_element_callback, $cb_args);
-					}
-				}
-				if ( $element->$parent_field == $parents[0]->$id_field ) {
-					break;
-				}
-			}
-		} else if ( !empty($previous_element) ) {
-			// Close off previous element.
-			if ( !$to_depth || ($depth < $to_depth) ) {
-				if ( !empty($end_element_callback) ) {
-					$cb_args = array_merge( array($output, $previous_element, $depth), $args);
-					$output = call_user_func_array($end_element_callback, $cb_args);
-				}
-			}
-		}
-
-		// Start the element.
-		if ( !$to_depth || ($depth < $to_depth) ) {
-			if ( !empty($start_element_callback) && ($element->$id_field != 0) ) {
-				$cb_args = array_merge( array($output, $element, $depth), $args);
-				$output = call_user_func_array($start_element_callback, $cb_args);
-			}
-		}
-
-		$previous_element = $element;
-	}
-
-	return $output;
-}
-
-function walk_page_tree($pages, $to_depth, $start_element_callback, $end_element_callback = '', $start_level_callback = '', $end_level_callback = '') {
-	$args = array('page', $pages, $to_depth, $start_element_callback, $end_element_callback, $start_level_callback, $end_level_callback);
-	$extra_args = array_slice(func_get_args(), 6);
-
-	return call_user_func_array('walk_tree', array_merge($args, $extra_args));
-}
-
-function walk_category_tree($pages, $to_depth, $start_element_callback, $end_element_callback = '', $start_level_callback = '', $end_level_callback = '') {
-	$args = array('category', $pages, $to_depth, $start_element_callback, $end_element_callback, $start_level_callback, $end_level_callback);
-	$extra_args = array_slice(func_get_args(), 6);
-	return call_user_func_array('walk_tree', array_merge($args, $extra_args));
-}
-
 function get_category_by_path($category_path, $full_match = true, $output = OBJECT) {
 	global $wpdb;
 	$category_path = rawurlencode(urldecode($category_path));
Index: wp-settings.php
===================================================================
--- wp-settings.php	(revision 3671)
+++ wp-settings.php	(working copy)
@@ -129,6 +129,7 @@
 $wpdb->show_errors();
 
 require (ABSPATH . WPINC . '/functions-formatting.php');
+require (ABSPATH . WPINC . '/walk-tree.php');
 require (ABSPATH . WPINC . '/functions-post.php');
 require (ABSPATH . WPINC . '/capabilities.php');
 require (ABSPATH . WPINC . '/classes.php');
