Index: test/keystrokes/gtk-demo/role_table.py
===================================================================
--- test/keystrokes/gtk-demo/role_table.py	(revision 3570)
+++ test/keystrokes/gtk-demo/role_table.py	(working copy)
@@ -71,9 +71,11 @@
     "Table Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Shopping list Frame ScrollPane Table Number ColumnHeader 3 bottles of coke'",
      "     VISIBLE:  '3 bottles of coke', cursor=1",
-     "SPEECH OUTPUT: ''",
+     "SPEECH OUTPUT: 'table'",
+     "SPEECH OUTPUT: 'Number'",
      "SPEECH OUTPUT: 'cell'",
      "SPEECH OUTPUT: '3'",
+     "SPEECH OUTPUT: '3'",
      "SPEECH OUTPUT: 'bottles of coke'",
      "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'row 1 of 5'"]))
@@ -104,9 +106,11 @@
     "Table Where Am I (again)",
     ["BRAILLE LINE:  'gtk-demo Application Shopping list Frame ScrollPane Table Number ColumnHeader 5'",
      "     VISIBLE:  '5', cursor=1",
-     "SPEECH OUTPUT: ''",
+     "SPEECH OUTPUT: 'table'",
+     "SPEECH OUTPUT: 'Number'",
      "SPEECH OUTPUT: 'cell'",
      "SPEECH OUTPUT: '5'",
+     "SPEECH OUTPUT: '5'",
      "SPEECH OUTPUT: 'packages of noodles'",
      "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'row 2 of 5'"]))
Index: test/keystrokes/gtk-demo/role_column_header.py
===================================================================
--- test/keystrokes/gtk-demo/role_column_header.py	(revision 3570)
+++ test/keystrokes/gtk-demo/role_column_header.py	(working copy)
@@ -112,8 +112,10 @@
     "Normal cell Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application GtkListStore demo Frame ScrollPane Table Severity ColumnHeader < > Fixed? 60482 Normal scrollable notebooks and hidden tabs'",
      "     VISIBLE:  'Normal scrollable notebooks and ', cursor=1",
-     "SPEECH OUTPUT: ''",
+     "SPEECH OUTPUT: 'table'",
+     "SPEECH OUTPUT: 'Severity'",
      "SPEECH OUTPUT: 'cell'",
+     "SPEECH OUTPUT: 'Normal'",
      "SPEECH OUTPUT: 'check box not checked'",
      "SPEECH OUTPUT: '60482'",
      "SPEECH OUTPUT: 'Normal'",
@@ -166,9 +168,11 @@
     "Checkbox cell Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application GtkListStore demo Frame ScrollPane Table Fixed? ColumnHeader < > Fixed? 60482 Normal scrollable notebooks and hidden tabs'",
      "     VISIBLE:  '< > Fixed? 60482 Normal scrollab', cursor=1",
-     "SPEECH OUTPUT: ''",
+     "SPEECH OUTPUT: 'table'",
+     "SPEECH OUTPUT: 'Fixed?'",
      "SPEECH OUTPUT: 'cell'",
      "SPEECH OUTPUT: 'check box not checked'",
+     "SPEECH OUTPUT: 'check box not checked'",
      "SPEECH OUTPUT: '60482'",
      "SPEECH OUTPUT: 'Normal'",
      "SPEECH OUTPUT: 'scrollable notebooks and hidden tabs'",
