Index: ChangeLog
===================================================================
--- ChangeLog	(revision 2468)
+++ ChangeLog	(working copy)
@@ -1,3 +1,17 @@
+2007-06-12  Rich Burridge <rich.burridge@sun.com>
+
+        * src/orca/scripts/StarOffice.py:
+          src/orca/settings.py:
+          src/orca/orca-setup.glade:
+          src/orca/orca_prefs.py:
+          src/orca/orca_gui_prefs.py:
+          Fixed bug #376515 – Add GUI support for the new customizable 
+          text-attribute feature. There is a new "Text Attributes" pane
+          in the Orca preferences dialog for this. It can also be set on
+          an individual app-specific basis.
+
+          (Thanks to Joanie for her extensive help with this new feature).
+
 2007-06-11  Willie Walker <william.walker@sun.com>
 
         * src/orca/atspi.py, src/orca/orca.py,
Index: src/orca/scripts/StarOffice.py
===================================================================
--- src/orca/scripts/StarOffice.py	(revision 2468)
+++ src/orca/scripts/StarOffice.py	(working copy)
@@ -870,38 +870,6 @@
         vbox.set_border_width(12)
         gtk.Widget.show(vbox)
 
-        # Text entry and label for "Enabled Text Attributes:".
-        #
-        hbox = gtk.HBox(False, 0)
-        gtk.Widget.show(hbox)
-
-        # Translators: All text in StarOffice/OpenOffice has a set of
-        # attributes associated with it (such as font type, size, style...)
-        # This label denotes those attributes that you user would like
-        # spoken when you ask to hear the text attributes associated with
-        # the current text (typically when the Insert-f key combination is
-        # pressed).
-        #
-        self.textAttributesLabel = gtk.Label(_("Enabled Text Attributes:"))
-        gtk.Widget.show(self.textAttributesLabel)
-        gtk.Box.pack_start(hbox, self.textAttributesLabel, False, False, 5)
-
-        self.enabledTextAttributes = gtk.Entry()
-        gtk.Widget.show(self.enabledTextAttributes)
-        self.enabledTextAttributes.set_text(orca.settings.enabledTextAttributes)
-        gtk.Box.pack_end(hbox, self.enabledTextAttributes, True, True, 5)
-
-        acc_targets = []
-        acc_src = self.textAttributesLabel.get_accessible()
-        relation_set = acc_src.ref_relation_set()
-        acc_targ = self.enabledTextAttributes.get_accessible()
-        acc_targets.append(acc_targ)
-        relation = atk.Relation(acc_targets, 1)
-        relation.set_property('relation-type', atk.RELATION_LABEL_FOR)
-        relation_set.add(relation)
-
-        gtk.Box.pack_start(vbox, hbox, False, False, 0)
-
         # Checkbox for "Speak spread sheet cell coordinates".
         #
         # Translators: If checked, then Orca will speak the coordinates
@@ -928,11 +896,7 @@
 
         global speakCellCoordinates
 
-        settings.enabledTextAttributes = self.enabledTextAttributes.get_text()
         prefs.writelines("\n")
-        prefs.writelines("orca.settings.enabledTextAttributes = '%s'\n" %  \
-                         settings.enabledTextAttributes)
-
         speakCellCoordinates = \
                  self.speakCellCoordinatesCheckButton.get_active()
         prefs.writelines( \
Index: src/orca/settings.py
===================================================================
--- src/orca/settings.py	(revision 2468)
+++ src/orca/settings.py	(working copy)
@@ -129,6 +129,7 @@
     "sayAllStyle",
     "keyboardLayout",
     "speakBlankLines",
+    "enabledTextAttributes",
 ]
 
 # The name of the module that hold the user interface for the main window
@@ -397,6 +398,10 @@
 #
 readTableCellRow    = True
 
