Index: content.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck/content.module,v
retrieving revision 1.139
diff -u -r1.139 content.module
--- content.module	13 Aug 2007 17:34:02 -0000	1.139
+++ content.module	16 Aug 2007 12:10:36 -0000
@@ -176,8 +176,8 @@
  * Each field defines its own component of the content entry form, via its
  * chosen widget.
  */
-function content_form(&$node) {
-  $form = array();
+function content_form(&$form, &$form_state) {
+  $node = $form['#node'];
   $type = content_types($node->type);
 
   // Set form parameters so we can accept file uploads.
@@ -185,35 +185,43 @@
     $form['#attributes'] = array("enctype" => "multipart/form-data");
   }
 
-  _content_widget_invoke('prepare form values', $node);
-  $form = array_merge($form, _content_widget_invoke('form', $node));
+  $type_name = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type);
+  $type = content_types($type_name);
+
+  if (count($type['fields'])) {
+    foreach ($type['fields'] as $field) {
+      $form = array_merge($form, content_form_field($form, $form_state, $field));
+    }
+  }
   return $form;
 }
 
 /**
- * Node form 'validate' handler.
+ * Create a separate form element for each field.
  */
-function content_node_form_validate($form, &$form_state) {
-  // TODO : the widget ops could probably go with new widget FAPIsation ?
-  $node = (object)$form_state['values'];
-  _content_widget_invoke('validate', $node);
-  _content_widget_invoke('process form values', $node);
-  $form_state['values'] = (array)$node;
-}
+function content_form_field(&$form, &$form_state, $field) {
+  $node = $form['#node'];
+  $items = isset($form_state['values']) ? $form_state['values'][$field['field_name']] : array();
 
-/**
- * Node form 'submit' handler.
- *
- * At submit time, the widget does whatever data massaging is necessary so that
- * the field has the content in the expected format and can commit the changes
- * to the database.
- */
-//
-function content_node_form_submit($form, &$form_state) {
-  // TODO : the widget ops could probably go with new widget FAPIsation ?
-  $node = (object)$form_state['values'];
-  _content_widget_invoke('submit', $node);
-  $form_state['values'] = (array)$node;
+  if (empty($items) || !is_array($items)) {
+    $items = array();
+  }
+  $widget_types = _content_widget_types();
+  $module = $widget_types[$field['widget']['type']]['module'];
+  $function = $module .'_widget';
+  if (function_exists($function)) {
+    // If we're building a node creation form, pre-fill with default values
+    if (empty($node->nid)) {
+      if (isset($widget_types['callbacks']) && !empty($widget_types['callbacks']['default_value'])) {
+        $function = $widget_types[$widget['type']]['callbacks']['default_value'];
+        if (!empty($function) && function_exists($function)) {
+          $items = array_merge($items, $function($node, $field, $items));
+        }
+      }
+    }
+    $result = $function($node, $field, $items);
+  }
+  return $result;
 }
 
 /**
@@ -347,11 +355,8 @@
  */
 function content_form_alter(&$form, $form_state, $form_id) {
   if (isset($form['type'])) {
-    $node = $form['#node'];
     if ($form['type']['#value'] .'_node_form' == $form_id) {
-      $form = array_merge($form, content_form($node));
-      $form['#validate'][] = 'content_node_form_validate';
-      $form['#submit'][] = 'content_node_form_submit';
+      $form = array_merge($form, content_form($form, $form_state));
     }
   }
 }
@@ -631,47 +636,6 @@
 }
 
 /**
- * Invoke a widget hook.
- */
-function _content_widget_invoke($op, &$node) {
-  $type_name = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type);
-  $type = content_types($type_name);
-  $widget_types = _content_widget_types();
-
-  $return = array();
-  if (count($type['fields'])) {
-    foreach ($type['fields'] as $field) {
-      $node_field = isset($node->$field['field_name']) ? $node->$field['field_name'] : array();
-
-      $module = $widget_types[$field['widget']['type']]['module'];
-      $function = $module .'_widget';
-      if (function_exists($function)) {
-        // If we're building a node creation form, pre-fill with default values
-        if ($op == 'prepare form values' && empty($node->nid)) {
-          $node_field = array_merge($node_field, content_default_value($node, $field, $node_field));
-        }
-
-        $result = $function($op, $node, $field, $node_field);
-        if (is_array($result) && $op == 'form') {
-          $result[$field['field_name']]['#weight'] = $field['widget']['weight'];
-        }
-        if (is_array($result)) {
-          $return = array_merge($return, $result);
-        }
-        else if (isset($result)) {
-          $return[] = $result;
-        }
-      }
-      // test for values in $node_field in case modules added items
-      if (is_object($node) && (isset($node->$field['field_name']) || count($node_field))) {
-        $node->$field['field_name'] = $node_field;
-      }
-    }
-  }
-  return $return;
-}
-
-/**
  * Return a list of all content types.
  *
  * @param $content_type_name
@@ -1062,7 +1026,7 @@
 /**
  *  Helper function for determining the behaviour of a field or a widget
  *  with respect to a given operation
- *  (currently only used for 'view' field op and 'default value' widegt op)
+ *  (currently only used for 'view' field op)
  *
  *  @param $entity
  *    'field' or 'widget'
@@ -1083,15 +1047,8 @@
   $entity_types = ($entity == 'field') ? _content_field_types() : _content_widget_types();
   $entity_type = ($entity == 'field') ? $field['type'] : $field['widget']['type'];
   $module = $entity_types[$entity_type]['module'];
-
-  if ($op == 'default value' && $module == 'computed_field') {
-    $callback_value =   CONTENT_CALLBACK_NONE;
-  }
-  else {
-    $callbacks = module_invoke($module, "{$entity}_settings", 'callbacks', $field);
-    $callback_value = (isset($callbacks[$op])) ? $callbacks[$op] : CONTENT_CALLBACK_DEFAULT;
-  }
-
+  $callbacks = module_invoke($module, "{$entity}_settings", 'callbacks', $field);
+  $callback_value = (isset($callbacks[$op])) ? $callbacks[$op] : CONTENT_CALLBACK_DEFAULT;
   return $callback_value;
 }
 
@@ -1115,11 +1072,7 @@
   $module = $widget_types[$field['widget']['type']]['module'];
 
   $default_value = array();
-  if (content_handle('widget', 'default value', $field) == CONTENT_CALLBACK_CUSTOM) {
-    $function = $module .'_widget';
-    $default_value = $function('default value', $node, $field, $items);
-  }
-  elseif (!empty($field['widget']['default_value_php'])) {
+  if (!empty($field['widget']['default_value_php'])) {
     ob_start();
     $result = eval($field['widget']['default_value_php']);
     ob_end_clean();
Index: content_admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck/content_admin.inc,v
retrieving revision 1.76
diff -u -r1.76 content_admin.inc
--- content_admin.inc	10 Aug 2007 02:02:39 -0000	1.76
+++ content_admin.inc	16 Aug 2007 12:39:20 -0000
@@ -100,7 +100,6 @@
     // Limiting weight to < 10 will keep workflow and submit elements from being added to the overview table.
     // They're outside the weight range allowed for CCK fields, so won't interfere with field placement.
     // Currently the 'menu' fieldset is at weight -2, so add a special check for that one.
-
     if (is_array($value) && (isset($value['#weight']) || $key == 'body_filter') && $key != 'menu' && $value['#weight'] <= 10) {
 
       // if this item is a group, insert group info into table, then add all the group fields below it
@@ -792,55 +791,7 @@
   // Add handling for default value if not provided by field.
   if (content_handle('widget', 'default value', $field) == CONTENT_CALLBACK_DEFAULT) {
     $form['#attributes'] = array("enctype" => "multipart/form-data");
-    $form['widget']['default_value_fieldset'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Default value'),
-      '#collapsible' => TRUE,
-      '#collapsed' => TRUE,
-    );
-    $default_value = isset($field['widget']['default_value']) ? $field['widget']['default_value'] : array();
-    $module = $widget_types[$field['widget']['type']]['module'];
-    $function = $module .'_widget';
-    if (function_exists($function)) {
-      $node = array(); // TODO are there things we need to add in here ?
-      // Make sure the default value is not a required field.
-      $widget_field = $field;
-      $widget_field['required'] = FALSE;
-      $function('prepare form values', $node, $widget_field, $default_value);
-      $form_element = $function('form', $node, $widget_field, $default_value);
-    }
-    else {
-      // TODO : generate a series of textfields ?
-      // why would a widget not have a hook_widget implementation ?
-    }
-    $form['widget']['default_value_fieldset']['default_value_widget'] = $form_element;
-    $form['widget']['default_value_fieldset']['default_value_widget']['#tree'] = TRUE;
-    $form['widget']['default_value_fieldset']['advanced_options'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Php code'),
-      '#collapsible' => TRUE,
-      '#collapsed' => empty($field['widget']['default_value_php']),
-    );
-    $db_info = content_database_info($field);
-    $columns = array_keys($db_info['columns']);
-    foreach ($columns as $key => $column) {
-      $columns[$key] = "'$column' => value for $column";
-    }
-    $sample = 'array(
-  0 => array('. implode(', ', $columns) .'),
-  // You\'ll usually want to stop here. Provide more values
-  // if you want your \'default value\' to be multi-valued :
-  1 => array('. implode(', ', $columns) .'),
-  2 => ...
-);';
-    $form['widget']['default_value_fieldset']['advanced_options']['default_value_php'] = array(
-      '#type' => 'textarea',
-      '#title' => t('Code'),
-      '#default_value' => isset($field['widget']['default_value_php']) ? $field['widget']['default_value_php'] : '',
-      '#rows' => 6,
-      '#tree' => TRUE,
-      '#description' => t('Advanced Usage Only: PHP code that returns a default value. Should not include &lt;?php ?&gt; delimiters. If this field is filled out, the value returned by this code will override any value specified above. Expected format :<pre>!sample</pre>Using !link_devel\'s \'devel load\' tab on a %type content page might help you figure out the expected format.', array('!sample' => $sample, '!link_devel' => l('devel.module', 'http://www.drupal.org/project/devel'), '%type' => $type_name)),
-    );
+    $form['widget'] = array_merge($form['widget'], content_default_value_widget($field));
   }
 
   $form['field'] = array(
@@ -900,86 +851,8 @@
   $widget_types = _content_widget_types();
   $widget_type = $widget_types[$field['widget']['type']];
 
-  module_invoke($widget_type['module'], 'widget_settings', 'validate', array_merge($field, $form_values));
   module_invoke($field_type['module'], 'field_settings', 'validate', array_merge($field, $form_values));
 
-  // If content.module is handling the default value,
-  // validate the result using the field validation.
-  if (content_handle('widget', 'default value', $field) == CONTENT_CALLBACK_DEFAULT) {
-    if (isset($form_values['default_value_php']) && ($php = trim($form_values['default_value_php']))) {
-      ob_start();
-      $return = eval($php);
-      ob_end_clean();
-      if (!is_array($return)) {
-        $error = TRUE;
-      }
-      else {
-        foreach ($return as $item) {
-          if (!is_array($item)) {
-            $error = TRUE;
-            break;
-          }
-        }
-      }
-      if ($error) {
-        $db_info = content_database_info($field);
-        $columns = array_keys($db_info['columns']);
-        foreach ($columns as $key => $column) {
-          $columns[$key] = "'$column' => value for $column";
-        }
-        $sample = 'array(
-  0 => array('. implode(', ', $columns) .'),
-  // You\'ll usually want to stop here. Provide more values
-  // if you want your \'default value\' to be multi-valued :
-  1 => array('. implode(', ', $columns) .'),
-  2 => ...
-);';
-        form_set_error('default_value_php', t('The default value php code returned an incorrect value<br/>Expected format : <pre>!sample</pre>Returned value : @value', array('!sample' => $sample, '@value' => print_r($return, true))));
-        return;
-      }
-      else {
-        $default_value = $return;
-        $is_code = TRUE;
-        form_set_value(array('#parents' => array('default_value_php')), $php, $form_state);
-        form_set_value(array('#parents' => array('default_value')), array(), $form_state);
-      }
-    }
-    else {
-      $default_value = $form_values['default_value_widget'][$field['field_name']];
-      $is_code = FALSE;
-      form_set_value(array('#parents' => array('default_value_php')), '', $form_state);
-      form_set_value(array('#parents' => array('default_value')), $default_value, $form_state);
-    }
-    if (isset($default_value)) {
-      $node = array();
-      $node[$form_values['field_name']] = $default_value;
-      $field['required'] = FALSE;
-      $field_function = $field_type['module'] .'_field';
-      $widget_function = $widget_type['module'] .'_widget';
-
-      // If default_value is created from php code, don't run it through widget processing.
-      // This way the default value code can directly create the correct array without being
-      // mangled by widget processing which sometimes requires an input array in a completely different format.
-      if (function_exists($widget_function) && !$is_code) {
-        $widget_function('validate', $node, $field, $default_value);
-        $widget_function('process form values', $node, $field, $default_value);
-        // The widget processing may have altered the value, save it to be sure.
-        form_set_value(array('#parents' => array('default_value')), $default_value, $form_state);
-      }
-      if (function_exists($field_function)) {
-        $field_function('validate', $node, $field, $default_value, NULL, NULL);
-      }
-      // The field validation routine won't set an error on the right field, so set it here.
-      if (form_get_errors()) {
-        if (trim($form_values['default_value_php'])) {
-          form_set_error('default_value_php', t('The default value php code created @value which is invalid.', array('@value' => print_r($default_value, true))));
-        }
-        else {
-          form_set_error('default_value', t('The default value is invalid.'));
-        }
-      }
-    }
-  }
 }
 
 /**
@@ -1052,6 +925,138 @@
 }
 
 /**
+ * Create a form element to input a default value in widget settings.
+ */
+function content_default_value_widget($field) {
+  $form['default_value_fieldset'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Default value'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+  // TODO are there things we need to add in here ?
+  $node = array();
+  $widget_field = $field;
+  $widget_field['required'] = FALSE; // Make sure the default value is not a required field.
+  $default_value = isset($field['widget']['default_value']) ? $field['widget']['default_value'] : array();
+  $widget_form_state = array('values' => array($field['field_name'] => $default_value));
+  $widget_form = array('#node' => (object) $node, $field['field_name'] => array());
+
+  $form['default_value_fieldset']['default_value_widget'] = content_form_field($widget_form, $widget_form_state, $widget_field);
+  $form['default_value_fieldset']['default_value_widget']['#tree'] = TRUE;
+  $form['default_value_fieldset']['default_value_widget']['#element_validate'][] = 'content_default_value_widget_validate';
+  $form['default_value_fieldset']['default_value_widget']['#field'] = $widget_field;
+  $form['default_value_fieldset']['advanced_options'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Php code'),
+    '#collapsible' => TRUE,
+    '#collapsed' => empty($field['widget']['default_value_php']),
+  );
+  $db_info = content_database_info($field);
+  $columns = array_keys($db_info['columns']);
+  foreach ($columns as $key => $column) {
+    $columns[$key] = "'$column' => value for $column";
+  }
+  $sample = 'array(
+  0 => array('. implode(', ', $columns) .'),
+  // You\'ll usually want to stop here. Provide more values
+  // if you want your \'default value\' to be multi-valued :
+  1 => array('. implode(', ', $columns) .'),
+  2 => ...
+);';
+  $form['default_value_fieldset']['advanced_options']['default_value_php'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Code'),
+    '#default_value' => isset($field['widget']['default_value_php']) ? $field['widget']['default_value_php'] : '',
+    '#rows' => 6,
+    '#tree' => TRUE,
+    '#description' => t('Advanced Usage Only: PHP code that returns a default value. Should not include &lt;?php ?&gt; delimiters. If this field is filled out, the value returned by this code will override any value specified above. Expected format :<pre>!sample</pre>Using !link_devel\'s \'devel load\' tab on a %type content page might help you figure out the expected format.', array('!sample' => $sample, '!link_devel' => l('devel.module', 'http://www.drupal.org/project/devel'), '%type' => $field['type_name'])),
+  );
+  return $form;
+}
+
+/**
+ * Validation for the default value widget.
+ */
+function content_default_value_widget_validate($form, &$form_state) {
+  $field = $form['#field'];
+  $form_values = $form_state['values'];
+  if (isset($form_values['default_value_php']) && ($php = trim($form_values['default_value_php']))) {
+    ob_start();
+    $return = eval($php);
+    ob_end_clean();
+    if (!is_array($return)) {
+      $error = TRUE;
+    }
+    else {
+      foreach ($return as $item) {
+        if (!is_array($item)) {
+          $error = TRUE;
+          break;
+        }
+      }
+    }
+    if ($error) {
+      $db_info = content_database_info($field);
+      $columns = array_keys($db_info['columns']);
+      foreach ($columns as $key => $column) {
+        $columns[$key] = "'$column' => value for $column";
+      }
+      $sample = 'array(
+  0 => array('. implode(', ', $columns) .'),
+  // You\'ll usually want to stop here. Provide more values
+  // if you want your \'default value\' to be multi-valued :
+  1 => array('. implode(', ', $columns) .'),
+  2 => ...
+);';
+      form_set_error('default_value_php', t('The default value php code returned an incorrect value<br/>Expected format : <pre>!sample</pre>Returned value : @value', array('!sample' => $sample, '@value' => print_r($return, true))));
+      return;
+    }
+    else {
+      $default_value = $return;
+      $is_code = TRUE;
+      form_set_value(array('#parents' => array('default_value_php')), $php, $form_state);
+      form_set_value(array('#parents' => array('default_value')), array(), $form_state);
+    }
+  }
+  else {
+    $default_value = $form_values['default_value_widget'][$field['field_name']];
+    $is_code = FALSE;
+    form_set_value(array('#parents' => array('default_value_php')), '', $form_state);
+    form_set_value(array('#parents' => array('default_value')), $default_value, $form_state);
+  }
+  if (isset($default_value)) {
+    $node = array();
+    $node[$form_values['field_name']] = $default_value;
+    $field['required'] = FALSE;
+    $field_function = $field_type['module'] .'_field';
+    $widget_function = $widget_type['module'] .'_widget';
+
+    // If default_value is created from php code, don't run it through widget processing.
+    // This way the default value code can directly create the correct array without being
+    // mangled by widget processing which sometimes requires an input array in a completely different format.
+    if (function_exists($widget_function) && !$is_code) {
+      $widget_function('validate', $node, $field, $default_value);
+      $widget_function('process form values', $node, $field, $default_value);
+      // The widget processing may have altered the value, save it to be sure.
+      form_set_value(array('#parents' => array('default_value')), $default_value, $form_state);
+    }
+    if (function_exists($field_function)) {
+      $field_function('validate', $node, $field, $default_value, NULL, NULL);
+    }
+    // The field validation routine won't set an error on the right field, so set it here.
+    if (form_get_errors()) {
+      if (trim($form_values['default_value_php'])) {
+        form_set_error('default_value_php', t('The default value php code created @value which is invalid.', array('@value' => print_r($default_value, true))));
+      }
+      else {
+        form_set_error('default_value', t('The default value is invalid.'));
+      }
+    }
+  }
+}
+
+/**
  * Perform adds, alters, and drops as needed to synchronize the database with
  * new field definitions.
  */
