diff --git a/handlers/views_handler_filter.inc b/handlers/views_handler_filter.inc
index 82f493c..a3eaebf 100644
--- a/handlers/views_handler_filter.inc
+++ b/handlers/views_handler_filter.inc
@@ -46,6 +46,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'])) {
@@ -86,6 +87,26 @@ class views_handler_filter extends views_handler {
       ),
     );
 
+    // A group is a combination of a filter, an operator an a value
+    // Operating like a single filter.
+    // User can choose from a select box which group want to apply
+    // And views will filter the view according to the defined values
+    // Because it acts as a standar filter, we have to define
+    // an identifier and other settings like the widget and the label.
+    // This settings are saved in other array to allow users to switch
+    // betwen a normal filter an a group of fiters 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;
   }
 
@@ -102,6 +123,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
@@ -112,16 +141,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);
+      }
     }
   }
 
@@ -131,9 +178,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);
+    }
   }
 
   /**
@@ -141,11 +191,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);
+    }
   }
 
   /**
@@ -223,6 +277,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) {
@@ -375,6 +517,46 @@ class views_handler_filter extends views_handler {
   }
 
   /**
+   * Validate the 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'])) {
+        if (!empty($group['title']) && empty($group['value'])) {
+          form_error($form['group_info']['group_items'][$id]['value'], t('The value is required if title for this item is defined.'));
+        }
+        if (empty($group['title']) && !empty($group['value'])) {
+          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() {
@@ -390,6 +572,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.
@@ -441,6 +667,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.
    */
@@ -492,6 +899,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'],
@@ -499,6 +913,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 5f8013e..bca1788 100644
--- a/handlers/views_handler_filter_boolean_operator.inc
+++ b/handlers/views_handler_filter_boolean_operator.inc
@@ -119,6 +119,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 0426a7c..b87889a 100644
--- a/handlers/views_handler_filter_in_operator.inc
+++ b/handlers/views_handler_filter_in_operator.inc
@@ -283,6 +283,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 767654b..0f13bd1 100644
--- a/handlers/views_handler_filter_numeric.inc
+++ b/handlers/views_handler_filter_numeric.inc
@@ -255,6 +255,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 d3bc60d..f5c515f 100644
--- a/handlers/views_handler_filter_string.inc
+++ b/handlers/views_handler_filter_string.inc
@@ -141,6 +141,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/includes/admin.inc b/includes/admin.inc
index e2b38c3..69944b8 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) {
@@ -4452,6 +4519,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 69ba6cf..d879401 100644
--- a/includes/view.inc
+++ b/includes/view.inc
@@ -921,6 +921,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 69301b8..71f5c13 100644
--- a/views.module
+++ b/views.module
@@ -1881,7 +1881,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),