+# The complete list of possible text attributes.
+#
+allTextAttributes = "bg-color:; bg-full-height:; bg-stipple:; direction:; editable:; family-name:; fg-color:; fg-stipple:; font-effect:none; indent:0; invisible:; justification:left; language:; left-margin:; line-height:100%; paragraph-style:Default; pixels-above-lines:; pixels-below-lines:; pixels-inside-wrap:; right-margin:; rise:; scale:; size:; stretch:; strikethrough:false; style:normal; text-decoration:none; text-rotation:0; text-shadow:none; underline:none; variant:; vertical-align:baseline; weight:400; wrap-mode:; writing-mode:lr-tb;"
+
 # The default set of text attributes to speak to the user. Specific
 # application scripts (or individual users can override these values if
 # so desired. Each of these text attributes is of the form <key>:<value>;
Index: src/orca/orca-setup.glade
===================================================================
--- src/orca/orca-setup.glade	(revision 2468)
+++ src/orca/orca-setup.glade	(working copy)
@@ -3276,6 +3276,333 @@
 		  <property name="type">tab</property>
 		</packing>
 	      </child>
+
+	      <child>
+		<widget class="GtkHBox" id="textAttributesHBox">
+		  <property name="border_width">5</property>
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">0</property>
+
+		  <child>
+		    <widget class="GtkFrame" id="textAttributesFrame">
+		      <property name="border_width">5</property>
+		      <property name="visible">True</property>
+		      <property name="label_xalign">0</property>
+		      <property name="label_yalign">0.5</property>
+		      <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+		      <child>
+			<widget class="GtkAlignment" id="alignment53">
+			  <property name="visible">True</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xscale">1</property>
+			  <property name="yscale">1</property>
+			  <property name="top_padding">0</property>
+			  <property name="bottom_padding">0</property>
+			  <property name="left_padding">12</property>
+			  <property name="right_padding">0</property>
+
+			  <child>
+			    <widget class="GtkVBox" id="vbox24">
+			      <property name="visible">True</property>
+			      <property name="homogeneous">False</property>
+			      <property name="spacing">0</property>
+
+			      <child>
+				<widget class="GtkScrolledWindow" id="scrolledwindow2">
+				  <property name="border_width">5</property>
+				  <property name="visible">True</property>
+				  <property name="can_focus">True</property>
+				  <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+				  <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+				  <property name="shadow_type">GTK_SHADOW_IN</property>
+				  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+				  <child>
+				    <widget class="GtkTreeView" id="textAttributesTreeView">
+				      <property name="visible">True</property>
+				      <property name="can_focus">True</property>
+				      <property name="headers_visible">True</property>
+				      <property name="rules_hint">False</property>
+				      <property name="reorderable">True</property>
+				      <property name="enable_search">True</property>
+				      <property name="fixed_height_mode">False</property>
+				      <property name="hover_selection">False</property>
+				      <property name="hover_expand">False</property>
+				    </widget>
+				  </child>
+				</widget>
+				<packing>
+				  <property name="padding">0</property>
+				  <property name="expand">True</property>
+				  <property name="fill">True</property>
+				</packing>
+			      </child>
+
+			      <child>
+				<widget class="GtkHBox" id="hbox41">
+				  <property name="border_width">5</property>
+				  <property name="visible">True</property>
+				  <property name="homogeneous">False</property>
+				  <property name="spacing">0</property>
+
+				  <child>
+				    <widget class="GtkButton" id="textSelectAllButton">
+				      <property name="border_width">5</property>
+				      <property name="can_focus">True</property>
+				      <property name="label" translatable="yes">_Speak all</property>
+				      <property name="use_underline">True</property>
+				      <property name="relief">GTK_RELIEF_NORMAL</property>
+				      <property name="focus_on_click">True</property>
+				      <signal name="clicked" handler="textSelectAllButtonClicked" last_modification_time="Fri, 08 Jun 2007 17:30:59 GMT"/>
+				    </widget>
+				    <packing>
+				      <property name="padding">0</property>
+				      <property name="expand">False</property>
+				      <property name="fill">False</property>
+				    </packing>
+				  </child>
+
+				  <child>
+				    <widget class="GtkButton" id="textUnselectAllButton">
+				      <property name="border_width">5</property>
+				      <property name="can_focus">True</property>
+				      <property name="label" translatable="yes">Speak _none</property>
+				      <property name="use_underline">True</property>
+				      <property name="relief">GTK_RELIEF_NORMAL</property>
+				      <property name="focus_on_click">True</property>
+				      <signal name="clicked" handler="textUnselectAllButtonClicked" last_modification_time="Fri, 08 Jun 2007 17:31:06 GMT"/>
+				    </widget>
+				    <packing>
+				      <property name="padding">0</property>
+				      <property name="expand">False</property>
+				      <property name="fill">False</property>
+				    </packing>
+				  </child>
+
+				  <child>
+				    <widget class="GtkButton" id="textResetButton">
+				      <property name="border_width">5</property>
+				      <property name="visible">True</property>
+				      <property name="can_focus">True</property>
+				      <property name="label" translatable="yes">_Reset</property>
+				      <property name="use_underline">True</property>
+				      <property name="relief">GTK_RELIEF_NORMAL</property>
+				      <property name="focus_on_click">True</property>
+				      <signal name="clicked" handler="textResetButtonClicked" last_modification_time="Fri, 08 Jun 2007 17:31:13 GMT"/>
+				    </widget>
+				    <packing>
+				      <property name="padding">0</property>
+				      <property name="expand">False</property>
+				      <property name="fill">False</property>
+				    </packing>
+				  </child>
+				</widget>
+				<packing>
+				  <property name="padding">0</property>
+				  <property name="expand">False</property>
+				  <property name="fill">True</property>
+				</packing>
+			      </child>
+			    </widget>
+			  </child>
+			</widget>
+		      </child>
+
+		      <child>
+			<widget class="GtkLabel" id="textAttributesFrameLabel">
+			  <property name="visible">True</property>
+			  <property name="label" translatable="yes">&lt;b&gt;Text attributes&lt;/b&gt;</property>
+			  <property name="use_underline">False</property>
+			  <property name="use_markup">True</property>
+			  <property name="justify">GTK_JUSTIFY_LEFT</property>
+			  <property name="wrap">False</property>
+			  <property name="selectable">False</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
+			</widget>
+			<packing>
+			  <property name="type">label_item</property>
+			</packing>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkFrame" id="adjustAttributesFrame">
+		      <property name="border_width">5</property>
+		      <property name="visible">True</property>
+		      <property name="label_xalign">0</property>
+		      <property name="label_yalign">0.5</property>
+		      <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+		      <child>
+			<widget class="GtkAlignment" id="alignment54">
+			  <property name="visible">True</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xscale">1</property>
+			  <property name="yscale">1</property>
+			  <property name="top_padding">0</property>
+			  <property name="bottom_padding">0</property>
+			  <property name="left_padding">12</property>
+			  <property name="right_padding">0</property>
+
+			  <child>
+			    <widget class="GtkVBox" id="vbox25">
+			      <property name="border_width">5</property>
+			      <property name="visible">True</property>
+			      <property name="homogeneous">False</property>
+			      <property name="spacing">0</property>
+
+			      <child>
+				<widget class="GtkButton" id="textMoveToTopButton">
+				  <property name="border_width">5</property>
+				  <property name="visible">True</property>
+				  <property name="can_focus">True</property>
+				  <property name="label" translatable="yes">Move to _top</property>
+				  <property name="use_underline">True</property>
+				  <property name="relief">GTK_RELIEF_NORMAL</property>
+				  <property name="focus_on_click">True</property>
+				  <signal name="clicked" handler="textMoveToTopButtonClicked" last_modification_time="Fri, 08 Jun 2007 17:30:14 GMT"/>
+				</widget>
+				<packing>
+				  <property name="padding">0</property>
+				  <property name="expand">False</property>
+				  <property name="fill">False</property>
+				</packing>
+			      </child>
+
+			      <child>
+				<widget class="GtkButton" id="textMoveUpOneButton">
+				  <property name="border_width">5</property>
+				  <property name="visible">True</property>
+				  <property name="can_focus">True</property>
+				  <property name="label" translatable="yes">Move _up one</property>
+				  <property name="use_underline">True</property>
+				  <property name="relief">GTK_RELIEF_NORMAL</property>
+				  <property name="focus_on_click">True</property>
+				  <signal name="clicked" handler="textMoveUpOneButtonClicked" last_modification_time="Fri, 08 Jun 2007 17:30:22 GMT"/>
+				</widget>
+				<packing>
+				  <property name="padding">0</property>
+				  <property name="expand">False</property>
+				  <property name="fill">False</property>
+				</packing>
+			      </child>
+
+			      <child>
+				<widget class="GtkButton" id="textMoveDownOneButton">
+				  <property name="border_width">5</property>
+				  <property name="visible">True</property>
+				  <property name="can_focus">True</property>
+				  <property name="label" translatable="yes">Move _down one</property>
+				  <property name="use_underline">True</property>
+				  <property name="relief">GTK_RELIEF_NORMAL</property>
+				  <property name="focus_on_click">True</property>
+				  <signal name="clicked" handler="textMoveDownOneButtonClicked" last_modification_time="Fri, 08 Jun 2007 17:30:28 GMT"/>
+				</widget>
+				<packing>
+				  <property name="padding">0</property>
+				  <property name="expand">False</property>
+				  <property name="fill">False</property>
+				</packing>
+			      </child>
+
+			      <child>
+				<widget class="GtkButton" id="textMoveToBottomButton">
+				  <property name="border_width">5</property>
+				  <property name="visible">True</property>
+				  <property name="can_focus">True</property>
+				  <property name="label" translatable="yes">Move to _bottom</property>
+				  <property name="use_underline">True</property>
+				  <property name="relief">GTK_RELIEF_NORMAL</property>
+				  <property name="focus_on_click">True</property>
+				  <signal name="clicked" handler="textMoveToBottomButtonClicked" last_modification_time="Fri, 08 Jun 2007 17:30:36 GMT"/>
+				</widget>
+				<packing>
+				  <property name="padding">0</property>
+				  <property name="expand">False</property>
+				  <property name="fill">False</property>
+				</packing>
+			      </child>
+			    </widget>
+			  </child>
+			</widget>
+		      </child>
+
+		      <child>
+			<widget class="GtkLabel" id="adjustAttributesFrameLabel">
+			  <property name="visible">True</property>
+			  <property name="label" translatable="yes">&lt;b&gt;Adjust selected
+attributes&lt;/b&gt;</property>
+			  <property name="use_underline">False</property>
+			  <property name="use_markup">True</property>
+			  <property name="justify">GTK_JUSTIFY_LEFT</property>
+			  <property name="wrap">False</property>
+			  <property name="selectable">False</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
+			</widget>
+			<packing>
+			  <property name="type">label_item</property>
+			</packing>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="tab_expand">False</property>
+		  <property name="tab_fill">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel" id="textAttributesTabLabel">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">Text Attributes</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">False</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+		<packing>
+		  <property name="type">tab</property>
+		</packing>
+	      </child>
 	    </widget>
 	    <packing>
 	      <property name="padding">3</property>
