diff --git a/handlers/views_handler_filter.inc b/handlers/views_handler_filter.inc
index e58fde5..5dd3672 100644
--- a/handlers/views_handler_filter.inc
+++ b/handlers/views_handler_filter.inc
@@ -64,6 +64,7 @@ class views_handler_filter extends views_handler {
 
     $this->operator = $this->options['operator'];
     $this->value = $this->options['value'];
+    $this->group_info = $this->options['group_info']['default_group'];
 
     // Compatibility: The new UI changed several settings.
     if (!empty($options['exposed']) && !empty($options['expose']['optional']) && !isset($options['expose']['required'])) {
@@ -104,6 +105,26 @@ class views_handler_filter extends views_handler {
       ),
     );
 
+    // A group is a combination of a filter, an operator and a value
+    // operating like a single filter.
+    // Users can choose from a select box which group they want to apply.
+    // Views will filter the view according to the defined values.
+    // Because it acts as a standard filter, we have to define
+    // an identifier and other settings like the widget and the label.
+    // This settings are saved in another array to allow users to switch
+    // between a normal filter and a group of filters with a single click.
+    $options['is_grouped'] = array('default' => FALSE);
+    $options['group_info'] = array(
+      'contains' => array(
+        'label' => array('default' => '', 'translatable' => TRUE),
+        'identifier' => array('default' => ''),
+        'optional' => array('default' => TRUE),
+        'widget' => array('default' => 'select'),
+        'remember' => array('default' => 0),
+        'default_group' => array('default' => 'All'),
+        'group_items' => array('default' => array()),
+      ),
+    );
     return $options;
   }
 
