Index: includes/tablesort.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/tablesort.inc,v
retrieving revision 1.58
diff -u -p -r1.58 tablesort.inc
--- includes/tablesort.inc	5 Dec 2009 20:17:02 -0000	1.58
+++ includes/tablesort.inc	10 Oct 2010 22:00:16 -0000
@@ -186,34 +186,6 @@ function tablesort_header($cell, $header
 }
 
 /**
- * Format a table cell.
- *
- * Adds a class attribute to all cells in the currently active column.
- *
- * @param $cell
- *   The cell to format.
- * @param $header
- *   An array of column headers in the format described in theme_table().
- * @param $ts
- *   The current table sort context as returned from tablesort_init().
- * @param $i
- *   The index of the cell's table column.
- * @return
- *   A properly formatted cell, ready for _theme_table_cell().
- */
-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)) {
-      $cell['class'][] = 'active';
-    }
-    else {
-      $cell = array('data' => $cell, 'class' => array('active'));
-    }
-  }
-  return $cell;
-}
-
-/**
  * Compose a URL query parameter array for table sorting links.
  *
  * @return
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.619
diff -u -p -r1.619 theme.inc
--- includes/theme.inc	8 Oct 2010 05:07:53 -0000	1.619
+++ includes/theme.inc	10 Oct 2010 22:00:18 -0000
@@ -1621,7 +1621,7 @@ function theme_table($variables) {
 
   // Add sticky headers, if applicable.
   if (count($header) && $sticky) {
-    drupal_add_js('misc/tableheader.js');
+    drupal_add_js(DRUPAL_ROOT . '/misc/tableheader.js');
     // Add 'sticky-enabled' class to the table to identify it for JS.
     // This is needed to target tables constructed by this function.
     $attributes['class'][] = 'sticky-enabled';
@@ -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 ($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	10 Oct 2010 22:00:20 -0000
@@ -119,7 +119,7 @@ class ThemeTableUnitTest extends DrupalW
     $rows = array(array(1,2,3), array(4,5,6), array(7,8,9));
     $this->content = theme('table', array('header' => $header, 'rows' => $rows));
     $js = drupal_add_js();
-    $this->assertTrue(isset($js['misc/tableheader.js']), t('tableheader.js was included when $sticky = TRUE.'));
+    $this->assertTrue(isset($js[DRUPAL_ROOT . '/misc/tableheader.js']), t('tableheader.js was included when $sticky = TRUE.'));
     $this->assertRaw('sticky-enabled',  t('Table has a class of sticky-enabled when $sticky = TRUE.'));
     drupal_static_reset('drupal_add_js');
   }
@@ -157,6 +157,87 @@ 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));
+    debug($this->content);
+    $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));
+    debug($this->content);
+    $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));
+    debug($this->content);
+    $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']);
+  }
+
+
 }
 
 /**
