Index: src/orca/settings.py
===================================================================
--- src/orca/settings.py	(revision 2483)
+++ src/orca/settings.py	(working copy)
@@ -130,6 +130,8 @@
     "keyboardLayout",
     "speakBlankLines",
     "enabledTextAttributes",
+    "enableProgressBarUpdates",
+    "progressBarUpdateInterval"
 ]
 
 # The name of the module that hold the user interface for the main window
@@ -398,6 +400,15 @@
 #
 readTableCellRow    = True
 
+# If True, enable speaking of progress bar updates.
+#
+enableProgressBarUpdates = False
+
+# The interval (in seconds) between speaking progress bar updates. A value
+# of zero means that progress bar updates should not be spoken at all.
+#
+progressBarUpdateInterval = 10
+
 # 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;"
Index: src/orca/orca-setup.glade
===================================================================
--- src/orca/orca-setup.glade	(revision 2483)
+++ src/orca/orca-setup.glade	(working copy)
@@ -1188,6 +1188,123 @@
 		  </child>
 
 		  <child>
+		    <widget class="GtkHBox" id="hbox43">
+		      <property name="visible">True</property>
+		      <property name="homogeneous">False</property>
+		      <property name="spacing">80</property>
+
+		      <child>
+			<widget class="GtkCheckButton" id="speechProgressBarCheckbutton">
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="label" translatable="yes">Speak progress bar _updates</property>
+			  <property name="use_underline">True</property>
+			  <property name="relief">GTK_RELIEF_NORMAL</property>
+			  <property name="focus_on_click">True</property>
+			  <property name="active">True</property>
+			  <property name="inconsistent">False</property>
+			  <property name="draw_indicator">True</property>
+			  <signal name="toggled" handler="speechProgressBarChecked" last_modification_time="Mon, 18 Jun 2007 16:59:36 GMT"/>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkHBox" id="speakUpdateIntervalHBox">
+			  <property name="visible">True</property>
+			  <property name="homogeneous">False</property>
+			  <property name="spacing">3</property>
+
+			  <child>
+			    <widget class="GtkLabel" id="speakProgressBarLabel">
+			      <property name="visible">True</property>
+			      <property name="label" translatable="yes">Update interval:</property>
+			      <property name="use_underline">True</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="mnemonic_widget">speakProgressBarSpinButton</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="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">False</property>
+			    </packing>
+			  </child>
+
+			  <child>
+			    <widget class="GtkSpinButton" id="speakProgressBarSpinButton">
+			      <property name="visible">True</property>
+			      <property name="can_focus">True</property>
+			      <property name="climb_rate">1</property>
+			      <property name="digits">0</property>
+			      <property name="numeric">True</property>
+			      <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+			      <property name="snap_to_ticks">False</property>
+			      <property name="wrap">False</property>
+			      <property name="adjustment">10 1 9999 1 10 10</property>
+			      <signal name="value_changed" handler="speakProgressBarValueChanged" last_modification_time="Thu, 14 Jun 2007 15:09:00 GMT"/>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">True</property>
+			    </packing>
+			  </child>
+
+			  <child>
+			    <widget class="GtkLabel" id="label25">
+			      <property name="visible">True</property>
+			      <property name="label" translatable="yes">seconds</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="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">False</property>
+			</packing>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
 		    <widget class="GtkCheckButton" id="speechIndentationCheckbutton">
 		      <property name="visible">True</property>
 		      <property name="can_focus">True</property>