Index: test/keystrokes/gtk-demo/role_tree_table.py
===================================================================
--- test/keystrokes/gtk-demo/role_tree_table.py	(revision 3570)
+++ test/keystrokes/gtk-demo/role_tree_table.py	(working copy)
@@ -85,11 +85,21 @@
 sequence.append(PauseAction(3000))
 sequence.append(utils.AssertPresentationAction(
     "January cell Where Am I",
-    ["BUG? - we're on the January cell",
-     "BRAILLE LINE:  'gtk-demo Application Card planning sheet Frame ScrollPane TreeTable'",
-     "     VISIBLE:  'TreeTable', cursor=1",
-     "SPEECH OUTPUT: ''",
-     "SPEECH OUTPUT: 'tree table'"]))
+    ["BRAILLE LINE:  'gtk-demo Application Card planning sheet Frame ScrollPane TreeTable Holiday ColumnHeader January expanded < > Alex < > Havoc < > Tim < > Owen < > Dave TREE LEVEL 1'",
+     "     VISIBLE:  'January expanded < > Alex < > Ha', cursor=1",
+     "SPEECH OUTPUT: 'tree table'",
+     "SPEECH OUTPUT: 'Holiday'",
+     "SPEECH OUTPUT: 'cell'",
+     "SPEECH OUTPUT: 'January'",
+     "SPEECH OUTPUT: 'January'",
+     "SPEECH OUTPUT: 'check box not checked'",
+     "SPEECH OUTPUT: 'check box not checked'",
+     "SPEECH OUTPUT: 'check box not checked'",
+     "SPEECH OUTPUT: 'check box not checked'",
+     "SPEECH OUTPUT: 'check box not checked'",
+     "SPEECH OUTPUT: 'row 1 of 53'",
+     "SPEECH OUTPUT: 'expanded'",
+     "SPEECH OUTPUT: 'tree level 1'"]))
 
 ########################################################################
 # Collapse the cell.
@@ -115,11 +125,21 @@
 sequence.append(PauseAction(3000))
 sequence.append(utils.AssertPresentationAction(
     "January cell collapsed Where Am I",
-    ["BUG? - we're on the January cell",
-     "BRAILLE LINE:  'gtk-demo Application Card planning sheet Frame ScrollPane TreeTable'",
-     "     VISIBLE:  'TreeTable', cursor=1",
-     "SPEECH OUTPUT: ''",
-     "SPEECH OUTPUT: 'tree table'"]))
+    ["BRAILLE LINE:  'gtk-demo Application Card planning sheet Frame ScrollPane TreeTable Holiday ColumnHeader January collapsed < > Alex < > Havoc < > Tim < > Owen < > Dave TREE LEVEL 1'",
+     "     VISIBLE:  'January collapsed < > Alex < > H', cursor=1",
+     "SPEECH OUTPUT: 'tree table'",
+     "SPEECH OUTPUT: 'Holiday'",
+     "SPEECH OUTPUT: 'cell'",
+     "SPEECH OUTPUT: 'January'",
+     "SPEECH OUTPUT: 'January'",
+     "SPEECH OUTPUT: 'check box not checked'",
+     "SPEECH OUTPUT: 'check box not checked'",
+     "SPEECH OUTPUT: 'check box not checked'",
+     "SPEECH OUTPUT: 'check box not checked'",
+     "SPEECH OUTPUT: 'check box not checked'",
+     "SPEECH OUTPUT: 'row 1 of 50'",
+     "SPEECH OUTPUT: 'collapsed'",
+     "SPEECH OUTPUT: 'tree level 1'"]))
 
 ########################################################################
 # Expand the cell again.
@@ -187,8 +207,10 @@
     "Alex checkbox cell Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Card planning sheet Frame ScrollPane TreeTable Alex ColumnHeader <x> Alex'",
      "     VISIBLE:  '<x> Alex', cursor=1",
-     "SPEECH OUTPUT: ''",
+     "SPEECH OUTPUT: 'tree table'",
+     "SPEECH OUTPUT: 'Alex'",
      "SPEECH OUTPUT: 'cell'",
+     "SPEECH OUTPUT: 'check box checked'",
      "SPEECH OUTPUT: 'New Years Day'",
      "SPEECH OUTPUT: 'check box checked'",
      "SPEECH OUTPUT: 'check box checked'",
Index: src/orca/scripts/StarOffice.py
===================================================================
--- src/orca/scripts/StarOffice.py	(revision 3570)
+++ src/orca/scripts/StarOffice.py	(working copy)
@@ -85,11 +85,51 @@
                (table.getColumnAtIndex(obj.getIndexInParent()) + 1)
         utterances.append(text)
 
