=== modified file 'includes/form.inc'
--- includes/form.inc	2009-12-17 21:59:31 +0000
+++ includes/form.inc	2009-12-25 07:45:49 +0000
@@ -280,6 +280,7 @@ function form_state_defaults() {
     'cache'=> FALSE,
     'method' => 'post',
     'groups' => array(),
+    'validate_exclusively_on_button' => array(),
   );
 }
 
@@ -412,6 +413,7 @@ function form_state_keys_no_cache() {
     'method',
     'submit_handlers',
     'submitted',
+    'validate_exclusively_on_button',
     'validate_handlers',
     'values',
   );
@@ -786,6 +788,12 @@ function drupal_validate_form($form_id, 
     }
   }
 
+  // Only keep the validate_exclusively_on_button state if there is a section
+  // relating to the pressed button.
+  if (!empty($form_state['validate_exclusively_on_button']) && !in_array($form_state['clicked_button']['#value'], $form_state['validate_exclusively_on_button'])) {
+    $form_state['validate_exclusively_on_button'] = array();
+  }
+
   _form_validate($form, $form_state, $form_id);
   $validated_forms[$form_id] = TRUE;
 }
@@ -884,7 +892,9 @@ function _form_validate(&$elements, &$fo
 
   // Recurse through all children.
   foreach (element_children($elements) as $key) {
-    if (isset($elements[$key]) && $elements[$key]) {
+    // If there was no button-specific validate section specified or this
+    // element is inside that section then validate.
+    if (empty($form_state['validate_exclusively_on_button']) || (isset($elements[$key]['#validate_exclusively_on_button']) && in_array($form_state['clicked_button']['#value'], $elements[$key]['#validate_exclusively_on_button']))) {
       _form_validate($elements[$key], $form_state);
     }
   }
@@ -968,7 +978,7 @@ function form_execute_handlers($type, &$
     $handlers = $form_state[$type . '_handlers'];
   }
   // Otherwise, check for a form-level handler.
-  elseif (isset($form['#' . $type])) {
+  elseif (isset($form['#' . $type]) && ($type != 'validate' || empty($form_state['validate_exclusively_on_button']))) {
     $handlers = $form['#' . $type];
   }
   else {
@@ -1006,7 +1016,7 @@ function form_execute_handlers($type, &$
  * @param $message
  *   The error message to present to the user.
  * @return
- *   Return value is for internal use only. To get a list of errors, use 
+ *   Return value is for internal use only. To get a list of errors, use
  *   form_get_errors() or form_get_error().
  */
 function form_set_error($name = NULL, $message = '') {
@@ -1170,6 +1180,10 @@ function form_builder($form_id, $element
       // later.
       unset($element['#sorted']);
     }
+    if (isset($element['#validate_exclusively_on_button'])) {
+      $element[$key]['#validate_exclusively_on_button'] = $element['#validate_exclusively_on_button'];
+      $form_state['validate_exclusively_on_button'] = array_merge($form_state['validate_exclusively_on_button'], $element['#validate_exclusively_on_button']);
+    }
     $element[$key] = form_builder($form_id, $element[$key], $form_state);
     $count++;
   }
@@ -3261,7 +3275,7 @@ function batch_process($redirect = NULL,
   $batch =& batch_get();
 
   drupal_theme_initialize();
-  
+
   if (isset($batch)) {
     // Add process information
     $process_info = array(
@@ -3276,7 +3290,7 @@ function batch_process($redirect = NULL,
     );
     $batch += $process_info;
 
-    // The batch is now completely built. Allow other modules to make changes to the 
+    // The batch is now completely built. Allow other modules to make changes to the
     // batch so that it is easier to reuse batch processes in other enviroments.
     drupal_alter('batch', $batch);
 

=== modified file 'modules/field/field.form.inc'
--- modules/field/field.form.inc	2009-12-21 13:47:31 +0000
+++ modules/field/field.form.inc	2009-12-25 07:07:21 +0000
@@ -227,6 +227,7 @@ function field_multiple_value_form($fiel
           '#field_name' => $field_name,
           '#language' => $langcode,
         );
+        $field_elements[$field_name]['#validate_exclusively_on_button'] = array(t('Add another item'));
       }
     }
   }

=== modified file 'modules/poll/poll.module'
--- modules/poll/poll.module	2009-12-14 20:38:15 +0000
+++ modules/poll/poll.module	2009-12-25 07:08:53 +0000
@@ -234,6 +234,7 @@ function poll_form($node, &$form_state) 
   // Add a wrapper for the choices and more button.
   $form['choice_wrapper'] = array(
     '#tree' => FALSE,
+    '#validate_exclusively_on_button' => array(t('More choices')),
     '#weight' => -4,
     '#prefix' => '<div class="clearfix" id="poll-choice-wrapper">',
     '#suffix' => '</div>',
@@ -901,4 +902,3 @@ function poll_user_cancel($edit, $accoun
       break;
   }
 }
-

=== modified file 'modules/simpletest/tests/form.test'
--- modules/simpletest/tests/form.test	2009-12-17 17:18:02 +0000
+++ modules/simpletest/tests/form.test	2009-12-25 07:19:51 +0000
@@ -231,6 +231,19 @@ class FormValidationTestCase extends Dru
     $this->assertNoFieldByName('name', t('Form element was hidden.'));
     $this->assertText('Name value: element_validate_access', t('Value for inaccessible form element exists.'));
   }
+
+  /**
+   * Test button press validates a section exclusively.
+   */
+  function testValidateExclusivelyOnButton() {
+    $edit = array('test' => 'pass');
+    $this->drupalPost('form-test/validate-exclusively-on-button', $edit, t('Partial validate'));
+    $this->assertNoText(t('!name field is required.', array('!name' => 'Title')));
+    $this->assertText('Test is validated');
+    $this->drupalPost('form-test/validate-exclusively-on-button', $edit, t('Full validate'));
+    $this->assertText(t('!name field is required.', array('!name' => 'Title')));
+    $this->assertText('Test is validated');
+  }
 }
 
 /**

=== modified file 'modules/simpletest/tests/form_test.module'
--- modules/simpletest/tests/form_test.module	2009-12-17 17:18:02 +0000
+++ modules/simpletest/tests/form_test.module	2009-12-25 07:47:41 +0000
@@ -17,6 +17,13 @@ function form_test_menu() {
     'access arguments' => array('access content'),
     'type' => MENU_CALLBACK,
   );
+  $items['form-test/validate-exclusively-on-button'] = array(
+    'title' => 'Form validation exclusively on button test',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('form_test_validate_exclusively_on_button'),
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK,
+  );
 
   $items['form_test/tableselect/multiple-true'] = array(
     'title' => 'Tableselect checkboxes test',
@@ -203,6 +210,34 @@ function form_test_validate_form_validat
   }
 }
 
+function form_test_validate_exclusively_on_button($form) {
+  $form['title'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Title',
+    '#required' => TRUE,
+  );
+  $form['wrapper']['#validate_exclusively_on_button'] = array(t('Partial validate'));
+  $form['wrapper']['test'] = array(
+    '#type' => 'textfield',
+    '#element_validate' => array('form_test_validate_test_element'),
+  );
+  $form['buttons']['partial'] = array(
+    '#type' => 'submit',
+    '#value' => t('Partial validate'),
+  );
+  $form['buttons']['full'] = array(
+    '#type' => 'submit',
+    '#value' => t('Full validate'),
+  );
+  return $form;
+}
+
+function form_test_validate_test_element($form, $form_state) {
+  if ($form_state['values']['test'] == 'pass') {
+    drupal_set_message('Test is validated');
+  }
+}
+
 /**
  * Create a header and options array. Helper function for callbacks.
  */
@@ -895,7 +930,7 @@ function form_test_state_persist($form, 
 
 /**
  * Submit handler.
- * 
+ *
  * @see form_test_state_persist()
  */
 function form_test_state_persist_submit($form, &$form_state) {
@@ -905,7 +940,7 @@ function form_test_state_persist_submit(
 
 /**
  * Implements hook_form_FORM_ID_alter().
- * 
+ *
  * @see form_test_state_persist()
  */
 function form_test_form_form_test_state_persist_alter(&$form, &$form_state) {