@@ -120,6 +141,14 @@ class views_handler_filter extends views_handler {
   function can_expose() { return TRUE; }
 
   /**
+   * Determine if a filter can be converted into a group.
+   * Only exposed filters with operators available can be converted into groups.
+   */
+  function can_build_group() { return $this->is_exposed() && (count($this->operator_options()) > 0); }
+
+  function is_a_group() { return !empty($this->options['is_grouped']); }
+
+  /**
    * Provide the basic form which calls through to subforms.
    * If overridden, it is best to call through to the parent,
    * or to at least make sure all of the functions in this form
@@ -130,16 +159,34 @@ class views_handler_filter extends views_handler {
     if ($this->can_expose()) {
       $this->show_expose_button($form, $form_state);
     }
-    $form['clear_markup_start'] = array(
-      '#markup' => '<div class="clearfix">',
-    );
-    $this->show_operator_form($form, $form_state);
-    $this->show_value_form($form, $form_state);
-    $form['clear_markup_end'] = array(
-      '#markup' => '</div>',
-    );
-    if ($this->can_expose()) {
-      $this->show_expose_form($form, $form_state);
+    if ($this->can_build_group()) {
+      $this->show_build_group_button($form, $form_state);
+    }
+
+    if ($this->is_a_group()) {
+      if ($this->can_build_group()) {
+        $form['clear_markup_start'] = array(
+          '#markup' => '<div class="clearfix">',
+        );
+        // Render the build group form
+        $this->show_build_group_form($form, $form_state);
+        $form['clear_markup_end'] = array(
+          '#markup' => '</div>',
+        );
+      }
+    }
+    else {
+      $form['clear_markup_start'] = array(
+        '#markup' => '<div class="clearfix">',
+      );
+      $this->show_operator_form($form, $form_state);
+      $this->show_value_form($form, $form_state);
+      $form['clear_markup_end'] = array(
+        '#markup' => '</div>',
+      );
+      if ($this->can_expose()) {
+        $this->show_expose_form($form, $form_state);
+      }
     }
   }
 
@@ -149,9 +196,12 @@ class views_handler_filter extends views_handler {
   function options_validate(&$form, &$form_state) {
     $this->operator_validate($form, $form_state);
     $this->value_validate($form, $form_state);
-    if (!empty($this->options['exposed'])) {
+    if (!empty($this->options['exposed']) && !$this->is_a_group()) {
       $this->expose_validate($form, $form_state);
     }
+    if ($this->is_a_group()) {
+      $this->build_group_validate($form, $form_state);
+    }
   }
 
   /**
@@ -159,11 +209,15 @@ class views_handler_filter extends views_handler {
    */
   function options_submit(&$form, &$form_state) {
     unset($form_state['values']['expose_button']); // don't store this.
+    unset($form_state['values']['group_button']); // don't store this.
     $this->operator_submit($form, $form_state);
     $this->value_submit($form, $form_state);
     if (!empty($this->options['exposed'])) {
       $this->expose_submit($form, $form_state);
     }
+    if ($this->is_a_group()) {
+      $this->build_group_submit($form, $form_state);
+    }
   }
 
   /**
@@ -241,6 +295,94 @@ class views_handler_filter extends views_handler {
   function value_submit($form, &$form_state) { }
 
   /**
+   * Shortcut to display the exposed options form.
+   */
+  function show_expose_form(&$form, &$form_state) {
+    if (empty($this->options['exposed'])) {
+      return;
+    }
+
+    $this->expose_form($form, $form_state);
+
+    // When we click the expose button, we add new gadgets to the form but they
+    // have no data in $_POST so their defaults get wiped out. This prevents
+    // these defaults from getting wiped out. This setting will only be TRUE
+    // during a 2nd pass rerender.
+    if (!empty($form_state['force_expose_options'])) {
+      foreach (element_children($form['expose']) as $id) {
+        if (isset($form['expose'][$id]['#default_value']) && !isset($form['expose'][$id]['#value'])) {
+          $form['expose'][$id]['#value'] = $form['expose'][$id]['#default_value'];
+        }
+      }
+    }
+  }
+
+  /**
+   * Shortcut to display the exposed options form.
+   */
+  function show_build_group_form(&$form, &$form_state) {
+    if (empty($this->options['is_grouped'])) {
+      return;
+    }
+
+    $this->build_group_form($form, $form_state);
+
+    // When we click the expose button, we add new gadgets to the form but they
+    // have no data in $_POST so their defaults get wiped out. This prevents
+    // these defaults from getting wiped out. This setting will only be TRUE
+    // during a 2nd pass rerender.
+    if (!empty($form_state['force_build_group_options'])) {
+      foreach (element_children($form['group_info']) as $id) {
+        if (isset($form['group_info'][$id]['#default_value']) && !isset($form['group_info'][$id]['#value'])) {
+          $form['group_info'][$id]['#value'] = $form['group_info'][$id]['#default_value'];
+        }
+      }
+    }
+  }
+
+  /**
+   * Shortcut to display the build_group/hide button.
+   */
+  function show_build_group_button(&$form, &$form_state) {
+
+    $form['group_button'] = array(
+      '#prefix' => '<div class="views-expose clearfix">',
+      '#suffix' => '</div>',
+      // Should always come after the description and the relationship.
+      '#weight' => -190,
+    );
+
+    $form['group_button']['checkbox'] = array(
+      '#theme_wrappers' => array('container'),
+      '#attributes' => array('class' => array('js-only')),
+    );
+    $form['group_button']['checkbox']['checkbox'] = array(
+      '#title' => t('Build a group of filters'),
+      '#description' => t('In a single widget users can choose for a pair of operator|value predefined'),
+      '#type' => 'checkbox',
+    );
+
+    if (empty($this->options['is_grouped'])) {
+      $form['group_button']['button'] = array(
+        '#limit_validation_errors' => array(),
+        '#type' => 'submit',
+        '#value' => t('Build group'),
+        '#submit' => array('views_ui_config_item_form_build_group'),
+      );
+      $form['group_button']['checkbox']['checkbox']['#default_value'] = 0;
+    }
+    else {
+      $form['group_button']['button'] = array(
+        '#limit_validation_errors' => array(),
+        '#type' => 'submit',
+        '#value' => t('Standard filter'),
+        '#submit' => array('views_ui_config_item_form_build_group'),
+      );
+      $form['group_button']['checkbox']['checkbox']['#default_value'] = 1;
+    }
+  }
+
+  /**
    * Shortcut to display the expose/hide button.
    */
   function show_expose_button(&$form, &$form_state) {
@@ -393,6 +535,55 @@ class views_handler_filter extends views_handler {
   }
 
   /**
+   * Validate the build group options form.
+   */
+  function build_group_validate($form, &$form_state) {
+    foreach ($form_state['values']['options']['group_info']['group_items'] as $id => $group) {
+      if (empty($group['remove'])) {
+
+        // Check if the title is defined but value wasn't defined
+        if (!empty($group['title'])) {
+          if ((!is_array($group['value']) && empty($group['value'])) || (is_array($group['value']) && count(array_filter($group['value'])) == 0)) {
+            form_error($form['group_info']['group_items'][$id]['value'], t('The value is required if title for this item is defined.'));
+          }
+        }
+
+        // Check if the value is defined but title wasn't defined
+        if ((!is_array($group['value']) && !empty($group['value'])) || (is_array($group['value']) && count(array_filter($group['value'])) > 0)) {
+          if (empty($group['title'])) {
+            form_error($form['group_info']['group_items'][$id]['title'], t('The title is required if value for this item is defined.'));
+          }
+        }
+
+      }
+    }
+  }
+
+  function build_group_submit($form, &$form_state) {
+    $groups = array();
+    uasort($form_state['values']['options']['group_info']['group_items'], 'drupal_sort_weight');
+    // Filter out removed items.
+    $new_id = 1;
+    $new_default = 'All';
+    foreach ($form_state['values']['options']['group_info']['group_items'] as $id => $group) {
+      if (empty($group['remove'])) {
+        // Don't store this
+        unset($group['remove']);
+        unset($group['weight']);
+        $groups[$new_id] = $group;
+        if ($form_state['values']['options']['group_info']['default_group'] == $id) {
+          $new_default = $new_id;
+        }
+      }
+      $new_id++;
+    }
+    if ($new_default != 'All') {
+      $form_state['values']['options']['group_info']['default_group'] = $new_default;
+    }
+    $form_state['values']['options']['group_info']['group_items'] = $groups;
+   }
+
+  /**
    * Provide default options for exposed filters.
    */
   function expose_options() {
@@ -408,6 +599,50 @@ class views_handler_filter extends views_handler {
   }
 
   /**
+   * Provide default options for exposed filters.
+   */
+  function build_group_options() {
+    $this->options['group_info'] = array(
+      'contains' => array(
+        'label' => '',
+        'identifier' => $this->options['id'],
+        'optional' => TRUE,
+        'widget' => 'select',
+        'remember' => FALSE,
+        'default_group' => 'All',
+        'group_items' => array(),
+      )
+    );
+  }
+
+  /**
+   * Build a form containing a group of operator | values to apply as a
+   * single filter.
+   */
+  function group_form(&$form, &$form_state) {
+    if (!empty($this->options['group_info']['optional'])) {
+      $any_label = variable_get('views_exposed_filter_any_label', 'old_any') == 'old_any' ? '<Any>' : t('- Any -');
+      $groups = array('All' => $any_label);
+    }
+    foreach ($this->options['group_info']['group_items'] as $id => $group) {
+      if (!empty($group['title'])) {
+        $groups[$id] = t($group['title']);
+      }
+    }
+
+    if (count($groups)) {
+      $value = $this->options['group_info']['identifier'];
+
+      $form[$value] = array(
+        '#type' => $this->options['group_info']['widget'],
+        '#default_value' => $this->group_info,
+        '#options' => $groups,
+      );
+      $this->options['expose']['label'] = '';
+    }
+  }
+
+  /**
    * Render our chunk of the exposed filter form when selecting
    *
    * You can override this if it doesn't do what you expect.
@@ -459,6 +694,187 @@ class views_handler_filter extends views_handler {
   }
 
   /**
+   * Build the form to let users create the group of exposed filters.
+   * This form is displayed when users click on button 'Build group'
+   */
+  function build_group_form(&$form, &$form_state) {
+    if (empty($this->options['exposed']) || empty($this->options['is_grouped'])) {
+      return;
+    }
+    $form['#theme'] = 'views_ui_build_group_filter_form';
+
+    // #flatten will move everything from $form['group_info'][$key] to $form[$key]
+    // prior to rendering. That's why the pre_render for it needs to run first,
+    // so that when the next pre_render (the one for fieldsets) runs, it gets
+    // the flattened data.
+    array_unshift($form['#pre_render'], 'views_ui_pre_render_flatten_data');
+    $form['group_info']['#flatten'] = TRUE;
+
+    if (!empty($this->options['group_info']['identifier'])) {
+      $identifier = $this->options['group_info']['identifier'];
+    }
+    else {
+      $identifier = 'group_' . $this->options['expose']['identifier'];
+    }
+    $form['group_info']['identifier'] = array(
+      '#type' => 'textfield',
+      '#default_value' => $identifier,
+      '#title' => t('Filter identifier'),
+      '#size' => 40,
+      '#description' => t('This will appear in the URL after the ? to identify this filter. Cannot be blank.'),
+      '#fieldset' => 'more',
+    );
+    $label = !empty($this->options['group_info']['label']) ? $this->options['group_info']['label'] : $this->options['expose']['label'];
+    $form['group_info']['label'] = array(
+      '#type' => 'textfield',
+      '#default_value' => $this->options['group_info']['label'],
+      '#title' => t('Label'),
+      '#size' => 40,
+    );
+    $form['group_info']['optional'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Optional'),
+      '#description' => t('This exposed filter is optional and will have added options to allow it not to be set.'),
+      '#default_value' => $this->options['group_info']['optional'],
+    );
+    $form['group_info']['widget'] = array(
+      '#type' => 'radios',
+      '#default_value' => $this->options['group_info']['widget'],
+      '#title' => t('Widget'),
+      '#options' => array(
+        'radios' => t('Radios'),
+        'select' => t('Select'),
+      ),
+      '#description' => t('Select which kind of widget will be used to render the group of filters'),
+    );
+    $form['group_info']['remember'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Remember'),
+      '#description' => t('Remember the last setting the user gave this filter.'),
+      '#default_value' => $this->options['group_info']['remember'],
+    );
+
+    $any_label = variable_get('views_exposed_filter_any_label', 'old_any') == 'old_any' ? '<Any>' : t('- Any -');
+    $groups = array('All' => $any_label);
+
+    // Provide 3 options to start when we are in a new group
+    if (count($this->options['group_info']['group_items']) == 0) {
+      $this->options['group_info']['group_items'] = array(array(), array(), array());
+    }
+
+    // After the general settings, comes a table with all the existent groups
+    $default_weight = 0;
+    foreach ($this->options['group_info']['group_items'] as $item_id => $item) {
+      if (!empty($form_state['values']['options']['group_info']['group_items'][$item_id]['remove'])) {
+        continue;
+      }
+      // Each rows contains three widgets:
+      // a) The title, where users define how they identify a pair of operator | value
+      // b) The operator
+      // c) The value (or values) to use in the filter with the selected operator
+
+      // In each row, we have to display the operator form and the value form
+      // $row acts as a fake form to render each widget in a row
+      $row = array();
+      $groups[$item_id] = '';
+      $this->operator_form($row, $form_state);
+      $row['operator']['#type'] = 'select';
+      $row['operator']['#title'] = '';
+      $this->value_form($row, $form_state);
+
+      // Fix the dependencies to update value forms when operators changes.
+      // This is needed because forms are inside a new form and their ids changes
+      // Dependencies are used when operator changes from to 'Between', 'Not Between', etc
+      // And two or more widgets are displayed
+      $without_childrens = TRUE;
+      foreach (element_children($row['value']) as $children) {
+        if (isset($row['value'][$children]['#dependency']['edit-options-operator'])) {
+          $row['value'][$children]['#dependency']["edit-options-group-info-group-items-$item_id-operator"] = $row['value'][$children]['#dependency']['edit-options-operator'];
+          unset($row['value'][$children]['#dependency']['edit-options-operator']);
+          $row['value'][$children]['#title'] = '';
+
+          if (!empty($this->options['group_info']['group_items'][$item_id]['value'][$children])) {
+            $row['value'][$children]['#default_value'] = $this->options['group_info']['group_items'][$item_id]['value'][$children];
+          }
+        }
+        $without_childrens = FALSE;
+      }
+
+      if ($without_childrens) {
+        if (!empty($this->options['group_info']['group_items'][$item_id]['value'])) {
+          $row['value']['#default_value'] = $this->options['group_info']['group_items'][$item_id]['value'];
+        }
+      }
+
+      if (!empty($this->options['group_info']['group_items'][$item_id]['operator'])) {
+        $row['operator']['#default_value'] = $this->options['group_info']['group_items'][$item_id]['operator'];
+      }
+
+      $default_title = '';
+      if (!empty($this->options['group_info']['group_items'][$item_id]['title'])) {
+        $default_title = $this->options['group_info']['group_items'][$item_id]['title'];
+      }
+
+      // Per item group we have a title that identifies it
+      $form['group_info']['group_items'][$item_id] = array(
+        'title' => array(
+          '#type' => 'textfield',
+          '#size' => 20,
+          '#default_value' => $default_title,
+        ),
+        'operator' => $row['operator'],
+        'value' => $row['value'],
+        'remove' => array(
+          '#type' => 'checkbox',
+          '#id' => 'views-remove-' . $item_id,
+          '#attributes' => array('class' => array('views-remove-checkbox')),
+          '#default_value' => 0,
+        ),
+        'weight' => array(
+          '#type' => 'weight',
+          '#delta' => 10,
+          '#default_value' => $default_weight++,
+          '#attributes' => array('class' => array('weight')),
+        ),
+      );
+    }
+
+    // From all groups, let chose which is the default
+    $form['group_info']['default_group'] = array(
+      '#type' => 'radios',
+      '#options' => $groups,
+      '#default_value' => $this->options['group_info']['default_group'],
+      '#required' => TRUE,
+      '#empty_value' => 'All',
+      '#empty_option' => 'All',
+    );
+
+    $form['group_info']['add_group'] = array(
+      '#prefix' => '<div class="views-build-group clear-block">',
+      '#suffix' => '</div>',
+      '#type' => 'submit',
+      '#value' => t('Add another item'),
+      '#submit' => array('views_ui_config_item_form_add_group'),
+    );
+
+    $js = array();
+    $js['tableDrag']['views-filter-groups']['weight'][0] = array(
+      'target' => 'weight',
+      'source' => NULL,
+      'relationship' => 'sibling',
+      'action' => 'order',
+      'hidden' => TRUE,
+      'limit' => 0,
+    );
+    if (!empty($form_state['js settings']) && is_array($js)) {
+      $form_state['js settings'] = array_merge($form_state['js settings'], $js);
+    }
+    else {
+      $form_state['js settings'] = $js;
+    }
+  }
+
+  /**
    * Make some translations to a form item to make it more suitable to
    * exposing.
    */
@@ -510,6 +926,13 @@ class views_handler_filter extends views_handler {
       return;
     }
 
+    if ($this->is_a_group()) {
+      return array(
+        'value' => $this->options['group_info']['identifier'],
+        'label' => $this->options['group_info']['label'],
+      );
+    }
+
     return array(
       'operator' => $this->options['expose']['operator_id'],
       'value' => $this->options['expose']['identifier'],
@@ -517,6 +940,69 @@ class views_handler_filter extends views_handler {
     );
   }
 
+  function convert_exposed_input(&$input) {
+    // Here is where the conversion from grouped filters into standard
+    // filters ones occurs.
+
+    // When a filter is a group, we find the set of operator and values
+    // that the choosed item represents, and inform views that a normal
+    // filter was submitted by telling the operator and the value selected.
+    if ($this->is_a_group()) {
+      $selected_group = $input[$this->options['group_info']['identifier']];
+      if ($selected_group == 'All' && !empty($this->options['group_info']['optional'])) {
+        return NULL;
+      }
+      if ($selected_group != 'All' && empty($this->options['group_info']['group_items'][$selected_group])) {
+        return FALSE;
+      }
+      if (isset($selected_group) && isset($this->options['group_info']['group_items'][$selected_group])) {
+        $input[$this->options['expose']['operator']] = $this->options['group_info']['group_items'][$selected_group]['operator'];
+        $input[$this->options['expose']['identifier']] = $this->options['group_info']['group_items'][$selected_group]['value'];
+        $this->options['expose']['use_operator'] = TRUE;
+
+        $this->group_info = $input[$this->options['group_info']['identifier']];
+        return TRUE;
+      }
+      else {
+        return FALSE;
+      }
+    }
+  }
+
+  function store_group_input($input, $status) {
+    if (!$this->is_a_group() || empty($this->options['group_info']['identifier'])) {
+      return TRUE;
+    }
+
+    if (empty($this->options['group_info']['remember'])) {
+      return;
+    }
+
+    // Figure out which display id is responsible for the filters, so we
+    // know where to look for session stored values.
+    $display_id = ($this->view->display_handler->is_defaulted('filters')) ? 'default' : $this->view->current_display;
+
+    // false means that we got a setting that means to recuse ourselves,
+    // so we should erase whatever happened to be there.
+    if ($status === FALSE && isset($_SESSION['views'][$this->view->name][$display_id])) {
+      $session = &$_SESSION['views'][$this->view->name][$display_id];
+
+      if (isset($session[$this->options['group_info']['identifier']])) {
+        unset($session[$this->options['group_info']['identifier']]);
+      }
+    }
+
+    if ($status !== FALSE) {
+      if (!isset($_SESSION['views'][$this->view->name][$display_id])) {
+        $_SESSION['views'][$this->view->name][$display_id] = array();
+      }
+
+      $session = &$_SESSION['views'][$this->view->name][$display_id];
+
+      $session[$this->options['group_info']['identifier']] = $input[$this->options['group_info']['identifier']];
+    }
+  }
+
   /**
    * Check to see if input from the exposed filters should change
    * the behavior of this filter.
diff --git a/handlers/views_handler_filter_boolean_operator.inc b/handlers/views_handler_filter_boolean_operator.inc
index 63071c6..ef916fa 100644
--- a/handlers/views_handler_filter_boolean_operator.inc
+++ b/handlers/views_handler_filter_boolean_operator.inc
@@ -121,6 +121,9 @@ class views_handler_filter_boolean_operator extends views_handler_filter {
   }
 
   function admin_summary() {
+    if ($this->is_a_group()) {
+      return t('grouped');
+    }
     if (!empty($this->options['exposed'])) {
       return t('exposed');
     }
diff --git a/handlers/views_handler_filter_in_operator.inc b/handlers/views_handler_filter_in_operator.inc
index 8054542..a010593 100644
--- a/handlers/views_handler_filter_in_operator.inc
+++ b/handlers/views_handler_filter_in_operator.inc
@@ -284,6 +284,9 @@ class views_handler_filter_in_operator extends views_handler_filter {
   }
 
   function admin_summary() {
+    if ($this->is_a_group()) {
+      return t('grouped');
+    }
     if (!empty($this->options['exposed'])) {
       return t('exposed');
     }
diff --git a/handlers/views_handler_filter_numeric.inc b/handlers/views_handler_filter_numeric.inc
index 71d8b9e..b0a751f 100644
--- a/handlers/views_handler_filter_numeric.inc
+++ b/handlers/views_handler_filter_numeric.inc
@@ -257,6 +257,9 @@ class views_handler_filter_numeric extends views_handler_filter {
   }
 
   function admin_summary() {
+    if ($this->is_a_group()) {
+      return t('grouped');
+    }
     if (!empty($this->options['exposed'])) {
       return t('exposed');
     }
diff --git a/handlers/views_handler_filter_string.inc b/handlers/views_handler_filter_string.inc
index 0674045..fd560a2 100644
--- a/handlers/views_handler_filter_string.inc
+++ b/handlers/views_handler_filter_string.inc
@@ -143,6 +143,9 @@ class views_handler_filter_string extends views_handler_filter {
   }
 
   function admin_summary() {
+    if ($this->is_a_group()) {
+      return t('grouped');
+    }
     if (!empty($this->options['exposed'])) {
       return t('exposed');
     }
diff --git a/help/filter.html b/help/filter.html
index 752617f..5984bcd 100644
--- a/help/filter.html
+++ b/help/filter.html
@@ -15,6 +15,19 @@ When you click the Rearrange Icon you can first rearrange your filters, easily d
 
 When you want that the user to select their own filter, you can expose the filter. A selection box will show for the user and they will be able to select one item. After that the view will reload and only the selected item will be displayed. You can also choose to expose the selection to a block, see <a href="topic:views/exposed-form">here</a>.
 
+For exposed filters, you can create a grouped filter. When filters are in a group, each item of the group represents a set of operators and values. The following table illustrates how this feature works. The values of the first column of the table are displayed as options of a single select box:
+
+<table>
+<thead>
+<tr><th>What the user see</th><th>What views does</th></tr>
+</thead>
+<tbody>
+<tr><td>Is lower than 10</td><td><strong>Operator:</strong> Is Lower than. <strong>Value:</strong> 10</td></tr>
+<tr><td>Is between 10 and 20</td><td><strong>Operator:</strong> Is between. <strong>Value:</strong> 10 and 20</td></tr>
+<tr><td>Is greater than 20</td><td><strong>Operator:</strong> Is Greater. <strong>Value:</strong> 20</td></tr>
+</tbody>
+</table>
+
 Taxonomy filters have been significantly altered in Views 7.x-3.x. D7 significantly re-organized taxonomy, there was a lot of duplicate taxonomy related fields and filters. Some of them were removed to try and reduce confusion between them. Implicit relationships to taxonomy have been removed, in favor of explicit relationships. If the filters you can find don't do what you need, try adding either the related taxonomy terms relationship, or a relationship on the specific taxonomy field. That will give you the term specific filters.
 
 You can override the complete filter section - see <a href="topic:views/overrides">here</a> for more information.
diff --git a/includes/admin.inc b/includes/admin.inc
index c608b29..5c10416 100644
--- a/includes/admin.inc
+++ b/includes/admin.inc
@@ -3466,6 +3466,7 @@ function theme_views_ui_expose_filter_form($variables) {
 
   $output = drupal_render($form['form_description']);
   $output .= drupal_render($form['expose_button']);
+  $output .= drupal_render($form['group_button']);
   if (isset($form['required'])) {
     $output .= drupal_render($form['required']);
   }
@@ -3496,6 +3497,72 @@ function theme_views_ui_expose_filter_form($variables) {
 }
 
 /**
+ * Theme the build group filter form.
+ */
+function theme_views_ui_build_group_filter_form($variables) {
+  $form = $variables['form'];
+  $more = drupal_render($form['more']);
+
+  $output = drupal_render($form['form_description']);
+  $output .= drupal_render($form['expose_button']);
+  $output .= drupal_render($form['group_button']);
+  if (isset($form['required'])) {
+    $output .= drupal_render($form['required']);
+  }
+
+  $output .= drupal_render($form['operator']);
+  $output .= drupal_render($form['value']);
+
+  $output .= '<div class="views-left-40">';
+  $output .= drupal_render($form['optional']);
+  $output .= drupal_render($form['remember']);
+  $output .= '</div>';
+
+  $output .= '<div class="views-right-60">';
+  $output .= drupal_render($form['widget']);
+  $output .= drupal_render($form['label']);
+  $output .= '</div>';
+
+
+  $header = array(
+    t('Default'),
+    t('Weight'),
+    t('Title'),
+    t('Operator'),
+    t('Value'),
+    t('Operations'),
+  );
+
+  $form['default_group'] = form_process_radios($form['default_group']);
+
+  $rows[] = array(
+    drupal_render($form['default_group']['All']),
+    '',
+    array(
+      'data' => t('- Any -'),
+      'colspan' => 4,
+    ),
+  );
+
+  foreach (element_children($form['group_items']) as $group_id) {
+    $form['group_items'][$group_id]['value']['#title'] = '';
+    $data = array(
+      'default' => drupal_render($form['default_group'][$group_id]),
+      'weight' => drupal_render($form['group_items'][$group_id]['weight']),
+      'title' => drupal_render($form['group_items'][$group_id]['title']),
+      'operator' => drupal_render($form['group_items'][$group_id]['operator']),
+      'value' => drupal_render($form['group_items'][$group_id]['value']),
+      'remove' => drupal_render($form['group_items'][$group_id]['remove']) . l('<span>' . t('Remove') . '</span>', 'javascript:void()', array('attributes' => array('id' => 'views-remove-link-' . $group_id, 'class' => array('views-hidden', 'views-button-remove', 'views-groups-remove-link', 'views-remove-link'), 'alt' => t('Remove this item'), 'title' => t('Remove this item')), 'html' => true)),
+    );
+    $rows[] = array('data' => $data, 'id' => 'views-row-' . $group_id, 'class' => array('draggable'));
+  }
+  $table = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('class' => array('views-filter-groups'), 'id' => 'views-filter-groups'))) . drupal_render($form['add_group']);
+  drupal_add_tabledrag('views-filter-groups', 'order', 'sibling', 'weight');
+  $render_form = drupal_render_children($form);
+  return $output . $render_form . $table . $more;
+}
+
+/**
  * Submit handler for rearranging form
  */
 function views_ui_rearrange_form_submit($form, &$form_state) {
@@ -4456,6 +4523,44 @@ function views_ui_config_item_form_expose($form, &$form_state) {
 }
 
 /**
+ * Override handler for views_ui_edit_display_form
+ */
+function views_ui_config_item_form_build_group($form, &$form_state) {
+  $item = &$form_state['handler']->options;
+  // flip
+  $item['is_grouped'] = empty($item['is_grouped']);
+
+  // If necessary, set new defaults:
+  if ($item['is_grouped']) {
+    $form_state['handler']->build_group_options();
+  }
+
+  $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
+
+  views_ui_cache_set($form_state['view']);
+  $form_state['rerender'] = TRUE;
+  $form_state['rebuild'] = TRUE;
+  $form_state['force_build_group_options'] = TRUE;
+}
+
+/**
+ * Add a new group to the exposed filter groups.
+ */
+function views_ui_config_item_form_add_group($form, &$form_state) {
+  $item =& $form_state['handler']->options;
+
+  // Add a new row.
+  $item['group_info']['group_items'][] = array();
+
+  $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
+
+  views_ui_cache_set($form_state['view']);
+  $form_state['rerender'] = TRUE;
+  $form_state['rebuild'] = TRUE;
+  $form_state['force_build_group_options'] = TRUE;
+}
+
+/**
  * Form to config_item items in the views UI.
  */
 function views_ui_config_item_extra_form($form, &$form_state) {
diff --git a/includes/handlers.inc b/includes/handlers.inc
index 8727eb3..29d6e6f 100644
--- a/includes/handlers.inc
+++ b/includes/handlers.inc
@@ -650,6 +650,8 @@ class views_handler extends views_object {
     return !empty($this->options['exposed']);
   }
 
+  function is_a_group() { return FALSE; }
+
   /**
    * Take input from exposed handlers and assign to this handler, if necessary.
    */
diff --git a/includes/view.inc b/includes/view.inc
index 0a2e7bc..7ff183b 100644
--- a/includes/view.inc
+++ b/includes/view.inc
@@ -928,6 +928,14 @@ class view extends views_db_object {
       if (!empty($handlers[$id]) && is_object($handlers[$id])) {
         // Give this handler access to the exposed filter input.
         if (!empty($this->exposed_data)) {
+          $converted = FALSE;
+          if ($handlers[$id]->is_a_group()) {
+            $converted = $handlers[$id]->convert_exposed_input($this->exposed_data);
+            $handlers[$id]->store_group_input($this->exposed_data, $converted);
+            if (!$converted) {
+              continue;
+            }
+          }
           $rc = $handlers[$id]->accept_exposed_input($this->exposed_data);
           $handlers[$id]->store_exposed_input($this->exposed_data, $rc);
           if (!$rc) {
diff --git a/js/views-admin.js b/js/views-admin.js
index 104239a..83df7ad 100644
--- a/js/views-admin.js
+++ b/js/views-admin.js
@@ -806,6 +806,17 @@ Drupal.behaviors.viewsRemoveIconClass.attach = function (context, settings) {
 }
 
 /**
+ * Check the checkbox associated to a link when the link is clicked.
+ */
+Drupal.behaviors.viewsRemoveFromCheckbox = {};
+Drupal.behaviors.viewsRemoveFromCheckbox.attach = function (context, settings) {
+  jQuery('a.views-remove-link', context).click(function() {
+    var $ = jQuery;
+    $(this).parent().find('input.views-remove-checkbox').attr('checked', true);
+  });
+}
+
+/**
  * Change "Expose filter" buttons into checkboxes.
  */
 Drupal.behaviors.viewsUiCheckboxify = {};
@@ -817,6 +828,11 @@ Drupal.behaviors.viewsUiCheckboxify.attach = function (context, settings) {
   for (i = 0; i < length; i++) {
     new Drupal.viewsUi.Checkboxifier($buttons[i]);
   }
+  $buttons = $('#edit-options-group-button-button').once('views-ui-checkboxify');
+  length = $buttons.length;
+  for (i = 0; i < length; i++) {
+    new Drupal.viewsUi.Checkboxifier($buttons[i]);
+  }
 };
 
 /**
diff --git a/views.module b/views.module
index 9ecedb9..141fa0e 100644
--- a/views.module
+++ b/views.module
@@ -1885,7 +1885,18 @@ function views_exposed_form($form, &$form_state) {
   foreach ($view->display_handler->handlers as $type => $value) {
     foreach ($view->$type as $id => $handler) {
       if ($handler->can_expose() && $handler->is_exposed()) {
-        $handler->exposed_form($form, $form_state);
+        // Grouped exposed filters have their own forms.
+        // Instead of render the standard exposed form, a new Select or
+        // Radio form field is rendered with the available groups.
+        // When an user choose an option the selected value is splitted
+        // into the operator and value that the item represents
+        if ($handler->is_a_group()) {
+          $handler->group_form($form, $form_state);
+          $id = $handler->options['group_info']['identifier'];
+        }
+        else {
+          $handler->exposed_form($form, $form_state);
+        }
         if ($info = $handler->exposed_info()) {
           $form['#info']["$type-$id"] = $info;
         }
diff --git a/views_ui.module b/views_ui.module
index a4480a9..8fab7df 100644
--- a/views_ui.module
+++ b/views_ui.module
@@ -203,6 +203,12 @@ function views_ui_theme() {
       'file' => "includes/admin.inc",
     ),
 
+    // Group of filters
+    'views_ui_build_group_filter_form' => array(
+      'render element' => 'form',
+      'file' => 'includes/admin.inc',
+    ),
+
     // tab themes
     'views_tabset' => array(
       'variables' => array('tabs' => NULL),