Index: src/orca/speechgenerator.py
===================================================================
--- src/orca/speechgenerator.py	(revision 2483)
+++ src/orca/speechgenerator.py	(working copy)
@@ -919,9 +919,12 @@
         Returns a list of utterances to be spoken for the object.
         """
 
+        percentValue = (obj.value.currentValue / \
+            (obj.value.maximumValue - obj.value.minimumValue)) * 100.0
+
         # Translators: this is the percentage value of a progress bar.
         #
-        percentage = _("%d percent.") % obj.value.currentValue + " "
+        percentage = _("%d percent.") % percentValue + " "
 
         utterances = []
 
Index: src/orca/default.py
===================================================================
--- src/orca/default.py	(revision 2483)
+++ src/orca/default.py	(working copy)
@@ -105,6 +105,12 @@
         self._display = None
         self._visibleRectangle = None
 
+        # Used by the visualAppearanceChanged routine for updating whether
+        # progress bars are spoken.
+        #
+        self.lastProgressBarTime = {}
+        self.lastProgressBarValue = {}
+
     def setupInputEventHandlers(self):
         """Defines InputEventHandler fields for this script that can be
         called by the key and braille bindings."""
@@ -1888,6 +1894,87 @@
 
         return commonAncestor
 
+    def handleProgressBarUpdate(self, event, obj):
+        """Determine whether this progress bar event should be spoken or not.
+        It should be spoken if:
+        1/ settings.enableProgressBarUpdates is True.
+        2/ The application with the progress bar has focus.
+        3/ The time of this event exceeds the
+           settings.progressBarUpdateInterval value.  This value
+           indicates the time (in seconds) between potential spoken
+           progress bar updates.
+        4/ The new value of the progress bar (converted to an integer),
+           is different from the last one or equals 100 (i.e complete).
+
+        Arguments:
+        - event: if not None, the Event that caused this to happen
+        - obj:  the Accessible progress bar object.
+        """
+
+        if settings.enableProgressBarUpdates:
+            if orca_state.locusOfFocus and \
+               orca_state.locusOfFocus.app == obj.app:
+                currentTime = time.time()
+
+                # If this progress bar is not already known, create initial
+                # values for it.
+                #
+                if not self.lastProgressBarTime.has_key(obj):
+                    self.lastProgressBarTime[obj] = 0.0
+                if not self.lastProgressBarValue.has_key(obj):
+                    self.lastProgressBarValue[obj] = None
+
+                lastProgressBarTime = self.lastProgressBarTime[obj]
+                lastProgressBarValue = self.lastProgressBarValue[obj]
+
+                if (currentTime - lastProgressBarTime) > \
+                           settings.progressBarUpdateInterval:
+                    value = obj.value
+                    currentValue = int(value.currentValue)
+                    percentValue = int((value.currentValue / \
+                        (value.maximumValue - value.minimumValue)) * 100.0)
+                    if lastProgressBarValue != currentValue \
+                       or percentValue == 100:
+                        utterances = []
+
+                        # There may be cases when more than one progress 
+                        # bar is updating at the same time in a window.
+                        # If this is the case, then speak the index of this
+                        # progress bar in the dictionary of known progress 
+                        # bars, as well as the value.
+                        #
+                        if len(self.lastProgressBarTime) > 1:
+                            index = 0
+                            for key in self.lastProgressBarTime.keys():
+                                if key == obj:
+                                    # Translators: this is an index value
+                                    # so that we can tell which progress bar
+                                    # we are referring to.
+                                    #
+                                    label = _("Progress bar %d.") % (index + 1)
+                                    utterances.append(label)
+                                else:
+                                    index += 1
+
+                        # Translators: this is the percentage value of a 
+                        # progress bar.
+                        #
+                        percentage = _("%d percent.") % percentValue + " "
+
+                        utterances.append(percentage)
+                        speech.speakUtterances(utterances)
+
+                        self.lastProgressBarTime[obj] = currentTime
+                        self.lastProgressBarValue[obj] = currentValue
+
+                        # If the percentage value for this progress bar is
+                        # 100%, then remove it from the dictionasries of 
+                        # known progress bars.
+                        #
+                        if percentValue == 100:
+                            del self.lastProgressBarTime[obj]
+                            del self.lastProgressBarValue[obj]
+
     def locusOfFocusChanged(self, event, oldLocusOfFocus, newLocusOfFocus):
         """Called when the visual object with focus changes.
 
