Index: includes/tablesort.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/tablesort.inc,v
retrieving revision 1.59
diff -u -p -r1.59 tablesort.inc
--- includes/tablesort.inc	15 Oct 2010 04:34:15 -0000	1.59
+++ includes/tablesort.inc	25 Oct 2010 21:33:33 -0000
@@ -203,6 +203,7 @@ function tablesort_header($cell, $header
  * @return
  *   A properly formatted cell, ready for _theme_table_cell().
  */
+// @todo Isn't used by core anymore, remove in Drupal 8.
 function tablesort_cell($cell, $header, $ts, $i) {
   if (isset($header[$i]['data']) && $header[$i]['data'] == $ts['name'] && !empty($header[$i]['field'])) {
     if (is_array($cell)) {
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.620
diff -u -p -r1.620 theme.inc
--- includes/theme.inc	21 Oct 2010 19:31:39 -0000	1.620
+++ includes/theme.inc	25 Oct 2010 21:33:35 -0000
@@ -1683,12 +1683,28 @@ function theme_table($variables) {
   }
 
   // Format the table header:
+  $active = array();
   if (count($header)) {
     $ts = tablesort_init($header);
-    // HTML requires that the thead tag has tr tags in it followed by tbody
-    // tags. Using ternary operator to check and see if we have any rows.
+    // HTML requires that the thead tag has tr tags in it and is followed by
+    // tbody tags. Using ternary operator to check and see if we have any rows.
     $output .= (count($rows) ? ' <thead><tr>' : ' <tr>');
+    $col = 0;
     foreach ($header as $cell) {
+      // Determine the active column(s).
+      if (is_array($cell)) {
+        $colspan = isset($cell['colspan']) ? (int) $cell['colspan'] : 1;
+        if (isset($cell['data']) && $cell['data'] == $ts['name']) {
+          for ($offset = 0; $offset < $colspan; ++$offset) {
+            $active[$col + $offset] = TRUE;
+          }
+        }
+        $col += $colspan;
+      }
+      else {
+        ++$col;
+      }
+      // Output the cell.
       $cell = tablesort_header($cell, $header, $ts);
       $output .= _theme_table_cell($cell, TRUE);
     }
@@ -1704,6 +1720,7 @@ function theme_table($variables) {
     $output .= "<tbody>\n";
     $flip = array('even' => 'odd', 'odd' => 'even');
     $class = 'even';
+    $rowspans = array(); // Used to keep overview of current column when rowspans are involved.
     foreach ($rows as $number => $row) {
       $attributes = array();
 
@@ -1730,9 +1747,34 @@ function theme_table($variables) {
 
         // Build row
         $output .= ' <tr' . drupal_attributes($attributes) . '>';
-        $i = 0;
+        $col = 0;
         foreach ($cells as $cell) {
-          $cell = tablesort_cell($cell, $header, $ts, $i++);
+          // Account for rowspans.
+          if (isset($rowspans[$number][$col])) {
+            $col += $rowspans[$number][$col];
+          }
+          // Determine whether this cell lies in an "active" column.
+          $colspan = is_array($cell) && isset($cell['colspan']) ? (int) $cell['colspan'] : 1;
+          for ($offset = 0; $offset < $colspan; ++$offset) {
+            if (!empty($active[$col + $offset])) {
+              if (is_array($cell)) {
+                $cell['class'][] = 'active';
+              }
+              else {
+                $cell = array('data' => $cell, 'class' => array('active'));
+              }
+              break;
+            }
+          }
+          // Calculate offset for next rows, if rowspan is set.
+          if (is_array($cell) && isset($cell['rowspan'])) {
+            $max_row = $number + $cell['rowspan'];
+            for ($row = $number + 1; $row < $max_row; ++$row) {
+              $rowspans[$row][$col] = $colspan;
+            }
+          }
+          $col += $colspan;
+          // Output cell.
           $output .= _theme_table_cell($cell);
         }
         $output .= " </tr>\n";
Index: modules/simpletest/tests/theme.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/theme.test,v
retrieving revision 1.22
diff -u -p -r1.22 theme.test
--- modules/simpletest/tests/theme.test	6 Oct 2010 13:38:40 -0000	1.22
+++ modules/simpletest/tests/theme.test	25 Oct 2010 21:33:36 -0000
@@ -157,6 +157,82 @@ class ThemeTableUnitTest extends DrupalW
     $this->assertRaw('<thead><tr><th>Header 1</th>', t('Table header was printed.'));
   }
 
+  /**
+   * Tests if correct sort links are generated for headers where requested and
+   * if the "active" class is set correctly.
+   */
+  function testThemeTableSort() {
+    $header = array(
+      array(
+        'data' => 'Header12',
+        'field' => 'a',
+        'sort' => 'asc',
+        'colspan' => 2,
+      ),
+      'Header3',
+      array(
+        'data' => 'Header4',
+        'field' => 'b',
+      ),
+      array(
+        'data' => 'Header5',
+        'field' => 'c',
+      ),
+    );
+    $rows = array(
+      array(
+        array('data' => 'Column1', 'rowspan' => 2,),
+        'Column2',
+        array('data' => 'Column34', 'colspan' => 2,),
+        'Column5',
+      ),
+      array(
+        'Row2Column2',
+        'Row2Column3',
+        'Row2Column4',
+        'Row2Column5',
+      ),
+    );
+    $this->content = theme('table', array('header' => $header, 'rows' => $rows));
+    $this->assertRaw('sort=desc&amp;order=Header12', t('Correct link for active sort-column.'));
+    $this->assertTrue(preg_match('#<th [^>]*class="active"([^<]|<[^/])+Header12#', $this->content), t('Correct class in header for active sort-column.'));
+    $this->assertNoRaw('order=Header3', t('No link for normal column.'));
+    $this->assertRaw('sort=asc&amp;order=Header4', t('Correct link for inactive sort-column.'));
+    $this->assertFalse(preg_match('#<th [^>]*class="active"([^<]|<[^/])+Header4#', $this->content), t('No class in header for inactive sort-column.'));
+    $this->assertNoRaw(' data="', t('"!attr" not set as HTML attribute.', array('!attr' => 'data')));
+    $this->assertNoRaw(' field="', t('"!attr" not set as HTML attribute.', array('!attr' => 'field')));
+    $this->assertNoRaw(' sort="', t('"!attr" not set as HTML attribute.', array('!attr' => 'sort')));
+    $this->assertRaw(' class="active">Column1</td>', t('Correct class set on active column !num.', array('!num' => 1)));
+    $this->assertRaw(' class="active">Column2</td>', t('Correct class set on active column !num.', array('!num' => 2)));
+    $this->assertRaw(' class="active">Row2Column2</td>', t('Correct class set on active column !num of second row.', array('!num' => 2)));
+    $this->assertNoRaw(' class="active">Row2Column3</td>', t('No class set on inactive column !num of second row.', array('!num' => 3)));
+
+    $_GET['sort'] = 'asc';
+    $_GET['order'] = 'Header4';
+    $this->content = theme('table', array('header' => $header, 'rows' => $rows));
+    $this->assertRaw('sort=asc&amp;order=Header12', t('Correct link for inactive sort-column.'));
+    $this-> assertFalse(preg_match('#<th [^>]*class="active"([^<]|<[^/])+Header12#', $this->content), t('No class in header for inactive sort-column.'));
+    $this->assertRaw('sort=desc&amp;order=Header4', t('Correct link for active sort-column.'));
+    $this-> assertTrue(preg_match('#<th [^>]*class="active"([^<]|<[^/])+Header4#', $this->content), t('Correct class in header for active sort-column.'));
+    $this->assertNoRaw(' class="active">Column1</td>', t('No class set on inactive column !num.', array('!num' => 1)));
+    $this->assertRaw(' class="active">Column34</td>', t('Correct class set on active column with colspan.', array('!num' => 1)));
+    $this->assertRaw(' class="active">Row2Column4</td>', t('Correct class set on active column !num of second row.', array('!num' => 4)));
+    $this->assertNoRaw(' class="active">Row2Column5</td>', t('No class set on inactive column !num of second row.', array('!num' => 5)));
+
+    $_GET['sort'] = 'desc';
+    $_GET['order'] = 'Header5';
+    $this->content = theme('table', array('header' => $header, 'rows' => $rows));
+    $this->assertRaw('sort=asc&amp;order=Header12', t('Correct link for inactive sort-column.'));
+    $this-> assertFalse(preg_match('#<th [^>]*class="active"([^<]|<[^/])+Header12#', $this->content), t('No class in header for inactive sort-column.'));
+    $this->assertRaw('sort=asc&amp;order=Header5', t('Correct link for active sort-column.'));
+    $this-> assertTrue(preg_match('#<th [^>]*class="active"([^<]|<[^/])+Header5#', $this->content), t('Correct class in header for active sort-column.'));
+    $this->assertNoRaw(' class="active">Column1</td>', t('No class set on first column.'));
+    $this->assertRaw(' class="active">Column5</td>', t('Correct class set on active column.'));
+    $this->assertNoRaw(' class="active">Row2Column4</td>', t('No class set on inactive column !num of second row.', array('!num' => 4)));
+    $this->assertRaw(' class="active">Row2Column5</td>', t('Correct class set on active column !num of second row.', array('!num' => 5)));
+
+    unset($_GET['sort'], $_GET['order']);
+  }
 }
 
 /**