Index: fieldgroup.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck/fieldgroup.module,v
retrieving revision 1.38
diff -u -r1.38 fieldgroup.module
--- fieldgroup.module	10 Aug 2007 02:01:53 -0000	1.38
+++ fieldgroup.module	10 Aug 2007 11:09:58 -0000
@@ -56,10 +56,10 @@
 function fieldgroup_theme() {
   return array(
     'fieldgroup_simple' => array(
-      'arguments' => array('element'),
+      'arguments' => array('element' => NULL),
       ),
     'fieldgroup_display_overview_form' => array(
-      'arguments' => array('form'),
+      'arguments' => array('form' => NULL),
       ),
   );
 }
Index: nodereference.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck/nodereference.module,v
retrieving revision 1.70
diff -u -r1.70 nodereference.module
--- nodereference.module	10 Aug 2007 02:00:37 -0000	1.70
+++ nodereference.module	16 Aug 2007 11:13:15 -0000
@@ -22,20 +22,6 @@
 }
 
 /**
- * Implementation of hook_theme().
- */
-function nodereference_theme() {
-  return array(
-    'nodereference_item_simple' => array(
-      'arguments' => array('item' => NULL),
-    ),
-    'nodereference_item_advanced' => array(
-      'arguments' => array('item' => NULL, 'view' => NULL),
-    ),
-  );
-}
-
-/**
  * Implementation of hook_field_info().
  */
 function nodereference_field_info() {
@@ -229,10 +215,56 @@
     'nodereference_select' => array(
       'label' => 'Select List',
       'field types' => array('nodereference'),
+      'callbacks' => array(
+        'default_value' => 'content_default_value',
+        ),
     ),
     'nodereference_autocomplete' => array(
       'label' => 'Autocomplete Text Field',
       'field types' => array('nodereference'),
+      'callbacks' => array(
+        'default_value' => 'content_default_value',
+        ),
+    ),
+  );
+}
+
+/**
+ * Implementation of hook_elements().
+ */
+function nodereference_elements() {
+  return array(
+    'nodereference_select' => array(
+      '#input' => TRUE,
+      '#process' => array('nodereference_select_process'),
+      '#afterbuild' => array('nodereference_select_afterbuild'),
+      '#element_validate' => array('nodereference_select_validate'),
+      ),
+    'nodereference_autocomplete' => array(
+      '#input' => TRUE,
+      '#process' => array('nodereference_autocomplete_process'),
+      '#afterbuild' => array('nodereference_autocomplete_afterbuild'),
+      '#element_validate' => array('nodereference_autocomplete_validate'),
+      ),
+    );
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function nodereference_theme() {
+  return array(
+    'nodereference_select' => array(
+      'arguments' => array('element' => NULL),
+    ),
+    'nodereference_autocomplete' => array(
+      'arguments' => array('element' => NULL),
+    ),
+    'nodereference_item_simple' => array(
+      'arguments' => array('item' => NULL),
+    ),
+    'nodereference_item_advanced' => array(
+      'arguments' => array('item' => NULL, 'view' => NULL),
     ),
   );
 }
@@ -240,165 +272,178 @@
 /**
  * Implementation of hook_widget().
  */
-function nodereference_widget($op, &$node, $field, &$items) {
-  if ($field['widget']['type'] == 'nodereference_select') {
-    switch ($op) {
-      case 'prepare form values':
-        $items_transposed = content_transpose_array_rows_cols($items);
-        $items['default nids'] = isset($items_transposed['nid']) ? $items_transposed['nid'] : array();
-        break;
-
-      case 'form':
-        $form = array();
-
-        $options = _nodereference_potential_references($field, TRUE);
-        foreach ($options as $key => $value) {
-          $options[$key] = _nodereference_item($field, $value);
-        }
-        if (!$field['required']) {
-          $options = array(0 => t('<none>')) + $options;
-        }
+function nodeference_widget(&$node, $field, &$items) {
+  $form = array();
+  $form[$field['field_name']] = array(
+    '#tree' => TRUE,
+    '#type' => $field['widget']['type'],
+    '#field' => $field,
+    '#items' => $items,
+    '#weight' => $field['widget']['weight'],
+    );
+  return $form;
+}
 
-        $form[$field['field_name']] = array('#tree' => TRUE);
-        $form[$field['field_name']]['nids'] = array(
-          '#type' => 'select',
-          '#title' => t($field['widget']['label']),
-          '#default_value' => $items['default nids'],
-          '#multiple' => $field['multiple'],
-          '#size' =>  $field['multiple'] ? min(count($options), 6) : 0,
-          '#options' => $options,
-          '#required' => $field['required'],
-          '#description' => t($field['widget']['description']),
-        );
-
-        return $form;
-
-      case 'process form values':
-        if ($field['multiple']) {
-          // if nothing selected, make it 'none'
-          if (empty($items['nids'])) {
-            $items['nids'] = array(0 => '0');
-          }
-          // drop the 'none' options if other items were also selected
-          elseif (count($items['nids']) > 1) {
-            unset($items['nids'][0]);
-          }
+/**
+ * Process nodereference_select widget.
+ */
+function nodereference_select_process($form) {
+  $field = $form['#field'];
+  $items = $form['#items'];
+  $options = _nodereference_potential_references($field, TRUE);
+  foreach ($options as $key => $value) {
+    $options[$key] = _nodereference_item($field, $value);
+  }
+  if (!$field['required']) {
+    $options = array(0 => t('<none>')) + $options;
+  }
+
+  // Pre process the nodereference data.
+  $items_transposed = content_transpose_array_rows_cols($items);
+  $items['default nids'] = isset($items_transposed['nid']) ? $items_transposed['nid'] : array();
+
+  $form[$field['field_name']] = array('#tree' => TRUE);
+  $form[$field['field_name']]['nids'] = array(
+    '#title' => t($field['widget']['label']),
+    '#default_value' => $items['default nids'],
+    '#multiple' => $field['multiple'],
+    '#size' =>  $field['multiple'] ? min(count($options), 6) : 0,
+    '#options' => $options,
+    '#required' => $field['required'],
+    '#description' => t($field['widget']['description']),
+  );
+  return $form;
+}
 
-          $items = array_values(content_transpose_array_rows_cols(array('nid' => $items['nids'])));
-        }
-        else {
-          $items[0]['nid'] = $items['nids'];
-        }
-        // Remove the widget's data representation so it isn't saved.
-        unset($items['nids']);
-        foreach ($items as $delta => $item) {
-          $items[$delta]['error_field'] =  $field['field_name'] .'][nids';
-        }
-        break;
+/**
+ * Process nodereference_select widget.
+ */
+function nodereference_autocomplete_process($form) {
+  $field = $form['#field'];
+  $items = $form['#items'];
+
+  // Pre process the nodereference data.
+  foreach ($items as $delta => $item) {
+    if (!empty($items[$delta]['nid'])) {
+      $items[$delta]['default node_name'] = db_result(db_query(db_rewrite_sql('SELECT n.title FROM {node} n WHERE n.nid = %d'), $items[$delta]['nid']));
+      $items[$delta]['default node_name'] .= ' [nid:'. $items[$delta]['nid'] .']';
     }
   }
-  else {
-    switch ($op) {
-      case 'prepare form values':
-        foreach ($items as $delta => $item) {
-          if (!empty($items[$delta]['nid'])) {
-            $items[$delta]['default node_name'] = db_result(db_query(db_rewrite_sql('SELECT n.title FROM {node} n WHERE n.nid = %d'), $items[$delta]['nid']));
-            $items[$delta]['default node_name'] .= ' [nid:'. $items[$delta]['nid'] .']';
-          }
-        }
-        break;
 
-      case 'form':
-        $form = array();
-        $form[$field['field_name']] = array('#tree' => TRUE);
-
-        if ($field['multiple']) {
-          $form[$field['field_name']]['#type'] = 'fieldset';
-          $form[$field['field_name']]['#description'] = t($field['widget']['description']);
-          $delta = 0;
-          foreach ($items as $item) {
-            if ($item['nid']) {
-              $form[$field['field_name']][$delta]['node_name'] = array(
-                '#type' => 'textfield',
-                '#title' => ($delta == 0) ? t($field['widget']['label']) : '',
-                '#autocomplete_path' => 'nodereference/autocomplete/'. $field['field_name'],
-                '#default_value' => isset($item['default node_name']) ? $item['default node_name'] : '',
-                '#required' => ($delta == 0) ? $field['required'] : FALSE,
-              );
-              $delta++;
-            }
-          }
-          foreach (range($delta, $delta + 2) as $delta) {
-            $form[$field['field_name']][$delta]['node_name'] = array(
-              '#type' => 'textfield',
-              '#title' => ($delta == 0) ? t($field['widget']['label']) : '',
-              '#autocomplete_path' => 'nodereference/autocomplete/'. $field['field_name'],
-              '#default_value' => '',
-              '#required' => ($delta == 0) ? $field['required'] : FALSE,
-            );
-          }
+  $delta = 0;
+  $max = !empty($field['multiple']) ? sizeof($items) + 2 : 0;
+  $form[$field['field_name']] = array('#tree' => TRUE);
+  foreach (range($delta, $max) as $delta) {
+    $form[$field['field_name']][$delta]['node_name'] = array(
+      '#type' => 'textfield',
+      '#title' => ($delta == 0) ? t($field['widget']['label']) : '',
+      '#autocomplete_path' => 'nodereference/autocomplete/'. $field['field_name'],
+      '#default_value' => isset($items[$delta]['default node_name']) ? $items[0]['default node_name'] : '',
+      '#required' => ($delta == 0) ? $field['required'] : FALSE,
+    );
+  }
+  return $form;
+}
+
+/**
+ * Helper function to determine the value for a text element.
+ *
+ * @param $element
+ *   The form element whose value is being populated.
+ * @param $items
+ *   The incoming POST data to populate the form element. If this is FALSE,
+ *   the element's default value should be returned.
+ * @return
+ *   The data that will appear in the $form_state['values'] collection
+ *   for this element. Return nothing to use the default.
+ */
+function nodereference_select_afterbuild($element, $items = FALSE) {
+  if ($items !== FALSE) {
+    if ($field['multiple']) {
+      // if nothing selected, make it 'none'
+      if (empty($items['nids'])) {
+        $items['nids'] = array(0 => '0');
+      }
+      // drop the 'none' options if other items were also selected
+      elseif (count($items['nids']) > 1) {
+        unset($items['nids'][0]);
+      }
+      $items = array_values(content_transpose_array_rows_cols(array('nid' => $items['nids'])));
+    }
+    else {
+      $items[0]['nid'] = $items['nids'];
+    }
+    // Remove the widget's data representation so it isn't saved.
+    unset($items['nids']);
+    foreach ($items as $delta => $item) {
+      $items[$delta]['error_field'] =  $field['field_name'] .'][nids';
+    }
+  }
+}
+
+/**
+ * Helper function to determine the value for a text element.
+ *
+ * @param $element
+ *   The form element whose value is being populated.
+ * @param $items
+ *   The incoming POST data to populate the form element. If this is FALSE,
+ *   the element's default value should be returned.
+ * @return
+ *   The data that will appear in the $form_state['values'] collection
+ *   for this element. Return nothing to use the default.
+ */
+function nodereference_autocomplete_afterbuild($element, $items = FALSE) {
+  if ($items !== FALSE) {
+    foreach ($items as $delta => $item) {
+      $nid = 0;
+      if (!empty($item['node_name'])) {
+        preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $item['node_name'], $matches);
+        if (!empty($matches)) {
+          // explicit nid
+          $nid = $matches[2];
         }
         else {
-          $form[$field['field_name']][0]['node_name'] = array(
-            '#type' => 'textfield',
-            '#title' => t($field['widget']['label']),
-            '#autocomplete_path' => 'nodereference/autocomplete/'. $field['field_name'],
-            '#default_value' => isset($items[0]['default node_name']) ? $items[0]['default node_name'] : '',
-            '#required' => $field['required'],
-            '#description' => t($field['widget']['description']),
-          );
+          // no explicit nid
+          // TODO :
+          // the best thing would be to present the user with an additional form,
+          // allowing the user to choose between valid candidates with the same title
+          // ATM, we pick the first matching candidate...
+          $nids = _nodereference_potential_references($field, FALSE, $item['node_name'], TRUE);
+          $nid = (!empty($nids)) ? array_shift(array_keys($nids)) : 0;
         }
-        return $form;
-
-      case 'validate':
-        foreach ($items as $delta => $item) {
-          $error_field = $field['field_name'] .']['. $delta .'][node_name';
-          if (!empty($item['node_name'])) {
-            preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $item['node_name'], $matches);
-            if (!empty($matches)) {
-              // explicit nid
-              list(, $title, $nid) = $matches;
-              if (!empty($title) && ($n = node_load($nid)) && $title != $n->title) {
-                form_set_error($error_field, t('%name : Title mismatch. Please check your selection.'), array('%name' => t($field['widget']['label'])));
-              }
-            }
-          }
-        }
-        return;
+      }
+      // Remove the widget's data representation so it isn't saved.
+      // TODO : problem when saving an empty noderef field :
+      // conflicts with line $data[] = $item[$column]; in content.module
+      unset($items[$delta]['node_name']);
+      if (!empty($nid)) {
+        $items[$delta]['nid'] = $nid;
+        $items[$delta]['error_field'] = $field['field_name'] .']['. $delta .'][node_name';
+      }
+      elseif ($delta > 0) {
+        // Don't save empty fields when they're not the first value (keep '0' otherwise)
+        unset($element[$delta]);
+      }
+    }
+  }
+  return $element;
+}
 
-      case 'process form values':
-        foreach ($items as $delta => $item) {
-          $nid = 0;
-          if (!empty($item['node_name'])) {
-            preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $item['node_name'], $matches);
-            if (!empty($matches)) {
-              // explicit nid
-              $nid = $matches[2];
-            }
-            else {
-              // no explicit nid
-              // TODO :
-              // the best thing would be to present the user with an additional form,
-              // allowing the user to choose between valid candidates with the same title
-              // ATM, we pick the first matching candidate...
-              $nids = _nodereference_potential_references($field, FALSE, $item['node_name'], TRUE);
-              $nid = (!empty($nids)) ? array_shift(array_keys($nids)) : 0;
-            }
-          }
-          // Remove the widget's data representation so it isn't saved.
-          // TODO : problem when saving an empty noderef field :
-          // conflicts with line $data[] = $item[$column]; in content.module
-          unset($items[$delta]['node_name']);
-          if (!empty($nid)) {
-            $items[$delta]['nid'] = $nid;
-            $items[$delta]['error_field'] = $field['field_name'] .']['. $delta .'][node_name';
-          }
-          elseif ($delta > 0) {
-            // Don't save empty fields when they're not the first value (keep '0' otherwise)
-            unset($items[$delta]);
-          }
+function nodereference_autocomplete_validate($form, &$form_state) {
+  $field = $form['#field'];
+  $items = $form_state['values'];
+  foreach ($items as $delta => $item) {
+    $error_field = $field['field_name'] .']['. $delta .'][node_name';
+    if (!empty($item['node_name'])) {
+      preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $item['node_name'], $matches);
+      if (!empty($matches)) {
+        // explicit nid
+        list(, $title, $nid) = $matches;
+        if (!empty($title) && ($n = node_load($nid)) && $title != $n->title) {
+          form_set_error($error_field, t('%name : Title mismatch. Please check your selection.'), array('%name' => t($field['widget']['label'])));
         }
-        break;
+      }
     }
   }
 }
@@ -545,4 +590,56 @@
   $options = array(0 => t('<empty>'));
   $options = $options + _nodereference_potential_references($filterinfo['extra']['field']);
   return $options;
+}
+
+/**
+ * Nodereference select theme.
+ *
+ * All labels, descriptions, and fieldsets are handled in the theme layer.
+ */
+function theme_nodereference_select($element) {
+  $field = $element['#field'];
+  if ($field['multiple']) {
+    foreach ($element as $item) {
+      $output .= theme('select', $item);
+    }
+    $fieldset['#type'] = 'fieldset';
+    $fieldset['#title'] = t($field['widget']['label']);
+    $fieldset['#description'] = t($field['widget']['description']);
+    $fieldset['#weight'] = $field['widget']['weight'];
+    $fieldset['#value'] = $output;
+    return theme('fieldset', $fieldset);
+  }
+  else {
+    $element['#title'] = t($field['widget']['label']);
+    $element['#description'] = t($field['widget']['description']);
+    $element['#weight'] = $field['widget']['weight'];
+    return theme('select', $element);
+  }
+}
+
+/**
+ * Nodereference autocomplete theme.
+ *
+ * All labels, descriptions, and fieldsets are handled in the theme layer.
+ */
+function theme_nodereference_autocomplete($element) {
+  $field = $element['#field'];
+  if ($field['multiple']) {
+    foreach ($element as $item) {
+      $output .= theme('textfield', $item);
+    }
+    $fieldset['#type'] = 'fieldset';
+    $fieldset['#title'] = t($field['widget']['label']);
+    $fieldset['#description'] = t($field['widget']['description']);
+    $fieldset['#weight'] = $field['widget']['weight'];
+    $fieldset['#value'] = $output;
+    return theme('fieldset', $fieldset);
+  }
+  else {
+    $element['#title'] = t($field['widget']['label']);
+    $element['#description'] = t($field['widget']['description']);
+    $element['#weight'] = $field['widget']['weight'];
+    return theme('textfield', $element);
+  }
 }
\ No newline at end of file
Index: number.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck/number.module,v
retrieving revision 1.57
diff -u -r1.57 number.module
--- number.module	9 Aug 2007 15:33:24 -0000	1.57
+++ number.module	16 Aug 2007 11:29:44 -0000
@@ -253,6 +253,33 @@
     'number' => array(
       'label' => 'Text Field',
       'field types' => array('number_integer', 'number_decimal'),
+      'callbacks' => array(
+        'default_value' => 'content_default_value',
+        ),
+    ),
+  );
+}
+
+/**
+ * Implementation of hook_elements().
+ */
+function number_elements() {
+  return array(
+    'number' => array(
+      '#input' => TRUE,
+      '#process' => array('number_process'),
+      '#element_validate' => array('number_validate'),
+    ),
+  );
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function number_theme() {
+  return array(
+    'number' => array(
+      'arguments' => array('element' => NULL),
     ),
   );
 }
@@ -260,87 +287,70 @@
 /**
  * Implementation of hook_widget().
  */
-function number_widget($op, &$node, $field, &$items) {
-  switch ($op) {
-    case 'form':
-      $form = array();
-      $prefixes = array();
-      $suffixes = array();
-      if (!empty($field['prefix'])) {
-        $prefixes = explode('|', $field['prefix']);
-      }
-      if (!empty($field['suffix'])) {
-        $suffixes = explode('|', $field['suffix']);
-      }
-      // We take the plural form (if any) for forms.
-      $prefix = array_pop($prefixes);
-      $suffix = array_pop($suffixes);
-
-      $form[$field['field_name']] = array('#tree' => TRUE);
-
-      if ($field['multiple']) {
-        $form[$field['field_name']]['#type'] = 'fieldset';
-        $form[$field['field_name']]['#description'] = t($field['widget']['description']);
-        $delta = 0;
-        foreach ($items as $data) {
-          if (isset($data['value'])) {
-            $form[$field['field_name']][$delta]['value'] = array(
-              '#type' => 'textfield',
-              '#title' => ($delta == 0) ? t($field['widget']['label']) : '',
-              '#default_value' => $data['value'],
-              '#required' => ($delta == 0) ? $field['required'] : FALSE,
-              '#size' => 20,
-              '#maxlength' => 11,
-              '#attributes' => array('class' => 'number'),
-              '#field_prefix' => $prefix,
-              '#field_suffix' => $suffix,
-            );
-            $delta++;
-          }
-        }
-        foreach (range($delta, $delta + 2) as $delta) {
-          $form[$field['field_name']][$delta]['value'] = array(
-            '#type' => 'textfield',
-            '#title' => ($delta == 0) ? t($field['widget']['label']) : '',
-            '#default_value' => '',
-            '#required' => ($delta == 0) ? $field['required'] : FALSE,
-            '#size' => 20,
-            '#maxlength' => 11,
-            '#attributes' => array('class' => 'number'),
-            '#field_prefix' => $prefix,
-            '#field_suffix' => $suffix,
-          );
-        }
-      }
-      else {
-        $form[$field['field_name']][0]['value'] = array(
-          '#type' => 'textfield',
-          '#title' => t($field['widget']['label']),
-          '#default_value' => isset($items[0]['value']) ? $items[0]['value'] : '',
-          '#required' => $field['required'],
-          '#description' => t($field['widget']['description']),
-          '#size' => 20,
-          '#maxlength' => 11,
-          '#attributes' => array('class' => 'number'),
-          '#field_prefix' => $prefix,
-          '#field_suffix' => $suffix,
-        );
-      }
-      return $form;
+function number_widget(&$node, $field, &$items) {
+  $form = array();
+  $form[$field['field_name']] = array(
+    '#tree' => TRUE,
+    '#type' => $field['widget']['type'],
+    '#field' => $field,
+    '#items' => $items,
+    '#weight' => $field['widget']['weight'],
+    );
+  return $form;
+}
+
+/**
+ * Process number widget.
+ */
+function number_process($form) {
+  $field = $form['#field'];
+  $items = $form['#items'];
+  $prefixes = array();
+  $suffixes = array();
+  if (!empty($field['prefix'])) {
+    $prefixes = explode('|', $field['prefix']);
+  }
+  if (!empty($field['suffix'])) {
+    $suffixes = explode('|', $field['suffix']);
+  }
+  // We take the plural form (if any) for forms.
+  $prefix = array_pop($prefixes);
+  $suffix = array_pop($suffixes);
+
+  $delta = 0;
+  $max = !empty($field['multiple']) ? sizeof($items) + 2 : 0;
+  foreach (range($delta, $max) as $delta) {
+    $form[$field['field_name']][$delta]['value'] = array(
+      '#type' => 'textfield', '#size' => 20, '#maxlength' => 11,
+      '#attributes' => array('class' => 'number'),
+      '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : '',
+      '#required' => ($delta == 0) ? $field['required'] : FALSE,
+      '#field_prefix' => $prefix,
+      '#field_suffix' => $suffix,
+      '#weight' => $delta,
+    );
+  }
+  return $form;
+}
 
-    case 'process form values':
-      // Don't save empty fields.
+/**
+ * Validate a number element.
+ */
+function number_validate($form, &$form_state) {
+  $field = $form['#field'];
+  $items = $form_state['values'];
+  if (is_array($items) && !empty($items)) {
+    // Don't save empty fields.
+    foreach ($items as $delta => $item) {
+      $form_state['values'][$delta]['value'] = preg_replace('@[^-0-9\.]@', '', $items[$delta]['value']);
+    }
+    if (!empty($element['#multiple'])) {
       foreach ($items as $delta => $item) {
-        $items[$delta]['value'] = preg_replace('@[^-0-9\.]@', '', $items[$delta]['value']);
-      }
-      if ($field['multiple']) {
-        foreach ($items as $delta => $item) {
-          if ($item['value'] == '' && $delta > 0) {
-            unset($items[$delta]);
-          }
+        if ($item['value'] == '' && $delta > 0) {
+          unset($form_state['values'][$delta]);
         }
       }
-      break;
+    }
   }
 }
 
@@ -381,3 +391,29 @@
   }
   return $allowed_values[$field['field_name']];
 }
+
+/**
+ * Number theme.
+ *
+ * All labels, descriptions, and fieldsets are handled in the theme layer.
+ */
+function theme_number($element) {
+  $field = $element['#field'];
+  if ($field['multiple']) {
+    foreach ($element as $item) {
+      $output .= theme('textfield', $item);
+    }
+    $fieldset['#type'] = 'fieldset';
+    $fieldset['#title'] = t($field['widget']['label']);
+    $fieldset['#description'] = t($field['widget']['description']);
+    $fieldset['#weight'] = $field['widget']['weight'];
+    $fieldset['#value'] = $output;
+    return theme('fieldset', $fieldset);
+  }
+  else {
+    $element['#title'] = t($field['widget']['label']);
+    $element['#description'] = t($field['widget']['description']);
+    $element['#weight'] = $field['widget']['weight'];
+    return theme('textfield', $element);
+  }
+}
\ No newline at end of file
Index: optionwidgets.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck/optionwidgets.install,v
retrieving revision 1.5
diff -u -r1.5 optionwidgets.install
--- optionwidgets.install	25 Jan 2007 17:27:45 -0000	1.5
+++ optionwidgets.install	12 Aug 2007 12:56:28 -0000
@@ -84,4 +84,16 @@
   db_query('DELETE FROM {cache}');
 
   return $ret;
+}
+
+/**
+ * Rename widgets from 'options_xxx' to 'optionwidgets_xxx' so hook_elements
+ * and hook_themes items are prefixed with module name as they should be.
+ */
+function optionwidgets_update_6000() {
+  $ret = array();
+  $result = db_query("UPDATE {node_field_instance} SET widget_type = 'optionwidgets_select' where widget_type = 'options_select'");
+  $result = db_query("UPDATE {node_field_instance} SET widget_type = 'optionwidgets_onoff' where widget_type = 'options_onoff'");
+  $result = db_query("UPDATE {node_field_instance} SET widget_type = 'optionwidgets_buttons' where widget_type = 'options_buttons'");
+  return $ret;
 }
\ No newline at end of file
Index: optionwidgets.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck/optionwidgets.module,v
retrieving revision 1.22
diff -u -r1.22 optionwidgets.module
--- optionwidgets.module	9 Aug 2007 18:50:28 -0000	1.22
+++ optionwidgets.module	16 Aug 2007 11:14:12 -0000
@@ -13,7 +13,7 @@
   if (preg_match('|^admin/content/types/[^/]*/fields/[^/]*$|', $path)) {
     if (($field = content_fields($arg[3], $arg[5])) && in_array($field['widget']['type'], array_keys(optionwidgets_widget_info()))) {
       $output = t('Create a list of options as a list in <strong>Allowed values</strong> or as an array in Php code at the bottom of this page. These values will be the same for the %field in all content types. ', array('%field' => $field['widget']['label']));
-      if ($field['widget']['type'] == 'options_onoff') {
+      if ($field['widget']['type'] == 'optionwidgets_onoff') {
         $output .= '<br/>'. t('For a \'Single on/off checkbox\' widget, define the \'off\' value first, then the \'on\' value in the <strong>Allowed values</strong> section. Note that the checkbox will be labeled with the label of the \'on\' value.');
       }
       else {
@@ -25,166 +25,237 @@
 }
 
 /**
- * Implementation of hook_theme().
- */
-function optionwidgets_theme() {
-  return array(
-    'optionwidgets_none' => array(
-      'arguments' => array('widget_type', 'field_name', 'node_type'),
-      ),
-  );
-}
-
-/**
  * Implementation of hook_widget_info().
  */
 function optionwidgets_widget_info() {
 
   $option_types = array('text', 'number_integer', 'number_decimal');
   return array(
-    'options_select' => array(
+    'optionwidgets_select' => array(
       'label' => 'Select list',
       'field types' => $option_types,
+      'callbacks' => array(
+        'default_value' => 'content_default_value',
+        ),
     ),
-    'options_buttons' => array(
+    'optionwidgets_buttons' => array(
       'label' => 'Check boxes/radio buttons',
       'field types' => $option_types,
+      'callbacks' => array(
+        'default_value' => 'content_default_value',
+        ),
     ),
-    'options_onoff' => array(
+    'optionwidgets_onoff' => array(
       'label' => 'Single on/off checkbox',
       'field types' => $option_types,
+      'callbacks' => array(
+        'default_value' => 'content_default_value',
+        ),
     ),
   );
 }
 
 /**
- * Implementation of hook_widget().
+ * Implementation of hook_elements().
  */
-function optionwidgets_widget($op, &$node, $field, &$items) {
-  switch ($op) {
-    case 'prepare form values':
-      $options = _optionwidgets_options($field, $node);
-      $items_transposed = content_transpose_array_rows_cols($items);
-      $values = (isset($items_transposed['value']) && is_array($items_transposed['value'])) ? $items_transposed['value'] : array();
-
-      $keys = array();
-      foreach ($values as $value) {
-        $key = array_search($value, array_keys($options));
-        if (isset($key)) {
-          $keys[] = $value;
-        }
-      }
-      if ($field['multiple'] || $field['widget']['type'] == 'options_onoff') {
-        $items['default keys'] = $keys;
-      }
-      else {
-        $items['default key'] = reset($keys);
-      }
-      break;
+function optionwidgets_elements() {
+  return array(
+    'optionwidgets_select' => array(
+      '#input' => TRUE,
+      '#process' => array('optionwidgets_preprocess', 'optionwidgets_select_process'),
+      '#element_validate' => array('optionwidgets_validate'),
+      ),
+    'optionwidgets_buttons' => array(
+      '#input' => TRUE,
+      '#process' => array('optionwidgets_preprocess', 'optionwidgets_buttons_process'),
+      '#element_validate' => array('optionwidgets_validate'),
+      ),
+    'optionwidgets_onoff' => array(
+      '#input' => TRUE,
+      '#process' => array('optionwidgets_preprocess', 'optionwidgets_onoff_process'),
+      '#element_validate' => array('optionwidgets_validate'),
+      ),
+    );
+}
 
-    case 'form':
-      $options = _optionwidgets_options($field, $node);
-      $form = array();
-
-      $form[$field['field_name']] = array('#tree' => TRUE);
-
-      switch ($field['widget']['type']) {
-        case 'options_select':
-          if ($field['multiple']) {
-            $form[$field['field_name']]['keys'] = array(
-              '#type' => 'select',
-              '#title' => t($field['widget']['label']),
-              '#default_value' => $items['default keys'],
-              '#multiple' => TRUE,
-              '#size' => min(count($options), 6),
-              '#options' => $options,
-              '#required' => $field['required'],
-              '#description' => t($field['widget']['description']),
-            );
-          }
-          else {
-            $form[$field['field_name']]['key'] = array(
-              '#type' => 'select',
-              '#title' => t($field['widget']['label']),
-              '#default_value' => $items['default key'],
-              '#multiple' => FALSE,
-              '#options' => $options,
-              '#required' => $field['required'],
-              '#description' => t($field['widget']['description']),
-            );
-          }
-          break;
-
-        case 'options_onoff':
-          // Display only the 'On' value of $options;
-          $vals = array_keys($options);
-          $on_value = $vals[1];
-          $form[$field['field_name']]['keys'] = array(
-            '#type' => 'checkbox',
-            '#title' => $options[$on_value],
-            '#default_value' => $items['default keys'][0],
-            '#return_value' => $on_value,
-            '#description' => t($field['widget']['description']),
-            '#required' => FALSE,
-          );
-          break;
-
-        case 'options_buttons':
-          if ($field['multiple']) {
-            $form[$field['field_name']]['keys'] = array(
-              '#type' => 'checkboxes',
-              '#title' => t($field['widget']['label']),
-              '#default_value' => $items['default keys'],
-              '#options' => $options,
-              '#required' => $field['required'],
-              '#description' => t($field['widget']['description']),
-            );
-          }
-          else {
-            $form[$field['field_name']]['key'] = array(
-              '#type' => 'radios',
-              '#title' => t($field['widget']['label']),
-              '#default_value' => $items['default key'],
-              '#options' => $options,
-              '#required' => $field['required'],
-              '#description' => t($field['widget']['description']),
-            );
-          }
-          break;
-      }
-      return $form;
+/**
+ * Implementation of hook_theme().
+ */
+function optionwidgets_theme() {
+  return array(
+    'optionwidgets_select' => array(
+      'arguments' => array('element' => NULL),
+    ),
+    'optionwidgets_buttons' => array(
+      'arguments' => array('element' => NULL),
+    ),
+    'optionwidgets_onoff' => array(
+      'arguments' => array('element' => NULL),
+    ),
+    'optionwidgets_none' => array(
+      'arguments' => array('widget_type' => NULL, 'field_name' => NULL, 'node_type' => NULL),
+      ),
+  );
+}
 
-    case 'process form values':
-      $options = _optionwidgets_options($field, $node);
-      if ($field['multiple'] || $field['widget']['type'] == 'options_onoff') {
-        $keys = (array) $items['keys'];
-      }
-      else {
-        $keys = array(!empty($items['key']) ? $items['key'] : '');
-      }
+/**
+ * Implementation of hook_widget().
+ */
+function optionwidgets_widget(&$node, $field, &$items) {
+  $form = array();
+  $form[$field['field_name']] = array(
+    '#tree' => TRUE,
+    '#type' => $field['widget']['type'],
+    '#field' => $field,
+    '#items' => $items,
+    '#weight' => $field['widget']['weight'],
+    '#nid' => $node->nid,
+    );
+  return $form;
+}
 
-      $values = array();
-      foreach ($keys as $key) {
-        if (isset($options[$key])) {
-          $values[] = $key;
-        }
-      }
+/**
+ * Pre process optionwidgets items.
+ *
+ * All optionwidgets use the same preprocessing.
+ */
+function optionwidgets_preprocess($form, &$form_state) {
+  $field = $form['#field'];
+  $items = $form['#items'];
+
+  $items_transposed = content_transpose_array_rows_cols($items);
+  $values = (isset($items_transposed['value']) && is_array($items_transposed['value'])) ? $items_transposed['value'] : array();
+  $keys = array();
+  foreach ($values as $value) {
+    $key = array_search($value, array_keys($options));
+    if (isset($key)) {
+      $keys[] = $value;
+    }
+  }
+  if ($field['multiple'] || $field['widget']['type'] == 'optionwidgets_onoff') {
+    $items['default keys'] = $keys;
+  }
+  else {
+    $items['default key'] = reset($keys);
+  }
+  $form_state['values'] = $items;
+}
 
-      if ($field['widget']['type'] == 'options_onoff' && empty($values)) {
-        $keys = array_keys($options);
-        $values[] = $keys[0];
-      }
+/**
+ * Process an optionwidgets_select element.
+ */
+function optionwidgets_select_process($form, &$form_state) {
+  $field = $form['#field'];
+  $items = $form_state['values'];
+
+  $options = _optionwidgets_options($field);
+  if ($field['multiple']) {
+    $form[$field['field_name']]['keys'] = array(
+      '#type' => 'select',
+      '#default_value' => $items['default keys'],
+      '#multiple' => TRUE,
+      '#size' => min(count($options), 6),
+      '#options' => $options,
+      '#required' => $field['required'],
+    );
+  }
+  else {
+    $form[$field['field_name']]['key'] = array(
+      '#type' => 'select',
+      '#default_value' => $items['default key'],
+      '#multiple' => FALSE,
+      '#options' => $options,
+      '#required' => $field['required'],
+    );
+  }
+  return $form;
+}
 
-      $items = content_transpose_array_rows_cols(array('value' => $values));
+/**
+ * Process an optionwidgets_onoff element.
+ */
+function optionwidgets_onoff_process($form, &$form_state) {
+  $field = $form['#field'];
+  $items = $form_state['values'];
+
+  $options = _optionwidgets_options($field);
+  // Display only the 'On' value of $options;
+  $vals = array_keys($options);
+  $on_value = $vals[1];
+  $form[$field['field_name']]['keys'] = array(
+    '#type' => 'checkbox',
+    '#default_value' => $items['default keys'][0],
+    '#return_value' => $on_value,
+    '#required' => FALSE,
+  );
+  return $form;
+}
 
-      // Remove the widget's data representation so it isn't saved.
-      unset($items['keys']);
-      unset($items['key']);
-      break;
+/**
+ * Process an optionwidgets_buttons element.
+ */
+function optionwidgets_buttons_process($form, &$form_state) {
+  $field = $form['#field'];
+  $items = $form_state['values'];
+
+  $options = _optionwidgets_options($field);
+  if ($field['multiple']) {
+    $form[$field['field_name']]['keys'] = array(
+      '#type' => 'checkboxes',
+      '#default_value' => $items['default keys'],
+      '#options' => $options,
+      '#required' => $field['required'],
+    );
+  }
+  else {
+    $form[$field['field_name']]['key'] = array(
+      '#type' => 'radios',
+      '#default_value' => $items['default key'],
+      '#options' => $options,
+      '#required' => $field['required'],
+    );
+  }
+  return $form;
+}
+
+/**
+ * Validate a number element.
+ *
+ * Validation is the same for all kinds of optionwidgets, so all can use
+ * the same callback.
+ */
+function optionwidgets_validate($form, &$form_state) {
+  $field = $form['#field'];
+  $items = $form_state['values'];
+
+  $options = _optionwidgets_options($field);
+  if ($field['multiple'] || $field['widget']['type'] == 'optionwidgets_onoff') {
+    $keys = (array) $items['keys'];
   }
+  else {
+    $keys = array(!empty($items['key']) ? $items['key'] : '');
+  }
+  $values = array();
+  foreach ($keys as $key) {
+    if (isset($options[$key])) {
+      $values[] = $key;
+    }
+  }
+  if ($field['widget']['type'] == 'optionwidgets_onoff' && empty($values)) {
+    $keys = array_keys($options);
+    $values[] = $keys[0];
+  }
+  $form_state['values'] = content_transpose_array_rows_cols(array('value' => $values));
+  // Remove the widget's data representation so it isn't saved.
+  unset($form_state['values']['keys']);
+  unset($form_state['values']['key']);
 }
 
-function _optionwidgets_options($field, $node) {
+/**
+ * Create an options array for this field.
+ */
+function _optionwidgets_options($field) {
+
   $types = _content_field_types();
   $field_allowed_values = $types[$field['type']]['module'] .'_allowed_values';
   if (function_exists($field_allowed_values)) {
@@ -194,10 +265,10 @@
     $allowed_values = array();
   }
 
-  if ($field['widget']['type'] == 'options_select' ||
-     ($field['widget']['type'] == 'options_buttons' && !$field['multiple'])) {
+  if ($field['widget']['type'] == 'optionwidgets_select' ||
+     ($field['widget']['type'] == 'optionwidgets_buttons' && !$field['multiple'])) {
     if (!$field['required']) {
-      $allowed_values = array('' => theme('optionwidgets_none', $field['widget']['type'], $field['field_name'], $node->type)) + $allowed_values;
+      $allowed_values = array('' => theme('optionwidgets_none', $field['widget']['type'], $field['field_name'], $node)) + $allowed_values;
     }
   }
 
@@ -208,11 +279,53 @@
  *  Theme the label for the empty value for options that are not required.
  *  The default theme will display N/A for a radio list and blank for a select.
  */
-function theme_optionwidgets_none($widget_type, $field_name, $node_type) {
+function theme_optionwidgets_none($widget_type, $field_name, $node) {
   switch ($widget_type) {
-  case 'options_buttons':
+  case 'optionwidgets_buttons':
     return t('N/A');
   default :
     return '';
   }
+}
+
+/**
+ * Optionwidgets select theme.
+ *
+ * All labels, descriptions, and fieldsets are handled in the theme layer.
+ *
+ */
+function theme_optionwidgets_select($element) {
+  $field = $element['#field'];
+  $element['#title'] = t($field['widget']['label']);
+  $element['#description'] = t($field['widget']['description']);
+  return theme('select', $element);
+}
+
+/**
+ * Optionwidgets onoff theme.
+ *
+ * All labels, descriptions, and fieldsets are handled in the theme layer.
+ */
+function theme_optionwidgets_onoff($element) {
+  $field = $element['#field'];
+  $element['#title'] = t($field['widget']['label']);
+  $element['#description'] = t($field['widget']['description']);
+  return theme('checkbox', $element);
+}
+
+/**
+ * Optionwidgets buttons theme.
+ *
+ * All labels, descriptions, and fieldsets are handled in the theme layer.
+ */
+function theme_optionwidgets_buttons($element) {
+  $field = $element['#field'];
+  $element['#title'] = t($field['widget']['label']);
+  $element['#description'] = t($field['widget']['description']);
+  if ($element['#field']['multiple']) {
+    return theme('checkboxes', $element);
+  }
+  else {
+    return theme('radios', $element);
+  }
 }
\ No newline at end of file
Index: text.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck/text.install,v
retrieving revision 1.12
diff -u -r1.12 text.install
--- text.install	25 Jan 2007 17:27:45 -0000	1.12
+++ text.install	13 Aug 2007 11:27:56 -0000
@@ -188,3 +188,20 @@
 
   return $ret;
 }
+
+/**
+ * Rename widgets to match hook_elements values.
+ */
+function text_update_6000() {
+  $ret = array();
+  $fields = content_fields();
+  foreach ($fields as $field) {
+    if ($field['widget']['type'] == 'text' && $field['widget']['rows'] > 1) {
+      $result = db_query("UPDATE {node_field_instance} SET widget_type = 'text_textarea' where field_name = '". $field_name ."'");
+    }
+    elseif ($field['widget']['type'] == 'text') {
+      $result = db_query("UPDATE {node_field_instance} SET widget_type = 'text_textfield' where field_name = '". $field_name ."'");
+    }
+  }
+  return $ret;
+}
Index: text.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck/text.module,v
retrieving revision 1.58
diff -u -r1.58 text.module
--- text.module	10 Aug 2007 02:13:56 -0000	1.58
+++ text.module	16 Aug 2007 11:13:09 -0000
@@ -196,9 +196,53 @@
  */
 function text_widget_info() {
   return array(
-    'text' => array(
+    'text_textfield' => array(
       'label' => 'Text Field',
       'field types' => array('text'),
+      'callbacks' => array(
+        'default_value' => 'content_default_value',
+        ),
+    ),
+    'text_textarea' => array(
+      'label' => 'Text Area',
+      'field types' => array('text'),
+      'callbacks' => array(
+        'default_value' => 'content_default_value',
+        ),
+    ),
+  );
+}
+
+/**
+ * Implementation of hook_elements().
+ */
+function text_elements() {
+  return array(
+    'text_textfield' => array(
+      '#input' => TRUE,
+      '#process' => array('text_textfield_process'),
+      '#element_validate' => array('text_textfield_validate'),
+      '#field' => array(),
+      ),
+    'text_textarea' => array(
+      '#input' => TRUE,
+      '#process' => array('text_textarea_process'),
+      '#element_validate' => array('text_textarea_validate'),
+      '#field' => array(),
+      ),
+    );
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function text_theme() {
+  return array(
+    'text_textarea' => array(
+      'arguments' => array('element' => NULL),
+    ),
+    'text_textfield' => array(
+      'arguments' => array('element' => NULL),
     ),
   );
 }
@@ -210,13 +254,21 @@
   switch ($op) {
     case 'form':
       $form = array();
-      $form['rows'] = array(
-        '#type' => 'textfield',
-        '#title' => t('Rows'),
-        '#default_value' => isset($widget['rows']) ? $widget['rows'] : 1,
-        '#required' => TRUE,
-      );
+      if ($widget['type'] == 'text_textfield') {
+        $form['rows'] = array(
+          '#type' => 'hidden',
+          '#value' => 1,
+          );
+      }
+      else {
+        $form['rows'] = array(
+          '#type' => 'textfield',
+          '#title' => t('Rows'),
+          '#default_value' => isset($widget['rows']) ? $widget['rows'] : 1,
+          '#required' => TRUE,
+          );
       return $form;
+      }
 
     case 'validate':
       if (!is_numeric($widget['rows']) || intval($widget['rows']) != $widget['rows'] || $widget['rows'] <= 0) {
@@ -232,112 +284,98 @@
 /**
  * Implementation of hook_widget().
  */
-function text_widget($op, &$node, $field, &$items) {
-  switch ($op) {
-    case 'form':
-      $form = array();
+function text_widget($node, $field, &$items) {
+  $form = array();
+  $form[$field['field_name']] = array(
+    '#tree' => TRUE,
+    '#type' => $field['widget']['type'],
+    '#field' => $field,
+    '#items' => $items,
+    '#nid' => $node->nid,
+    );
+  return $form;
+}
 
-      $form[$field['field_name']] = array('#tree' => TRUE);
+/**
+ * Process a textfield element.
+ */
+function text_textfield_process($form) {
+  $field = $form['#field'];
+  $items = $form['#items'];
+  $delta = 0;
+  $max = !empty($field['multiple']) ? sizeof($items) + 2 : 0;
+
+  foreach (range($delta, $max) as $delta) {
+    $form[$field['field_name']][$delta]['value'] = array(
+      '#type' => 'textfield',
+      '#title' => ($delta == 0) ? t($field['widget']['label']) : '',
+      '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : '',
+      '#required' => ($delta == 0) ? $field['required'] : FALSE,
+      '#maxlength' => !empty($field['max_length']) ? $field['max_length'] : NULL,
+      '#weight' => $delta,
+    );
+  }
+  return $form;
+}
 
-      $rows = isset($field['widget']['rows']) ? $field['widget']['rows'] : 1;
-      if ($field['multiple']) {
-        $form[$field['field_name']]['#type'] = 'fieldset';
-        $form[$field['field_name']]['#description'] = t($field['widget']['description']);
-        $delta = 0;
-        foreach ($items as $data) {
-          if ($data['value']) {
-            if ($rows == 1) {
-              $form[$field['field_name']][$delta]['value'] = array(
-                '#type' => 'textfield',
-                '#title' => ($delta == 0) ? t($field['widget']['label']) : '',
-                '#default_value' => $data['value'],
-                '#required' => ($delta == 0) ? $field['required'] : FALSE,
-                '#maxlength' => !empty($field['max_length']) ? $field['max_length'] : NULL,
-                '#weight' => $field['widget']['weight'],
-              );
-            }
-            else {
-              $form[$field['field_name']][$delta]['value'] = array(
-                '#type' => 'textarea',
-                '#title' => ($delta == 0) ? t($field['widget']['label']) : '',
-                '#default_value' => $data['value'],
-                '#required' => ($delta == 0) ? $field['required'] : FALSE,
-                '#rows' => $rows,
-                '#weight' => $field['widget']['weight'],
-              );
-            }
-            if (!empty($field['text_processing'])) {
-              $format = $data['format'];
-              $form[$field['field_name']][$delta]['format'] = filter_form($data['format'], $form[$field['field_name']][$delta]['value']['#weight'] + 1, array($field['field_name'], $delta, 'format'));
-            }
-            $delta++;
-          }
-        }
-        foreach (range($delta, $delta + 2) as $delta) {
-          if ($rows == 1) {
-            $form[$field['field_name']][$delta]['value'] = array(
-              '#type' => 'textfield',
-              '#title' => ($delta == 0) ? t($field['widget']['label']) : '',
-              '#default_value' => '',
-              '#required' => ($delta == 0) ? $field['required'] : FALSE,
-              '#maxlength' => !empty($field['max_length']) ? $field['max_length'] : NULL,
-              '#weight' => $field['widget']['weight'],
-            );
-          }
-          else {
-            $form[$field['field_name']][$delta]['value'] = array(
-              '#type' => 'textarea',
-              '#title' => ($delta == 0) ? t($field['widget']['label']) : '',
-              '#default_value' => '',
-              '#required' => ($delta == 0) ? $field['required'] : FALSE,
-              '#rows' => $rows,
-              '#weight' => $field['widget']['weight'],
-            );
-          }
-          if (!empty($field['text_processing'])) {
-            $format = FILTER_FORMAT_DEFAULT;
-            $form[$field['field_name']][$delta]['format'] = filter_form($format, $form[$field['field_name']][$delta]['value']['#weight'] + 1, array($field['field_name'], $delta, 'format'));
-          }
-        }
-      }
-      else {
-        if ($rows == 1) {
-          $form[$field['field_name']][0]['value'] = array(
-            '#type' => 'textfield',
-            '#title' => t($field['widget']['label']),
-            '#default_value' => isset($items[0]['value']) ? $items[0]['value'] : '',
-            '#required' => $field['required'],
-            '#description' => t($field['widget']['description']),
-            '#maxlength' => !empty($field['max_length']) ? $field['max_length'] : NULL,
-            '#weight' => $field['widget']['weight'],
-          );
-        }
-        else {
-          $form[$field['field_name']][0]['value'] = array(
-            '#type' => 'textarea',
-            '#title' => t($field['widget']['label']),
-            '#default_value' => isset($items[0]['value']) ? $items[0]['value'] : '',
-            '#required' => $field['required'],
-            '#rows' => $rows,
-            '#description' => t($field['widget']['description']),
-            '#weight' => $field['widget']['weight'],
-          );
-        }
-        if (!empty($field['text_processing'])) {
-          $format = isset($items[0]['format']) ? $items[0]['format'] : FILTER_FORMAT_DEFAULT;
-          $form[$field['field_name']][0]['format'] = filter_form($format , $form[$field['field_name']][0]['value']['#weight'] + 1, array($field['field_name'], 0, 'format'));
+/**
+ * Process a textarea element
+ */
+function text_textarea_process($form) {
+  $field = $form['#field'];
+  $items = $form['#items'];
+  $delta = 0;
+  $max = !empty($field['multiple']) ? sizeof($items) + 2 : 0;
+
+  foreach (range($delta, $max) as $delta) {
+    $form[$field['field_name']][$delta]['value'] = array(
+      '#type' => 'textarea',
+      '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : '',
+      '#required' => ($delta == 0) ? $field['required'] : FALSE,
+      '#rows' => $rows,
+      '#weight' => $delta,
+    );
+  }
+  if (!empty($field['text_processing'])) {
+    $format = FILTER_FORMAT_DEFAULT;
+    $form[$field['field_name']][$delta]['format'] = filter_form($format, $form[$field['field_name']][$delta]['value']['#weight'] + 1, array($field['field_name'], $delta, 'format'));
+  }
+  return $form;
+}
+
+/**
+ * Validate a textfield element.
+ */
+function text_textfield_validate($form, &$form_state) {
+  $field = $form['#field'];
+  $items = $form_state['values'];
+  if (is_array($items)) {
+    // Don't save empty fields.
+    if (!empty($form['#multiple'])) {
+      foreach ($items as $delta => $item) {
+        if ($item['value'] == '' && $delta > 0) {
+          unset($form_state['values'][$delta]);
         }
       }
-      return $form;
+    }
+  }
+}
 
-    case 'process form values':
-      // Don't save empty fields except the first value
+/**
+ * Validate a textarea element.
+ */
+function text_textarea_validate($form, &$form_state) {
+  $field = $form['#field'];
+  $items = $form_state['values'];
+  if (is_array($items) && !empty($items)) {
+    // Don't save empty fields.
+    if (!empty($form['#multiple'])) {
       foreach ($items as $delta => $item) {
         if ($item['value'] == '' && $delta > 0) {
-          unset($items[$delta]);
+          unset($form_state['values'][$delta]);
         }
       }
-      break;
+    }
   }
 }
 
@@ -377,4 +415,56 @@
     }
   }
   return $allowed_values[$field['field_name']];
+}
+
+/**
+ * Textfield theme.
+ *
+ * All labels, descriptions, and fieldsets are handled in the theme layer.
+ */
+function theme_text_textfield($element) {
+  $field = $element['#field'];
+  if ($field['multiple']) {
+    foreach ($element as $item) {
+      $output .= theme('textfield', $item);
+    }
+    $fieldset['#type'] = 'fieldset';
+    $fieldset['#title'] = t($field['widget']['label']);
+    $fieldset['#description'] = t($field['widget']['description']);
+    $fieldset['#weight'] = $field['widget']['weight'];
+    $fieldset['#value'] = $output;
+    return theme('fieldset', $fieldset);
+  }
+  else {
+    $element['#title'] = t($field['widget']['label']);
+    $element['#description'] = t($field['widget']['description']);
+    $element['#weight'] = $field['widget']['weight'];
+    return theme('textfield', $element);
+  }
+}
+
+/**
+ * Textarea theme.
+ *
+ * All labels, descriptions, and fieldsets are handled in the theme layer.
+ */
+function theme_text_textarea($element) {
+  $field = $element['#field'];
+  if ($field['multiple']) {
+    foreach ($element as $item) {
+      $output .= theme('textarea', $item);
+    }
+    $fieldset['#type'] = 'fieldset';
+    $fieldset['#title'] = t($field['widget']['label']);
+    $fieldset['#description'] = t($field['widget']['description']);
+    $fieldset['#weight'] = $field['widget']['weight'];
+    $fieldset['#value'] = $output;
+    return theme('fieldset', $fieldset);
+  }
+  else {
+    $element['#title'] = t($field['widget']['label']);
+    $element['#description'] = t($field['widget']['description']);
+    $element['#weight'] = $field['widget']['weight'];
+    return theme('textarea', $element);
+  }
 }
\ No newline at end of file
Index: userreference.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck/userreference.module,v
retrieving revision 1.49
diff -u -r1.49 userreference.module
--- userreference.module	10 Aug 2007 01:57:22 -0000	1.49
+++ userreference.module	16 Aug 2007 11:13:41 -0000
@@ -7,6 +7,20 @@
  */
 
 /**
+ * Implementation of hook_menu().
+ */
+function userreference_menu() {
+  $items = array();
+  $items['user/autocomplete'] = array(
+    'title' => t('User reference autocomplete'),
+    'page callback' => 'userreference_autocomplete',
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK
+  );
+  return $items;
+}
+
+/**
  * Implementation of hook_field_info().
  */
 function userreference_field_info() {
@@ -120,136 +134,117 @@
     'userreference_autocomplete' => array(
       'label' => 'Autocomplete Text Field',
       'field types' => array('userreference'),
+      'callbacks' => array(
+        'default_value' => 'content_default_value',
+        ),
+    ),
+  );
+}
+
+/**
+ * Implementation of hook_elements().
+ */
+function userreference_elements() {
+  return array(
+    'userreference_select' => array(
+      '#input' => TRUE,
+      '#process' => array('userreference_select_process'),
+      '#element_validate' => array('userreference_select_validate'),
+      ),
+    'userreference_autocomplete' => array(
+      '#input' => TRUE,
+      '#process' => array('userreference_autocomplete_process'),
+      '#element_validate' => array('userreference_autocomplete_validate'),
+      ),
+    );
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function userreference_theme() {
+  return array(
+    'userreference_select' => array(
+      'arguments' => array('element' => NULL),
+    ),
+    'userreference_autocomplete' => array(
+      'arguments' => array('element' => NULL),
     ),
   );
 }
 
 /**
  * Implementation of hook_widget().
+ *
+ * Add a form element #type for this widget.
  */
-function userreference_widget($op, &$node, $field, &$items) {
-  if ($field['widget']['type'] == 'userreference_select') {
-    switch ($op) {
-      case 'prepare form values':
-        $items_transposed = content_transpose_array_rows_cols($items);
-        // get rid of null values
-        $items['default uids'] = isset($items_transposed['uid']) ? array_filter($items_transposed['uid']) : array();
-        break;
-
-      case 'form':
-        $form = array();
-
-        $options = _userreference_potential_references($field);
-        if (!$field['required']) {
-          $options = array('none' => t('<none>')) + $options;
-        }
-        if (empty($items['default uids'])) {
-          $items['default uids'][] = 'none';
-        }
-        $form[$field['field_name']] = array('#tree' => TRUE);
-        $form[$field['field_name']]['uids'] = array(
-          '#type' => 'select',
-          '#title' => t($field['widget']['label']),
-          '#default_value' => $items['default uids'],
-          '#multiple' => $field['multiple'],
-          '#size' => $field['multiple'] ? min(count($options), 6) : 0,
-          '#options' => $options,
-          '#required' => $field['required'],
-          '#description' => t($field['widget']['description']),
-        );
-
-        return $form;
-
-      case 'process form values':
-        if ($field['multiple']) {
-          // drop the 'none' option
-          unset($items['uids']['none']);
-          if (!empty($items['uids'])) {
-            $items = array_values(content_transpose_array_rows_cols(array('uid' => $items['uids'])));
-          }
-          else {
-            $items[0]['uid'] = '';
-          }
-        }
-        else {
-          $items[0]['uid'] = ($items['uids'] != 'none') ? $items['uids'] : '';
-        }
-        // Remove the widget's data representation so it isn't saved.
-        unset($items['uids']);
-        foreach ($items as $delta => $item) {
-          $items[$delta]['error_field'] =  $field['field_name'] .'][uids';
-        }
-    }
-  }
-  else {
-    switch ($op) {
-      case 'prepare form values':
-        foreach ($items as $delta => $item) {
-          if (!empty($items[$delta]['uid'])) {
-            $items[$delta]['default user_name'] = db_result(db_query("SELECT name FROM {users} WHERE uid = '%d'", $items[$delta]['uid']));
-          }
-        }
-        break;
+function userreference_widget(&$node, $field, &$items) {
+  $form = array();
+  $form[$field['field_name']] = array(
+    '#tree' => TRUE,
+    '#type' => $field['widget']['type'],
+    '#field' => $field,
+    '#items' => $items,
+    '#weight' => $field['widget']['weight'],
+    '#nid' => $node->nid,
+    );
+  return $form;
+}
 
-      case 'form':
-        $form = array();
-        $form[$field['field_name']] = array('#tree' => TRUE);
-
-        if ($field['multiple']) {
-          $form[$field['field_name']]['#type'] = 'fieldset';
-          $form[$field['field_name']]['#description'] = t($field['widget']['description']);
-          $delta = 0;
-          foreach ($items as $item) {
-            if ($item['uid']) {
-              $form[$field['field_name']][$delta]['user_name'] = array(
-                '#type' => 'textfield',
-                '#title' => ($delta == 0) ? t($field['widget']['label']) : '',
-                '#autocomplete_path' => 'user/autocomplete',
-                '#default_value' => isset($item['default user_name']) ? $item['default user_name'] : '',
-                '#required' => ($delta == 0) ? $field['required'] : FALSE,
-              );
-              $delta++;
-            }
-          }
-          foreach (range($delta, $delta + 2) as $delta) {
-            $form[$field['field_name']][$delta]['user_name'] = array(
-              '#type' => 'textfield',
-              '#title' => ($delta == 0) ? t($field['widget']['label']) : '',
-              '#autocomplete_path' => 'user/autocomplete',
-              '#default_value' => '',
-              '#required' => ($delta == 0) ? $field['required'] : FALSE,
-            );
-          }
-        }
-        else {
-          $form[$field['field_name']][0]['user_name'] = array(
-            '#type' => 'textfield',
-            '#title' => t($field['widget']['label']),
-            '#autocomplete_path' => 'user/autocomplete',
-            '#default_value' => isset($items[0]['default user_name']) ? $items[0]['default user_name'] : '',
-            '#required' => $field['required'],
-            '#description' => t($field['widget']['description']),
-          );
-        }
-        return $form;
+/**
+ * Process a userreference_select element.
+ */
+function userreference_userreference_select_process($form, &$form_state) {
+  $field = $form['#field'];
+  $items = $form['#items'];
+
+  // Pre process the items.
+  $items_transposed = content_transpose_array_rows_cols($items);
+  // get rid of null values
+  $items['default uids'] = isset($items_transposed['uid']) ? array_filter($items_transposed['uid']) : array();
+
+  $options = _userreference_potential_references($field);
+  if (!$field['required']) {
+    $options = array('none' => t('<none>')) + $options;
+  }
+  if (empty($items['default uids'])) {
+    $items['default uids'][] = 'none';
+  }
+  $form[$field['field_name']]['uids'] = array(
+    '#type' => 'select',
+    '#default_value' => $items['default uids'],
+    '#multiple' => $field['multiple'],
+    '#size' => $field['multiple'] ? min(count($options), 6) : 0,
+    '#options' => $options,
+    '#required' => $field['required'],
+  );
+  return $form;
+}
 
-      case 'process form values':
-        foreach ($items as $delta => $item) {
-          $uid = '';
-          if (!empty($items[$delta]['user_name'])) {
-            $uid = db_result(db_query("SELECT uid FROM {users} WHERE name = '%s'", $items[$delta]['user_name']));
-          }
-          // Remove the widget's data representation so it isn't saved.
-          unset($items[$delta]['user_name']);
-          $items[$delta]['uid'] = $uid;
-          $items[$delta]['error_field'] = $field['field_name'] .']['. $delta .'][user_name';
-          // Don't save empty fields except the first value
-          if (empty($uid) && $delta > 0) {
-            unset($items[$delta]);
-          }
-        }
+/**
+ * Process a userreference_select element.
+ */
+function userreference_userreference_autocomplete_process($form, &$form_state) {
+  $field = $form['#field'];
+  $items = $form['#items'];
+
+  foreach ($items as $delta => $item) {
+    if (!empty($items[$delta]['uid'])) {
+      $items[$delta]['default user_name'] = db_result(db_query("SELECT name FROM {users} WHERE uid = '%d'", $items[$delta]['uid']));
     }
   }
+
+  $delta = 0;
+  $max = !empty($field['multiple']) ? sizeof($items) + 2 : 0;
+  foreach (range($delta, $max) as $delta) {
+    $form[$field['field_name']][$delta]['user_name'] = array(
+      '#type' => 'textfield',
+      '#autocomplete_path' => 'user/autocomplete',
+      '#default_value' => isset($items[$delta]['default user_name']) ? $items[0]['default user_name'] : '',
+      '#required' => ($delta == 0) ? $field['required'] : FALSE,
+    );
+  }
+  return $form;
 }
 
 /**
@@ -284,4 +279,56 @@
   $options = views_handler_filter_usercurrent();
   $options = $options + _userreference_potential_references($filterinfo['extra']['field']);
   return $options;
+}
+
+/**
+ * Userreference select theme.
+ *
+ * All labels, descriptions, and fieldsets are handled in the theme layer.
+ */
+function theme_userreference_select($element) {
+  $field = $element['#field'];
+  if ($field['multiple']) {
+    foreach ($element as $item) {
+      $output .= theme('select', $item);
+    }
+    $fieldset['#type'] = 'fieldset';
+    $fieldset['#title'] = t($field['widget']['label']);
+    $fieldset['#description'] = t($field['widget']['description']);
+    $fieldset['#weight'] = $field['widget']['weight'];
+    $fieldset['#value'] = $output;
+    return theme('fieldset', $fieldset);
+  }
+  else {
+    $element['#title'] = t($field['widget']['label']);
+    $element['#description'] = t($field['widget']['description']);
+    $element['#weight'] = $field['widget']['weight'];
+    return theme('select', $element);
+  }
+}
+
+/**
+ * Userreference autocomplete theme.
+ *
+ * All labels, descriptions, and fieldsets are handled in the theme layer.
+ */
+function theme_userreference_autocomplete($element) {
+  $field = $element['#field'];
+  if ($field['multiple']) {
+    foreach ($element as $item) {
+      $output .= theme('textfield', $item);
+    }
+    $fieldset['#type'] = 'fieldset';
+    $fieldset['#title'] = t($field['widget']['label']);
+    $fieldset['#description'] = t($field['widget']['description']);
+    $fieldset['#weight'] = $field['widget']['weight'];
+    $fieldset['#value'] = $output;
+    return theme('fieldset', $fieldset);
+  }
+  else {
+    $element['#title'] = t($field['widget']['label']);
+    $element['#description'] = t($field['widget']['description']);
+    $element['#weight'] = $field['widget']['weight'];
+    return theme('textfield', $element);
+  }
 }
\ No newline at end of file