@@ -2099,6 +2186,11 @@
         - obj: the Accessible whose visual appearance changed.
         """
 
+        # Check if this event is for a progress bar.
+        #
+        if obj.role == rolenames.ROLE_PROGRESS_BAR:
+            self.handleProgressBarUpdate(event, obj)
+
         # We care if panels are suddenly showing.  The reason for this
         # is that some applications, such as Evolution, will bring up
         # a wizard dialog that uses "Forward" and "Backward" buttons
Index: src/orca/orca_gui_prefs.py
===================================================================
--- src/orca/orca_gui_prefs.py	(revision 2483)
+++ src/orca/orca_gui_prefs.py	(working copy)
@@ -956,6 +956,16 @@
 
         self.sayAllStyle.set_active(prefs["sayAllStyle"])
 
+        # Set the sensitivity of the "Update Interval" items, depending
+        # upon whether the "Speak progress bar updates" checkbox is checked.
+        #
+        enable = prefs["enableProgressBarUpdates"]
+        self.speechProgressBarCheckbutton.set_active(enable)
+        self.speakUpdateIntervalHBox.set_sensitive(enable)
+
+        interval = prefs["progressBarUpdateInterval"]
+        self.speakProgressBarSpinButton.set_value(interval)
+
         # Braille pane.
         #
         self.brailleSupportCheckbutton.set_active(prefs["enableBraille"])
@@ -1834,6 +1844,35 @@
             else:
                 self.prefsDict["readTableCellRow"] = True
 
+    def speechProgressBarChecked(self, widget):
+        """Signal handler for the "toggled" signal for the
+           speechProgressBarCheckbutton GtkCheckButton widget.
+           The user has [un]checked the "Speak progress bar updates" checkbox.
+           Set the 'enableProgressBarUpdates' preference to the new value.
+           Set the rest of the 'update interval' hbox items [in]sensensitive
+           depending upon whether this checkbox is checked.
+
+        Arguments:
+        - widget: the component that generated the signal.
+        """
+
+        enable = widget.get_active()
+        self.prefsDict["enableProgressBarUpdates"] = enable
+        self.speakUpdateIntervalHBox.set_sensitive(enable)
+
+    def speakProgressBarValueChanged(self, widget):
+        """Signal handler for the "value_changed" signal for the
+           speakProgressBarSpinButton GtkSpinButton widget.
+           The user has changed the value of the "speak progress bar
+           updates" spin button. Set the 'progressBarUpdateInterval'
+           preference to the new integer value.
+
+        Arguments:
+        - widget: the component that generated the signal.
+        """
+
+        self.prefsDict["progressBarUpdateInterval"] = widget.get_value_as_int()
+
     def abbrevRolenamesChecked(self, widget):
         """Signal handler for the "toggled" signal for the abbrevRolenames
            GtkCheckButton widget. The user has [un]checked the 'Abbreviated
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 2483)
+++ ChangeLog	(working copy)
@@ -1,3 +1,37 @@
+2007-06-19  Rich Burridge <rich.burridge@sun.com>
+
+        * src/orca/default.py:
+          src/orca/speechgenerator.py:
+          src/orca/settings.py:
+          src/orca/orca-setup.glade:
+          src/orca/orca_gui_prefs.py:
+          Work on bug #438333 – Need to present progress bar activity.
+          Implementation of speech support for progress bars as outlined
+          by Mike in comment #6, plus the following changes (per email 
+          from Will):
+
+          - Adjust the GUI for this particular feature to have a checkbox
+            and a separate label/spin button. ie.:
+
+            [ ] Speak Progress Bar Updates       Update Interval:  ( 10 )
+
+            where the label/spin button pair would be inactivate/grayed if
+            the checkbox wasn't checked, and the interval spin button went
+            from 1 upwards in intervals of 1 with a default value of 10.
+
+          - When progress bar is at 100%, we should present it, regardless
+            of the interval.
+
+          - There may be cases when more than one progress bar is updating 
+            at the same time in a window. If this is the case, then speak 
+            the index of this progress bar in the dictionary of known 
+            progress bars, as well as the value.
+
+          Note that the progress bar currentValue isn't always a value in
+          the range 0-100. You need to look at the minimumValue and the
+          maximumValue to determine the range. Also fixed 
+          speechgenerator._getSpeechForProgressBar to reflect this.
+
 2007-06-19 Scott Haeger <scott@bashautomation.com>
 
         * src/orca/Gecko.py Fixed bug #447191 – Firefox read page summary