+        # Speak the dynamic column header (if any).
+        #
+        if self._script.dynamicColumnHeaders.has_key(table):
+            row = self._script.dynamicColumnHeaders[table]
+            header = self._script.getDynamicRowHeaderCell(obj, row)
+            try:
+                headerText = header.queryText()
+            except:
+                headerText = None
+
+            if header.childCount > 0:
+                for child in header:
+                    text = self._script.getText(child, 0, -1)
+                    if text:
+                        utterances.append(text)
+            elif headerText:
+                text = self._script.getText(header, 0, -1)
+                if text:
+                    utterances.append(text)
+
         # Translators: this represents the row number of a table.
         #
         text = _("row %d") % (table.getRowAtIndex(obj.getIndexInParent()) + 1)
         utterances.append(text)
 
+        # Speak the dynamic row header (if any).
+        #
+        if self._script.dynamicRowHeaders.has_key(table):
+            column = self._script.dynamicRowHeaders[table]
+            header = self._script.getDynamicColumnHeaderCell(obj, column)
+            try:
+                headerText = header.queryText()
+            except:
+                headerText = None
+
+            if header.childCount > 0:
+                for child in header:
+                    text = self._script.getText(child, 0, -1)
+                    if text:
+                        utterances.append(text)
+            elif headerText:
+                text = self._script.getText(header, 0, -1)
+                if text:
+                    utterances.append(text)
+
         text = obj.queryText().getText(0, -1)
         utterances.append(text)
 
Index: src/orca/where_am_I.py
===================================================================
--- src/orca/where_am_I.py	(revision 3570)
+++ src/orca/where_am_I.py	(working copy)
@@ -495,34 +495,60 @@
         """Tree Tables present the following information (an example is
         'Tree table, Mike Pedersen, item 8 of 10, tree level 2'):
 
-        1. label, if any
-        2. role
-        3. current row (regardless of speak cell/row setting)
-        4. relative position
-        5. if expandable/collapsible: expanded/collapsed
-        6. if applicable, the level
+        1. parent's role
+        2. column header of object
+        3. row header of object
+        4. object's role
+        5. object's contents, if there are multiple columns
+        6. current row (regardless of speak cell/row setting)
+        7. relative position
+        8. if expandable/collapsible: expanded/collapsed
+        9. if applicable, the level
 
         Nautilus and Gaim
         """
 
-        # Speak the first two items (and possibly the position)
         utterances = []
         if obj.parent.getRole() == pyatspi.ROLE_TABLE_CELL:
             obj = obj.parent
         parent = obj.parent
 
-        text = self._getObjLabel(obj)
+        text = rolenames.getSpeechForRoleName(parent)
         utterances.append(text)
 
+        try:
+            table = parent.queryTable()
+        except:
+            table = None
+            nColumns = 0
+        else:
+            nColumns = table.nColumns
+
+            column = table.getColumnAtIndex(obj.getIndexInParent())
+            header = table.getColumnHeader(column)
+            if header:
+                text = self._getObjName(header)
+                utterances.append(text)
+
+            row = table.getRowAtIndex(obj.getIndexInParent())
+            header = table.getRowHeader(row)
+            if header:
+                text = self._getObjName(header)
+                utterances.append(text)
+
         text = rolenames.getSpeechForRoleName(obj)
         utterances.append(text)
+
+        if nColumns > 1:
+            text = self._getTableCell(obj)
+            utterances.append(text)
+
         debug.println(self._debugLevel, "first table cell utterances=%s" % \
                       utterances)
         speech.speakUtterances(utterances)
 
         utterances = []
-        if not basicOnly:
-            table = parent.queryTable()
+        if not basicOnly and table:
             row = table.getRowAtIndex(
               orca_state.locusOfFocus.getIndexInParent())
             # Translators: this in reference to a row in a table.
