Index: modules/field/views_handler_field_field.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/modules/field/Attic/views_handler_field_field.inc,v
retrieving revision 1.1.2.27
diff -u -p -r1.1.2.27 views_handler_field_field.inc
--- modules/field/views_handler_field_field.inc	8 Jan 2011 12:42:26 -0000	1.1.2.27
+++ modules/field/views_handler_field_field.inc	19 Jan 2011 19:02:21 -0000
@@ -32,6 +32,24 @@ function _field_view_formatter_options($
  * A field that displays fields.
  */
 class views_handler_field_field extends views_handler_field {
+  function init(&$view, $options) {
+    parent::init($view, $options);
+
+    $field = $this->definition['field_info'];
+    $this->multiple = FALSE;
+    $this->limit_values = FALSE;
+
+    if ($field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
+      $this->multiple = TRUE;
+
+      // We should only limit values if the user hasn't selected "all", or the
+      // value matching field cardinality (which also means "give me everything")
+      if (is_numeric($options['delta_limit']) && ($options['delta_limit'] != $field['cardinality'])) {
+        $this->limit_values = TRUE;
+      }
+    }
+  }
+
   /**
    * Called to add the field to a query.
    *
@@ -82,7 +100,6 @@ class views_handler_field_field extends 
     $this->aliases['entity_type'] = $this->query->add_field(NULL, "'$entity_type'", $entity_type_alias);
 
     $fields = $this->additional_fields;
-
     // We've already added entity_type, so we can remove it from the list.
     $entity_type_key = array_search('entity_type', $fields);
     if ($entity_type_key !== FALSE) {
@@ -100,7 +117,7 @@ class views_handler_field_field extends 
     }
 
     // Add additional fields (and the table join itself) if needed.
-    if ($use_groupby || !empty($this->definition['add fields to query'])) {
+    if ($this->add_field_table($use_groupby)) {
       $this->ensure_my_table();
       $this->add_additional_fields($fields);
 
@@ -111,10 +128,19 @@ class views_handler_field_field extends 
         $this->query->add_where(0, $column, $this->query->options['field_language']);
       }
 
-      // Do we need to filter by delta?
-      if (is_numeric($this->options['delta'])) {
+      // Limit a multiple value field.
+      // Only done when we are explicitly told to add everything to the query.
+      if ($this->limit_values && !empty($this->definition['add fields to query'])) {
         $column = $this->table_alias . ".delta";
-        $this->query->add_where(0, $column, (int) $this->options['delta']);
+        $offset = (int) $this->options['delta_offset'];
+        $or = db_or();
+
+        for ($i = 0; $i < $this->options['delta_limit']; $i++) {
+          $delta = $offset + $i;
+          $or->condition($column, $delta);
+        }
+
+        $this->query->add_where(0, $or);
       }
     }
 
@@ -129,6 +155,38 @@ class views_handler_field_field extends 
   }
 
   /**
+   * Determine if the field table should be added to the query.
+   */
+  function add_field_table($use_groupby) {
+    // Grouping is enabled, or we are explicitly required to do this.
+    if ($use_groupby || !empty($this->definition['add fields to query'])) {
+      return TRUE;
+    }
+    // This a multiple value field, but "group multiple values" is not checked.
+    if ($this->multiple && !$this->options['group_rows']) {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Determine if this field is click sortable.
+   */
+  function click_sortable() {
+    // Not click sortable in any case.
+    if (empty($this->definition['click sortable'])) {
+      return FALSE;
+    }
+    // A field is not click sortable if it's a multiple field with
+    // "group multiple values" checked, since a click sort in that case would
+    // add a join to the field table, which would produce unwanted duplicates.
+    if ($this->multiple && $this->options['group_rows']) {
+      return FALSE;
+    }
+    return TRUE;
+  }
+
+  /**
    * Called to determine what to tell the clicksorter.
    */
   function click_sort($order) {
@@ -148,9 +206,6 @@ class views_handler_field_field extends 
     $field_type = field_info_field_types($field['type']);
     $column_names = array_keys($field['columns']);
 
-    $options['delta'] = array(
-      'default' => '',
-    );
     // If the field has a "value" column, we probably need that one.
     $options['click_sort_column'] = array(
       'default' => in_array('value', $column_names) ? 'value' : '',
@@ -168,6 +223,22 @@ class views_handler_field_field extends 
       'default' => array(),
     );
 
+    // Options used for multiple value fields.
+    $options['group_rows'] = array(
+      'default' => FALSE,
+    );
+    // If we know the exact number of allowed values, then that can be
+    // the default. Otherwise, default to 'all'.
+    $options['delta_limit'] = array(
+      'default' => ($field['cardinality'] > 1) ? $field['cardinality'] : 'all',
+    );
+    $options['delta_offset'] = array(
+      'default' => 0,
+    );
+    $options['delta_reversed'] = array(
+      'default' => FALSE,
+    );
+
     return $options;
   }
 
@@ -178,24 +249,9 @@ class views_handler_field_field extends 
     $formatters = _field_view_formatter_options($field['type']);
     $column_names = array_keys($field['columns']);
 
-    if ($field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
-      if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
-        $type = 'textfield';
-        $options = NULL;
-        $description = t('The sequence number of the value to show. Numbers start from 0. Leave empty to display all values.');
-      }
-      else {
-        $type = 'select';
-        $options = array_merge(array('' => t('All')), range(0, $field['cardinality'] - 1));
-        $description = t('The sequence number of the value to show.');
-      }
-      $form['delta'] = array(
-        '#title' => t('Delta'),
-        '#type' => $type,
-        '#options' => $options,
-        '#default_value' => $this->options['delta'],
-        '#description' => $description,
-      );
+    // If this is a multiple value field, add its options.
+    if ($this->multiple) {
+      $this->multiple_options_form($form, $form_state);
     }
 
     // No need to ask the user anything if the field has only one column.
@@ -255,6 +311,65 @@ class views_handler_field_field extends 
   }
 
   /**
+   * Provide options for multiple value fields.
+   */
+  function multiple_options_form(&$form, &$form_state) {
+    $field = $this->definition['field_info'];
+
+    // @todo Rename 'group_rows' to something like 'one_row_per_entity' as
+    // 'grouping' is understood to be done by value, which this does not do.
+    // If checked, this groups by entity_id not the field value.
+    $form['group_rows'] = array(
+      '#title' => t('Display a single row per entity (multiple value field)'),
+      '#type' => 'checkbox',
+      '#default_value' => $this->options['group_rows'],
+      '#description' => t('If checked, then one row per entity will be used to display the number of values selected below. If unchecked, then a display row will be created for each value in this multiple value field with a single value displayed in each row. However, the number of distinct values displayed from this field will equal the number of values selected below (which, if less than the number of field values, may appear to cause duplicates). This setting is not compatible with click-sorting in table displays nor grouping.'),
+    );
+
+    // Make the string translatable by keeping it as a whole rather than
+    // translating prefix and suffix separately.
+    list($prefix, $suffix) = explode('@count', t('Display @count value(s)'));
+
+    if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
+      $type = 'textfield';
+      $options = NULL;
+      $size = 5;
+    }
+    else {
+      $type = 'select';
+      $options = drupal_map_assoc(range(1, $field['cardinality']));
+      $size = 1;
+    }
+    $form['delta_limit'] = array(
+      '#type' => $type,
+      '#size' => $size,
+      '#field_prefix' => $prefix,
+      '#field_suffix' => $suffix,
+      '#options' => $options,
+      '#default_value' => $this->options['delta_limit'],
+      '#prefix' => '<div class="container-inline">',
+    );
+    list($prefix, $suffix) = explode('@count', t('starting from @count'));
+    $form['delta_offset'] = array(
+      '#type' => 'textfield',
+      '#size' => 5,
+      '#field_prefix' => $prefix,
+      '#field_suffix' => $suffix,
+      '#default_value' => $this->options['delta_offset'],
+      '#description' => t('(first item is 0)'),
+    );
+    $form['delta_reversed'] = array(
+      '#title' => t('Reversed'),
+      '#type' => 'checkbox',
+      '#default_value' => $this->options['delta_reversed'],
+      '#suffix' => '</div>',
+      '#process' => array('form_process_checkbox', 'ctools_dependent_process'),
+      '#dependency' => array('edit-options-group-rows' => array(TRUE)),
+      '#description' => t('(start from last values)'),
+    );
+  }
+
+  /**
    * Extend the groupby form with group columns.
    */
   function groupby_form(&$form, &$form_state) {
@@ -339,13 +454,35 @@ class views_handler_field_field extends 
         return '';
       }
 
-      // The user wants to render only one value from a multivalue field.
-      // The Field API doesn't support this, so we just hack out the
-      // values we don't want to render.
-      if (is_numeric($this->options['delta'])) {
-        $delta = (int) $this->options['delta'];
+      if ($this->limit_values) {
+        // Don't let odd usage cause errors.
+        if ($this->options['delta_limit'] == 0) {
+          return '';
+        }
+
+        $all_values = $display_entity->{$this->definition['field_name']}[$langcode];
+        if ($this->options['delta_reversed']) {
+          $all_values = array_reverse($all_values);
+        }
+
+        // Offset is calculated differently when row grouping for a field is
+        // not enabled. Since there are multiple rows, the delta needs to be
+        // taken into account, so that different values are shown per row.
+        $offset = (int) $this->options['delta_offset'];
+        if (!$this->options['group_rows']) {
+          $delta = $values->{$this->aliases['delta']};
+          $offset += $this->options['delta_limit'] * $delta;
+        }
 
-        $display_entity->{$this->definition['field_name']}[$langcode] = array($display_entity->{$this->definition['field_name']}[$langcode][$delta]);
+        $new_values = array();
+        for ($i = 0; $i < $this->options['delta_limit']; $i++) {
+          $new_delta = $offset + $i;
+
+          if (isset($all_values[$new_delta])) {
+            $new_values[] = $all_values[$new_delta];
+          }
+        }
+        $display_entity->{$this->definition['field_name']}[$langcode] = $new_values;
       }
 
       $display = array(
@@ -381,4 +518,21 @@ class views_handler_field_field extends 
       return LANGUAGE_NONE;
     }
   }
+
+  /**
+   * Return DIV or SPAN based upon the field's element type.
+   *
+   * Fields rendered with the 'group multiple' option use <div> markers,
+   * and thus shouldn't be wrapped in a <span>.
+   */
+  function element_type() {
+    if (!$this->multiple || !$this->options['group_rows']) {
+      return parent::element_type();
+    }
+
+    if (isset($this->definition['element type'])) {
+      return $this->definition['element type'];
+    }
+    return 'div';
+  }
 }
