Index: modules/field/modules/options/options.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/options/options.module,v
retrieving revision 1.8
diff -u -p -r1.8 options.module
--- modules/field/modules/options/options.module	1 Aug 2009 06:03:12 -0000	1.8
+++ modules/field/modules/options/options.module	7 Aug 2009 08:05:17 -0000
@@ -22,7 +22,10 @@ function options_theme() {
     ),
     'options_none' => array(
       'arguments' => array('widget_type' => NULL, 'field_name' => NULL, 'node_type' => NULL),
-      ),
+    ),
+    'options_autocomplete' => array(
+      'arguments' => array('element' => NULL),
+    ),
   );
 }
 
@@ -40,7 +43,6 @@ function options_theme() {
  * differently.
  */
 function options_field_widget_info() {
-
   return array(
     'options_select' => array(
       'label' => t('Select list'),
@@ -63,6 +65,14 @@ function options_field_widget_info() {
         'multiple values' => FIELD_BEHAVIOR_CUSTOM,
       ),
     ),
+    'options_autocomplete' => array(
+      'label' => t('Autocomplete text field'),
+      'field types' => array('list', 'list_boolean', 'list_text', 'list_number'),
+      'settings' => array('size' => 60, 'autocomplete_path' => 'taxonomy/autocomplete'),
+      'behaviors' => array(
+        'multiple values' => FIELD_BEHAVIOR_CUSTOM,
+      ),
+    ),
   );
 }
 
@@ -81,18 +91,47 @@ function options_elements() {
       '#input' => TRUE,
       '#columns' => array('value'), '#delta' => 0,
       '#process' => array('options_select_elements_process'),
-      ),
+    ),
     'options_buttons' => array(
       '#input' => TRUE,
       '#columns' => array('value'), '#delta' => 0,
       '#process' => array('options_buttons_elements_process'),
-      ),
+    ),
     'options_onoff' => array(
       '#input' => TRUE,
       '#columns' => array('value'), '#delta' => 0,
       '#process' => array('options_onoff_elements_process'),
-      ),
+    ),
+    'options_autocomplete' => array(
+      '#input' => TRUE,
+      '#columns' => array('value'), '#delta' => 0,
+      '#process' => array('options_autocomplete_elements_process'),
+    ),
+  );
+}
+
+/**
+ * Implement hook_field_widget_settings_form().
+ */
+function options_field_widget_settings_form($field, $instance) {
+  $widget = $instance['widget'];
+  $settings = $widget['settings'];
+  if ($widget['type'] == 'options_autocomplete') {
+    $form['size'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Size of autocomplete textfield'),
+      '#default_value' => $settings['size'],
+      '#required' => TRUE,
+      '#element_validate' => array('_element_validate_integer_positive'),
     );
+    $form['autocomplete_path'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Autocomplete path'),
+      '#default_value' => $settings['autocomplete_path'],
+      '#required' => TRUE,
+    );
+  }
+  return $form;
 }
 
 /**
@@ -103,6 +142,7 @@ function options_field_widget(&$form, &$
     '#type' => $instance['widget']['type'],
     '#default_value' => !empty($items) ? $items : array(),
   );
+  // TODO: autocomplete textfield needs to do something here.
   return $element;
 }
 
@@ -255,6 +295,60 @@ function options_onoff_elements_process(
 }
 
 /**
+ * Process an individual element.
+ *
+ * Build the form element. When creating a form using FAPI #process,
+ * note that $element['#value'] is already set.
+ *
+ * The $field and $instance arrays are in $form['#fields'][$element['#field_name']].
+ *
+ * TODO: For widgets to be actual FAPI 'elements', reusable outside of a
+ * 'field' context, they shoudn't rely on $field and $instance. The bits of
+ * information needed to adjust the behavior of the 'element' should be
+ * extracted in hook_field_widget() above.
+ */
+function options_autocomplete_elements_process($element, &$form_state, $form) {
+  $field = $form['#fields'][$element['#field_name']]['field'];
+  $instance = $form['#fields'][$element['#field_name']]['instance'];
+  $field_key = $element['#columns'][0];
+
+  // See if this element is in the database format or the transformed format,
+  // and transform it if necessary.
+  if (is_array($element['#value']) && !array_key_exists($field_key, $element['#value'])) {
+    $element['#value'] = options_data2form($element, $element['#default_value'], $field);
+  }
+  $typed_string = $element['#value'];
+  //$options = options_options($field, $instance);
+  //$multiple = isset($element['#multiple']) ? $element['#multiple'] : $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED;
+
+  $value = array();
+  $element[$field_key] = array(
+    '#type' => 'textfield',
+    '#default_value' => $typed_string,
+    '#autocomplete_path' => !empty($instance['widget']['settings']['autocomplete_path']) ? $instance['widget']['settings']['autocomplete_path'] : NULL,
+    //'#autocomplete_path' => $element['#autocomplete_path'],
+    '#size' => $instance['widget']['settings']['size'],
+    '#attributes' => array('class' => 'text'),
+    '#title' => $element['#title'],
+    '#description' => $element['#description'],
+    '#required' => $element['#required'],
+  );
+  $element[$field_key]['#maxlength'] = !empty($field['settings']['max_length']) ? $field['settings']['max_length'] : NULL;
+
+// Set #element_validate in a way that it will not wipe out other
+  // validation functions already set by other modules.
+  if (empty($element['#element_validate'])) {
+    $element['#element_validate'] = array();
+  }
+  array_unshift($element['#element_validate'], 'options_validate');
+
+  // Make sure field info will be available to the validator which
+  // does not get the values in $form.
+  $form_state['#fields'][$element['#field_name']] = $form['#fields'][$element['#field_name']];
+  return $element;
+}
+
+/**
  * FAPI function to validate options element.
  */
 function options_validate($element, &$form_state) {
@@ -300,7 +394,14 @@ function options_data2form($element, $it
       $keys[] = $value;
     }
   }