Index: src/orca/orca_prefs.py
===================================================================
--- src/orca/orca_prefs.py	(revision 2468)
+++ src/orca/orca_prefs.py	(working copy)
@@ -204,6 +204,13 @@
     else:
         return "orca.settings.LAPTOP_MODIFIER_KEYS"
 
+def _getTextAttributesString(enabledTextAttributes):
+    """ Returns a string that represents the enabled text attributes 
+    passed in.
+    """
+
+    return "\"" + enabledTextAttributes + "\""
+
 def _getVerbosityString(verbosityLevel):
     """Returns a string that represents the verbosity level passed in."""
     if verbosityLevel == settings.VERBOSITY_LEVEL_BRIEF:
@@ -494,6 +501,8 @@
             value = _getKeyboardLayoutString(prefsDict[key])
         elif key == "orcaModifierKeys":
             value = _getOrcaModifierKeysString(prefsDict[key])
+        elif key == "enabledTextAttributes":
+            value = _getTextAttributesString(prefsDict[key])
         else:
             value = prefsDict[key]
 
Index: src/orca/orca_gui_prefs.py
===================================================================
--- src/orca/orca_gui_prefs.py	(revision 2468)
+++ src/orca/orca_gui_prefs.py	(working copy)
@@ -60,6 +60,7 @@
 
 (HANDLER, DESCRIP, MOD_MASK1, MOD_USED1, KEY1, TEXT1, MOD_MASK2, MOD_USED2, KEY2, TEXT2, MODIF, EDITABLE) = range(12)
 
+(IS_SPOKEN, NAME, VALUE) = range(3)
 
 class orcaSetupGUI(orca_glade.GladeWrapper):
 
@@ -744,6 +745,174 @@
         self._setupSpeechSystems(factories)
         self.initializingSpeech = False
 
+    def _setTextAttributes(self, view, setAttributes, state, moveToTop=False):
+        """Given a set of text attributes, update the model used by the
+        text attribute tree view.
+
+        Arguments:
+        - view: the text attribute tree view.
+        - setAttributes: the list of attributes to update.
+        - state: the state (True or False) that they all should be set to.
+        - moveToTop: if True, move these attributes to the top of the list.
+        """
+
+        model = view.get_model()
+        view.set_model(None)
+
+        [attrList, attrDict] = \
+            orca_state.activeScript.textAttrsToDictionary(setAttributes)
+        [allAttrList, allAttrDict] = \
+            orca_state.activeScript.textAttrsToDictionary( \
+                                        settings.allTextAttributes)
+
+        for i in range(0, len(attrList)):
+            for path in range(0, len(allAttrList)):
+                if attrList[i] == model[path][NAME]:
+                    iter = model.get_iter(path)
+                    model.set(iter, IS_SPOKEN, state,
+                                    NAME, attrList[i],
+                                    VALUE, attrDict[attrList[i]])
+                    if moveToTop:
+                        iter = model.get_iter(path)
+                        otherIter = model.get_iter(i)
+                        model.move_before(iter, otherIter)
+                    break
+
+        view.set_model(model)
+
+    def _updateTextDictEntry(self):
+        """The user has updated the text attribute list in some way. Update
+        the "enabledTextAttributes" preference string to reflect the current
+        state of the text attribute list.
+        """
+
+        model = self.getTextAttributesView.get_model()
+        attrStr = ""
+        noRows = model.iter_n_children(None)
+        for path in range(0, noRows):
+            if model[path][IS_SPOKEN]:
+                attrStr += model[path][NAME] + ":" + model[path][VALUE] + "; "
+
+        self.prefsDict["enabledTextAttributes"] = attrStr
+
+    def textAttributeToggled(self, cell, path, model):
+        """The user has toggled the state of one of the text attribute
+        checkboxes. Update our model to reflect this, then update the
+        "enabledTextAttributes" preference string.
+
+        Arguments:
+        - cell: the cell that changed.
+        - path: the path of that cell.
+        - model: the model that the cell is part of.
+        """
+
+        iter = model.get_iter(path)
+        model.set(iter, IS_SPOKEN, not model[path][IS_SPOKEN])
+        self._updateTextDictEntry()
+
+    def textAttrValueEdited(self, cell, path, new_text, model):
+        """The user has edited the value of one of the text attributes.
+        Update our model to reflect this, then update the 
+        "enabledTextAttributes" preference string.
+
+        Arguments:
+        - cell: the cell that changed.
+        - path: the path of that cell. 
+        - new_text: the new text attribute value string.
+        - model: the model that the cell is part of.
+        """
+
+        iter = model.get_iter(path)
+        model.set(iter, VALUE, new_text)
+        self._updateTextDictEntry()
+
+    def textAttrCursorChanged(self, widget):
+        """Set the search column in the text attribute tree view
+        depending upon which column the user currently has the cursor in.
+        """
+
+        [path, focusColumn] = self.getTextAttributesView.get_cursor()
+        if focusColumn:
+            noColumns = len(self.getTextAttributesView.get_columns())
+            for i in range(0, noColumns):
+                col = self.getTextAttributesView.get_column(i)
+                if focusColumn == col:
+                    self.getTextAttributesView.set_search_column(i+1)
+                    break
+
+    def _createTextAttributesTreeView(self):
+        """Create the text attributes tree view. The view is the
+        textAttributesTreeView GtkTreeView widget. The view will consist
+        of a list containing three columns:
+          IS_SPOKEN - a checkbox whose state indicates whether this text
+                      attribute will be spoken or not.
+          NAME      - the text attribute name.
+          VALUE     - if set, (and this attributes is enabled for speaking),
+                      then this attribute will be spoken unless it equals 
+                      this value.
+        """
+
+        self.getTextAttributesView = \
+                       self.widgets.get_widget("textAttributesTreeView")
+        model = gtk.ListStore(gobject.TYPE_BOOLEAN,
+                              gobject.TYPE_STRING,
+                              gobject.TYPE_STRING)
+
+        # Initially setup the list store model based on the values of all 
+        # the known text attributes.
+        #
+        [allAttrList, allAttrDict] = \
+            orca_state.activeScript.textAttrsToDictionary( \
+                                        settings.allTextAttributes)
+        for i in range(0, len(allAttrList)):
+            iter = model.append()
+            model.set(iter, IS_SPOKEN, False,
+                            NAME, allAttrList[i],
+                            VALUE, allAttrDict[allAttrList[i]])
+
+        self.getTextAttributesView.set_model(model)
+
+        # Attribute Name column (IS_SPOKEN and NAME).
+        #
+        column = gtk.TreeViewColumn(_("Attribute Name"))
+        column.set_min_width(250)
+        column.set_resizable(True)
+        renderer = gtk.CellRendererToggle()
+        column.pack_start(renderer, False)
+        column.set_attributes(renderer, active=IS_SPOKEN)
+        renderer.connect("toggled",
+                         self.textAttributeToggled,
+                         model)
+
+        renderer = gtk.CellRendererText()
+        column.pack_end(renderer, True)
+        column.set_attributes(renderer, text=NAME)
+
+        self.getTextAttributesView.insert_column(column, 0)
+        column.clicked()
+
+        # Attribute Value column (VALUE)
+        #
+        column = gtk.TreeViewColumn(_("Speak Unless"))
+        renderer = gtk.CellRendererText()
+        renderer.set_property('editable', True)
+        column.pack_end(renderer, True)
+        column.set_attributes(renderer, text=VALUE)
+        renderer.connect("edited", self.textAttrValueEdited, model)
+
+        self.getTextAttributesView.insert_column(column, 1)
+
+        # Check all the enabled (spoken) text attributes.
+        #
+        self._setTextAttributes(self.getTextAttributesView,
+                                settings.enabledTextAttributes, True, True)
+
+        # Connect a handler for when the user changes columns within the
+        # view, so that we can adjust the search column for item lookups.
+        #
+        self.getTextAttributesView.connect("cursor_changed",
+                                           self.textAttrCursorChanged)
+
     def _initGUIState(self):
         """Adjust the settings of the various components on the
         configuration GUI depending upon the users preferences.
@@ -980,6 +1149,10 @@
         targetDisplay = prefs["magTargetDisplay"]
         self.magTargetDisplayEntry.set_text(targetDisplay)
 
+        # Text attributes pane.
+        #
+        self._createTextAttributesTreeView()
+
         # General pane.
         #
         self.showMainWindowCheckButton.set_active(prefs["showMainWindow"])
@@ -1043,6 +1216,12 @@
             except AttributeError:
                 debug.printException(debug.LEVEL_FINEST)
 
+        # We always want to re-order the text attributes page so that enabled
+        # items are consistently at the top.
+        #
+        self._setTextAttributes(self.getTextAttributesView,
+                                settings.enabledTextAttributes, True, True)
+
         self.orcaSetupWindow.show()
 
     def _initComboBox(self, combobox):
@@ -1600,7 +1779,7 @@
 
     def sayAllStyleChanged(self, widget):
         """Signal handler for the "changed" signal for the sayAllStyle