-  if ($field['cardinality'] || $element['#type'] == 'options_onoff') {
+  if ($element['#type'] == 'options_autocomplete') {
+    $tags = array();
+    foreach ($items as $item) {
+      $tags[$item['value']] = isset($item['taxonomy_term']) ? $item['taxonomy_term'] : taxonomy_term_load($item['value']);
+    }
+    return taxonomy_implode_tags($tags);
+  }
+  elseif ($field['cardinality'] || $element['#type'] == 'options_onoff') {
     return array($field_key => $keys);
   }
   else {
@@ -322,6 +423,58 @@ function options_form2data($element, $fi
 
   $values = array_values($items);
 
+  if ($element['#type'] == 'options_autocomplete') {
+
+  // Free tagging vocabularies do not send their tids in the form,
+  // so we'll detect them here and process them independently.
+  if (isset($values[0])) {
+    $typed_input = $values[0];
+    unset($values[0]);
+
+//    foreach ($typed_input as $vid => $vid_value) {
+//      $vocabulary = taxonomy_vocabulary_load($vid);
+      $typed_terms = drupal_explode_tags($typed_input);
+
+      $inserted = array();
+      foreach ($typed_terms as $typed_term) {
+        // See if the term exists in the chosen vocabulary
+        // and return the tid; otherwise, add a new record.
+        $possibilities = taxonomy_get_term_by_name($typed_term);
+        $typed_term_tid = NULL; // tid match, if any.
+        foreach ($possibilities as $possibility) {
+//          if ($possibility->vid == $vid) {
+            $typed_term_tid = $possibility->tid;
+//          }
+        }
+
+//        if (!$typed_term_tid) {
+//          $edit = array(
+//            'vid' => $vid,
+//            'name' => $typed_term,
+//            'vocabulary_machine_name' => $vocabulary->machine_name,
+//          );
+//          $term = (object)$edit;
+//          $status = taxonomy_term_save($term);
+//          $typed_term_tid = $term->tid;
+//        }
+
+        // Defend against duplicate, differently cased tags
+        if (!isset($inserted[$typed_term_tid])) {
+          $values[$typed_term_tid] = $typed_term_tid;
+//          db_insert('taxonomy_term_node')
+//            ->fields(array(
+//              'nid' => $node->nid,
+//              'vid' => $node->vid,
+//              'tid' => $typed_term_tid
+//            ))
+//            ->execute();
+//          $inserted[$typed_term_tid] = TRUE;
+        }
+      }
+//    }
+  }
+  }
+
   if ($element['#type'] == 'options_onoff' && ($values[0] === 0)) {
     $keys = array_keys($options);
     $values = array(array_key_exists(0, $keys) ? $keys[0] : NULL);
Index: modules/taxonomy/taxonomy.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v
retrieving revision 1.493
diff -u -p -r1.493 taxonomy.module
--- modules/taxonomy/taxonomy.module	4 Aug 2009 06:50:07 -0000	1.493
+++ modules/taxonomy/taxonomy.module	7 Aug 2009 08:05:56 -0000
@@ -1833,6 +1833,7 @@ function taxonomy_field_info() {
 function taxonomy_field_widget_info_alter(&$info) {
   $info['options_select']['field types'][] = 'taxonomy_term';
   $info['options_buttons']['field types'][] = 'taxonomy_term';
+  $info['options_autocomplete']['field types'][] = 'taxonomy_term';
 }
 
 /**