-           GtkComboBox widget. Set the 'sayAllStyle' preference to the 
+           GtkComboBox widget. Set the 'sayAllStyle' preference to the
            new value.
 
         Arguments:
@@ -2112,6 +2291,128 @@
                 self.prefsDict["orcaModifierKeys"] = \
                     settings.LAPTOP_MODIFIER_KEYS
 
+    def textSelectAllButtonClicked(self, widget):
+        """Signal handler for the "clicked" signal for the 
+        textSelectAllButton GtkButton widget. The user has clicked 
+        the Speak all button.  Check all the text attributes and 
+        then update the "enabledTextAttributes" preference string.
+
+        Arguments:
+        - widget: the component that generated the signal.
+        """ 
+
+        self._setTextAttributes(self.getTextAttributesView,
+                                settings.allTextAttributes, True)
+        self._updateTextDictEntry()
+        
+    def textUnselectAllButtonClicked(self, widget):
+        """Signal handler for the "clicked" signal for the 
+        textUnselectAllButton GtkButton widget. The user has clicked 
+        the Speak none button. Uncheck all the text attributes and 
+        then update the "enabledTextAttributes" preference string.
+
+        Arguments:
+        - widget: the component that generated the signal. 
+        """
+
+        self._setTextAttributes(self.getTextAttributesView,
+                                settings.allTextAttributes, False)
+        self._updateTextDictEntry()
+        
+    def textResetButtonClicked(self, widget):
+        """Signal handler for the "clicked" signal for the 
+        textResetButton GtkButton widget. The user has clicked
+        the Reset button. Reset all the text attributes to their 
+        initial state and then update the "enabledTextAttributes" 
+        preference string.
+
+        Arguments:
+        - widget: the component that generated the signal.
+        """
+
+        self._setTextAttributes(self.getTextAttributesView,
+                                settings.allTextAttributes, False)
+        self._setTextAttributes(self.getTextAttributesView,
+                                settings.enabledTextAttributes, True)
+        self._updateTextDictEntry()
+        
+    def textMoveToTopButtonClicked(self, widget):
+        """Signal handler for the "clicked" signal for the
+        textMoveToTopButton GtkButton widget. The user has clicked
+        the Move to top button. Move the selected rows in the text 
+        attribute view to the very top of the list and then update 
+        the "enabledTextAttributes" preference string.
+
+        Arguments:
+        - widget: the component that generated the signal.
+        """
+
+        textSelection = self.getTextAttributesView.get_selection()
+        [model, paths] = textSelection.get_selected_rows()
+        for path in paths:
+            iter = model.get_iter(path)
+            model.move_after(iter, None)
+        self._updateTextDictEntry()
+
+    def textMoveUpOneButtonClicked(self, widget):
+        """Signal handler for the "clicked" signal for the
+        textMoveUpOneButton GtkButton widget. The user has clicked
+        the Move up one button. Move the selected rows in the text
+        attribute view up one row in the list and then update the 
+        "enabledTextAttributes" preference string.
+
+        Arguments:
+        - widget: the component that generated the signal.
+        """
+
+        textSelection = self.getTextAttributesView.get_selection()
+        [model, paths] = textSelection.get_selected_rows()
+        for path in paths:
+            iter = model.get_iter(path)
+            if path[0]:
+                otherIter = model.iter_nth_child(None, path[0]-1)
+                model.swap(iter, otherIter)
+        self._updateTextDictEntry()
+
+    def textMoveDownOneButtonClicked(self, widget):
+        """Signal handler for the "clicked" signal for the
+        textMoveDownOneButton GtkButton widget. The user has clicked
+        the Move down one button. Move the selected rows in the text
+        attribute view down one row in the list and then update the
+        "enabledTextAttributes" preference string.
+
+        Arguments:
+        - widget: the component that generated the signal.
+        """
+
+        textSelection = self.getTextAttributesView.get_selection()
+        [model, paths] = textSelection.get_selected_rows()
+        noRows = model.iter_n_children(None)
+        for path in paths:
+            iter = model.get_iter(path)
+            if path[0] < noRows-1:
+                otherIter = model.iter_next(iter)
+                model.swap(iter, otherIter)
+        self._updateTextDictEntry()
+
+    def textMoveToBottomButtonClicked(self, widget):
+        """Signal handler for the "clicked" signal for the
+        textMoveToBottomButton GtkButton widget. The user has clicked
+        the Move to bottom button. Move the selected rows in the text
+        attribute view to the bottom of the list and then update the
+        "enabledTextAttributes" preference string.
+
+        Arguments:
+        - widget: the component that generated the signal.
+        """
+
+        textSelection = self.getTextAttributesView.get_selection()
+        [model, paths] = textSelection.get_selected_rows()
+        for path in paths:
+            iter = model.get_iter(path)
+            model.move_before(iter, None)
+        self._updateTextDictEntry()
+
     def helpButtonClicked(self, widget):
         """Signal handler for the "clicked" signal for the helpButton
            GtkButton widget. The user has clicked the Help button.
