[orca] Rewrite progressbar code



commit 0913f8222d0ee3d0679a1b919c6f4d9ee1dfb74c
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Fri Nov 20 12:49:57 2015 -0500

    Rewrite progressbar code
    
    * Nuke the very scary handleProgressBarUpdates
    * Create script utilities to handle the logic needed by the event handler
    * Move presentation to the generators
    * Add braille presentation support (disabled by default)
    * Move configuration to General prefs (temporary, but it's not just Speech)
    * Update documentation
    * Add settings, GUI, and docs for (not yet merged) beep presentation support
    * Work around Firefox's extra, hidden progress bars
    * Also fix a few GUI issues I noticed while doing the above
    * Also remove apostrophes from the ui file while in there to work around
      bgo 758372.

 help/C/preferences_general.page               |   70 ++++
 help/C/preferences_speech.page                |   48 ---
 src/orca/braille_generator.py                 |   32 ++
 src/orca/formatting.py                        |   10 +-
 src/orca/generator.py                         |    8 +-
 src/orca/orca-setup.ui                        |  471 ++++++++++++-------------
 src/orca/orca_gui_prefs.py                    |   39 +--
 src/orca/script_utilities.py                  |  143 ++++++++
 src/orca/scripts/default.py                   |  147 +-------
 src/orca/scripts/toolkits/Gecko/script.py     |   18 -
 src/orca/scripts/toolkits/WebKitGtk/script.py |    6 +-
 src/orca/scripts/web/script.py                |    4 +-
 src/orca/scripts/web/script_utilities.py      |   16 +-
 src/orca/settings.py                          |    8 +-
 src/orca/speech_generator.py                  |   27 ++
 15 files changed, 552 insertions(+), 495 deletions(-)
---
diff --git a/help/C/preferences_general.page b/help/C/preferences_general.page
index 7143779..dc7c70b 100644
--- a/help/C/preferences_general.page
+++ b/help/C/preferences_general.page
@@ -177,4 +177,74 @@
       </item>
     </list>
   </section>
+  <section id="progress_bar_updates">
+    <title>Progress Bar Updates</title>
+    <section>
+      <title>Speak updates</title>
+      <p>
+        If the <gui>Speak updates</gui> checkbox is checked <app>Orca</app> will
+        periodically speak the status of progress bars.
+      </p>
+      <p>
+        Default value: checked
+      </p>
+    </section>
+    <section>
+      <title>Braille updates</title>
+      <p>
+        If the <gui>Braille updates</gui> checkbox is checked <app>Orca</app> will
+        periodically display the status of progress bars on your refreshable braille
+        display.
+      </p>
+      <p>
+        Default value: not checked
+      </p>
+    </section>
+    <section>
+      <title>Beep updates</title>
+      <p>
+        If the <gui>Beep updates</gui> checkbox is checked <app>Orca</app> will
+        periodically emit beeps which increase in pitch as the value of the progress
+        bar increases.
+      </p>
+      <p>
+        Default value: not checked
+      </p>
+    </section>
+    <section>
+      <title>Frequency (secs)</title>
+      <p>
+        This spin button determines how often updates are presented.
+      </p>
+      <p>
+        Default value: 10
+      </p>
+    </section>
+    <section>
+      <title>Restrict to</title>
+      <p>
+         This combo box allows you to control which progress bars should be
+         presented, assuming the presentation of progress bar updates has been
+         enabled. The choices are <gui>All</gui>, <gui>Application</gui>, and
+         <gui>Window</gui>.
+      </p>
+      <p>
+        Choosing <gui>All</gui> will result in <app>Orca</app> presenting
+        updates for all progress bars, regardless of where the progress bars
+        are located.
+      </p>
+      <p>
+        Choosing <gui>Application</gui> will result in <app>Orca</app>
+        presenting updates from progress bars in the active application, even
+        if they are not in the active window.
+      </p>
+      <p>
+         Choosing <gui>Window</gui> will result in <app>Orca</app> only
+         presenting updates for progress bars in the active window.
+      </p>
+      <p>
+        Default value: <gui>Application</gui>
+      </p>
+    </section>
+  </section>
 </page>
diff --git a/help/C/preferences_speech.page b/help/C/preferences_speech.page
index 9928d46..aebd187 100644
--- a/help/C/preferences_speech.page
+++ b/help/C/preferences_speech.page
@@ -216,52 +216,4 @@
       </p>
     </section>
   </section>
-  <section id="progress_bar_updates">
-    <title>Progress Bar Updates</title>
-    <section>
-      <title>Enabled</title>
-      <p>
-        If the <gui>Enabled</gui> checkbox is checked <app>Orca</app> will
-        periodically present the status of progress bars.
-      </p>
-      <p>
-        Default value: checked
-      </p>
-    </section>
-    <section>
-      <title>Frequency (secs)</title>
-      <p>
-        This spin button determines how often the announcement is made.
-      </p>
-      <p>
-        Default value: 10
-      </p>
-    </section>
-    <section>
-      <title>Restrict to</title>
-      <p>
-         This combo box allows you to control which progress bars should be
-         presented, assuming the presentation of progress bar updates has been
-         enabled. The choices are <gui>All</gui>, <gui>Application</gui>, and
-         <gui>Window</gui>.
-      </p>
-      <p>
-        Choosing <gui>All</gui> will result in <app>Orca</app> presenting
-        updates for all progress bars, regardless of where the progress bars
-        are located.
-      </p>
-      <p>
-        Choosing <gui>Application</gui> will result in <app>Orca</app>
-        presenting updates from progress bars in the active application, even
-        if they are not in the active window.
-      </p>
-      <p>
-         Choosing <gui>Window</gui> will result in <app>Orca</app> only
-         presenting updates for progress bars in the active window.
-      </p>
-      <p>
-        Default value: <gui>Application</gui>
-      </p>
-    </section>
-  </section>
 </page>
diff --git a/src/orca/braille_generator.py b/src/orca/braille_generator.py
index 4462c95..10efc8f 100644
--- a/src/orca/braille_generator.py
+++ b/src/orca/braille_generator.py
@@ -131,6 +131,11 @@ class BrailleGenerator(generator.Generator):
         empty array.  Note that a 'role' attribute in args will
         override the accessible role of the obj.
         """
+
+        if args.get('isProgressBarUpdate') \
+           and not _settingsManager.getSetting('brailleProgressBarUpdates'):
+            return []
+
         result = []
         role = args.get('role', obj.getRole())
         verbosityLevel = _settingsManager.getSetting('brailleVerbosityLevel')
@@ -347,6 +352,29 @@ class BrailleGenerator(generator.Generator):
 
         return result
 
+    def _generateProgressBarIndex(self, obj, **args):
+        if not args.get('isProgressBarUpdate') \
+           or not _settingsManager.getSetting('brailleProgressBarUpdates'):
+            return []
+
+        acc, updateTime, updateValue = self._script.utilities.getMostRecentProgressBarUpdate()
+        if acc != obj:
+            number, count = self._script.utilities.getProgressBarNumberAndCount(obj)
+            return ['%s' % number]
+
+        return []
+
+    def _generateProgressBarValue(self, obj, **args):
+        if args.get('isProgressBarUpdate') \
+           and not _settingsManager.getSetting('brailleProgressBarUpdates'):
+            return []
+
+        percent = self._script.utilities.getValueAsPercent(obj)
+        if percent is not None:
+            return ['%s%%' % percent]
+
+        return []
+
     #####################################################################
     #                                                                   #
     # Unfortunate hacks.                                                #
@@ -402,6 +430,10 @@ class BrailleGenerator(generator.Generator):
         """Returns True or False to indicate whether context should be
         included or not.
         """
+
+        if args.get('isProgressBarUpdate'):
+            return False
+
         # For multiline text areas, we only show the context if we
         # are on the very first line.  Otherwise, we show only the
         # line.
diff --git a/src/orca/formatting.py b/src/orca/formatting.py
index d63bcc9..5bc71c1 100644
--- a/src/orca/formatting.py
+++ b/src/orca/formatting.py
@@ -309,8 +309,8 @@ formatting = {
             'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection 
+ ' + MNEMONIC
             },
         pyatspi.ROLE_PROGRESS_BAR: {
-            'focused': 'percentage',
-            'unfocused': 'labelAndName + percentage'
+            'focused': 'progressBarIndex + progressBarValue',
+            'unfocused': 'progressBarIndex + labelAndName + progressBarValue'
             },
         pyatspi.ROLE_PUSH_BUTTON: {
             'focused': 'expandableState',
@@ -606,7 +606,11 @@ formatting = {
         pyatspi.ROLE_PASSWORD_TEXT: {
             'unfocused': BRAILLE_TEXT
             },
-        #pyatspi.ROLE_PROGRESS_BAR: 'default'
+        pyatspi.ROLE_PROGRESS_BAR: {
+            'unfocused': '(progressBarValue and \
+                           [Component(obj, asString(labelAndName + progressBarValue + roleName + 
progressBarIndex))]) \
+                           or []'
+            },
         pyatspi.ROLE_PUSH_BUTTON: {
             'unfocused': '[Component(obj,\
                                      asString((labelAndName or description) + expandableState + roleName))]'
diff --git a/src/orca/generator.py b/src/orca/generator.py
index b8bd0e2..953ac6e 100644
--- a/src/orca/generator.py
+++ b/src/orca/generator.py
@@ -330,7 +330,7 @@ class Generator:
                 link = None
                 if obj.getRole() == pyatspi.ROLE_LINK:
                     link = obj
-                elif obj.parent.getRole() == pyatspi.ROLE_LINK:
+                elif obj.parent and obj.parent.getRole() == pyatspi.ROLE_LINK:
                     link = obj.parent
                 if link:
                     basename = self._script.utilities.linkBasename(link)
@@ -1047,3 +1047,9 @@ class Generator:
 
     def _generatePageSummary(self, obj, **args):
         return []
+
+    def _generateProgressBarIndex(self, obj, **args):
+        return []
+
+    def _generateProgressBarValue(self, obj, **args):
+        return []
diff --git a/src/orca/orca-setup.ui b/src/orca/orca-setup.ui
index ad8c4a6..59a0819 100644
--- a/src/orca/orca-setup.ui
+++ b/src/orca/orca-setup.ui
@@ -1,102 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <!-- interface-requires gtk+ 3.0 -->
-  <object class="GtkAdjustment" id="adjustment1">
-    <property name="upper">10</property>
-    <property name="value">5</property>
-    <property name="step_increment">0.10000000149</property>
-    <property name="page_increment">1</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment10">
-    <property name="upper">20</property>
-    <property name="value">1</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment11">
-    <property name="lower">-1</property>
-    <property name="upper">1</property>
-    <property name="step_increment">0.050000000745099998</property>
-    <property name="page_increment">0.25</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment12">
-    <property name="lower">-1</property>
-    <property name="upper">1</property>
-    <property name="step_increment">0.050000000745099998</property>
-    <property name="page_increment">0.25</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment13">
-    <property name="lower">24</property>
-    <property name="upper">256</property>
-    <property name="value">1</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment14">
-    <property name="lower">1</property>
-    <property name="upper">256</property>
-    <property name="value">1</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment15">
-    <property name="upper">100</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment2">
-    <property name="upper">100</property>
-    <property name="value">50</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment3">
-    <property name="upper">10</property>
-    <property name="value">10</property>
-    <property name="step_increment">0.10000000149</property>
-    <property name="page_increment">1</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment4">
+  <object class="GtkAdjustment" id="brailleFlashTimeAdjustment">
     <property name="upper">9999</property>
     <property name="value">10</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
-  <object class="GtkAdjustment" id="adjustment5">
-    <property name="lower">1</property>
-    <property name="upper">16</property>
-    <property name="value">1</property>
-    <property name="step_increment">0.25</property>
-    <property name="page_increment">1</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment6">
-    <property name="lower">1</property>
-    <property name="upper">9999</property>
-    <property name="value">1</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment7">
-    <property name="lower">1</property>
-    <property name="upper">9999</property>
-    <property name="value">1</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment8">
-    <property name="lower">1</property>
-    <property name="upper">9999</property>
-    <property name="value">1</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment9">
-    <property name="lower">1</property>
-    <property name="upper">9999</property>
-    <property name="value">1</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
   <object class="GtkListStore" id="liststore1">
     <columns>
       <!-- column-name gchararray1 -->
@@ -188,6 +98,30 @@
       <column type="gchararray"/>
     </columns>
   </object>
+  <object class="GtkAdjustment" id="pitchAdjustment">
+    <property name="upper">10</property>
+    <property name="value">5</property>
+    <property name="step_increment">0.10000000149</property>
+    <property name="page_increment">1</property>
+  </object>
+  <object class="GtkAdjustment" id="progressBarUpdateIntervalAdjustment">
+    <property name="upper">9999</property>
+    <property name="value">10</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="rateAdjustment">
+    <property name="upper">100</property>
+    <property name="value">50</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="volumeAdjustment">
+    <property name="upper">10</property>
+    <property name="value">10</property>
+    <property name="step_increment">0.10000000149</property>
+    <property name="page_increment">1</property>
+  </object>
   <object class="GtkDialog" id="orcaSetupWindow">
     <property name="can_focus">False</property>
     <property name="title" translatable="yes">Screen Reader Preferences</property>
@@ -734,7 +668,7 @@
                             <property name="can_focus">False</property>
                             <child>
                               <object class="GtkCheckButton" id="rewindAndFastForwardInSayAllCheckButton">
-                                <property name="label" translatable="yes" comments="Translators: Orca has a 
'Say All' feature which speaks the entire document. Normally, pressing any key will interrupt Say All's 
presentation. However, if 'rewind and fast forward' is enabled, Up Arrow and Down Arrow can be used within 
Say All to quickly move within the document to re-hear something which was just read or skip past something 
of no interest.">Enable _rewind and fast forward in Say All</property>
+                                <property name="label" translatable="yes" comments="Translators: Orca has a 
Say All feature which speaks the entire document. Normally, pressing any key will interrupt Say All 
presentation. However, if rewind and fast forward is enabled, Up Arrow and Down Arrow can be used within Say 
All to quickly move within the document to re-hear something which was just read or skip past something of no 
interest.">Enable _rewind and fast forward in Say All</property>
                                 <property name="use_action_appearance">False</property>
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
@@ -753,7 +687,7 @@
                             </child>
                             <child>
                               <object class="GtkCheckButton" id="structNavInSayAllCheckButton">
-                                <property name="label" translatable="yes" comments="Translators: Orca has a 
'Say All' feature which speaks the entire document. Normally, pressing any key will interrupt Say All's 
presentation. However, if 'structural navigation' is enabled for Say All, users can use commands such as 
H/Shift+H to jump to the next/previous heading, P/Shift+P to jump to the next/previous paragraph, T/Shift+T 
to jump to the next/previous table, and so on. Thus this setting is like fast forward and rewind, but with 
semantic awareness for web documents and similar content.">Enable _structural navigation in Say All</property>
+                                <property name="label" translatable="yes" comments="Translators: Orca has a 
Say All feature which speaks the entire document. Normally, pressing any key will interrupt Say All 
presentation. However, if structural navigation is enabled for Say All, users can use commands such as 
H/Shift+H to jump to the next/previous heading, P/Shift+P to jump to the next/previous paragraph, T/Shift+T 
to jump to the next/previous table, and so on. Thus this setting is like fast forward and rewind, but with 
semantic awareness for web documents and similar content.">Enable _structural navigation in Say All</property>
                                 <property name="use_action_appearance">False</property>
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
@@ -782,7 +716,7 @@
                                     <property name="name">-`</property>
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
-                                    <property name="label" translatable="yes" comments="Translators: Say all 
by refers to the way that Orca will say (speak) an amount of text -- in particular, where Orca where insert 
pauses. There are currently two choices (supplied by a combo box to the right of this label): say all by 
sentence and  say all by line.  If Orca were speaking a work of fiction, it's probably best to do say all by 
sentence so it sound more natural. If Orca were speaking something like a page of computer commands, doing a 
say all by line would work better.">Say All B_y:</property>
+                                    <property name="label" translatable="yes" comments="Translators: Say all 
by refers to the way that Orca will say (speak) an amount of text -- in particular, where Orca where insert 
pauses. There are currently two choices (supplied by a combo box to the right of this label): say all by 
sentence and say all by line.  If Orca were speaking a work of fiction, it would probably be best to do say 
all by sentence so it sounds more natural. If Orca were speaking something like a page of computer commands, 
doing a say all by line would work better.">Say All B_y:</property>
                                     <property name="use_underline">True</property>
                                     <property name="mnemonic_widget">sayAllStyle</property>
                                     <property name="xalign">0</property>
@@ -841,6 +775,185 @@
                     <property name="height">1</property>
                   </packing>
                 </child>
+                <child>
+                  <object class="GtkFrame" id="progressBarUpdatesFrame">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object class="GtkAlignment" id="progressBarUpdatesAlignment">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <object class="GtkGrid" id="progressBarUpdatesGrid">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <child>
+                              <object class="GtkCheckButton" id="speakProgressBarUpdatesCheckButton">
+                                <property name="label" translatable="yes" comments="Translators: This is an 
option in the Preferences dialog box related to the presentation of progress bar updates. If this checkbox is 
checked, Orca will periodically speak the current percentage.">_Speak updates</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="valign">start</property>
+                                <property name="hexpand">True</property>
+                                <property name="use_underline">True</property>
+                                <property name="xalign">0</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="checkButtonToggled" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="left_attach">0</property>
+                                <property name="top_attach">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="brailleProgressBarUpdatesCheckButton">
+                                <property name="label" translatable="yes" comments="Translators: This is an 
option in the Preferences dialog box related to the presentation of progress bar updates. If this checkbox is 
checked, Orca will periodically display the current percentage in braille.">_Braille updates</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="valign">start</property>
+                                <property name="hexpand">True</property>
+                                <property name="use_underline">True</property>
+                                <property name="xalign">0</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="checkButtonToggled" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="left_attach">0</property>
+                                <property name="top_attach">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkGrid" id="progressBarUpdatesOptionsGrid">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="column_spacing">12</property>
+                                <child>
+                                  <object class="GtkSpinButton" id="progressBarUpdateIntervalSpinButton">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="invisible_char">●</property>
+                                    <property name="text" translatable="yes">10</property>
+                                    <property 
name="adjustment">progressBarUpdateIntervalAdjustment</property>
+                                    <property name="climb_rate">1</property>
+                                    <property name="numeric">True</property>
+                                    <property name="value">10</property>
+                                    <signal name="value-changed" 
handler="progressBarUpdateIntervalValueChanged" swapped="no"/>
+                                  </object>
+                                  <packing>
+                                    <property name="left_attach">1</property>
+                                    <property name="top_attach">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel" id="speakProgressBarLabel">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="label" translatable="yes" context="ProgressBar" 
comments="Translators: Here this is a label for a spin button through which a user can customize the 
frequency in seconds an announcement should be made regarding the current value of a progress bar.">Frequency 
(secs):</property>
+                                    <property name="use_underline">True</property>
+                                    <property 
name="mnemonic_widget">progressBarUpdateIntervalSpinButton</property>
+                                    <property name="xalign">0</property>
+                                  </object>
+                                  <packing>
+                                    <property name="left_attach">0</property>
+                                    <property name="top_attach">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel" id="progressBarVerbosityLabel">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="label" translatable="yes" comments="Translators: Orca 
has a setting which determines which progress bar updates should be announced. The options are all progress 
bars, only progress bars in the active application, or only progress bars in the current window.">Restrict 
to:</property>
+                                    <property name="use_underline">True</property>
+                                    <property name="mnemonic_widget">progressBarVerbosity</property>
+                                    <property name="xalign">0</property>
+                                  </object>
+                                  <packing>
+                                    <property name="left_attach">0</property>
+                                    <property name="top_attach">1</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkComboBox" id="progressBarVerbosity">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="model">model7</property>
+                                    <signal name="changed" handler="progressBarVerbosityChanged" 
swapped="no"/>
+                                    <child>
+                                      <object class="GtkCellRendererText" id="renderer33"/>
+                                      <attributes>
+                                        <attribute name="text">0</attribute>
+                                      </attributes>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="left_attach">1</property>
+                                    <property name="top_attach">1</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="left_attach">0</property>
+                                <property name="top_attach">3</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="beepProgressBarUpdatesCheckButton">
+                                <property name="label" translatable="yes" comments="Translators: This is an 
option in the Preferences dialog box related to the presentation of progress bar updates. If this checkbox is 
checked, Orca will periodically emit beeps which increase in pitch as the value of the progress bar 
increases.">Bee_p updates</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="valign">start</property>
+                                <property name="hexpand">True</property>
+                                <property name="use_underline">True</property>
+                                <property name="xalign">0</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="checkButtonToggled" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="left_attach">0</property>
+                                <property name="top_attach">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel" id="progressBarUpdatesLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes" comments="Translators: This is a label in 
the Preferences dialog box. It applies to several options related to which progress bars Orca should speak 
and how often Orca should speak them.">Progress Bar Updates</property>
+                        <attributes>
+                          <attribute name="weight" value="bold"/>
+                        </attributes>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">0</property>
+                    <property name="height">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
               </object>
             </child>
             <child type="tab">
@@ -900,7 +1013,7 @@
                               <object class="GtkScale" id="volumeScale">
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
-                                <property name="adjustment">adjustment3</property>
+                                <property name="adjustment">volumeAdjustment</property>
                                 <property name="round_digits">1</property>
                                 <property name="value_pos">right</property>
                                 <accelerator key="l" signal="grab_focus" modifiers="GDK_MOD1_MASK"/>
@@ -917,7 +1030,7 @@
                               <object class="GtkScale" id="pitchScale">
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
-                                <property name="adjustment">adjustment1</property>
+                                <property name="adjustment">pitchAdjustment</property>
                                 <property name="round_digits">1</property>
                                 <property name="value_pos">right</property>
                                 <accelerator key="t" signal="grab_focus" modifiers="GDK_MOD1_MASK"/>
@@ -951,7 +1064,7 @@
                               <object class="GtkScale" id="rateScale">
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
-                                <property name="adjustment">adjustment2</property>
+                                <property name="adjustment">rateAdjustment</property>
                                 <property name="round_digits">0</property>
                                 <property name="digits">0</property>
                                 <property name="value_pos">right</property>
@@ -1097,7 +1210,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                                 <property name="xalign">1</property>
-                                <property name="label" translatable="yes" comments="Translators: Having 
multiple 'voice types' in Orca makes it possible for the user to more quickly identify properties of text 
non-visually, such as the fact that text is written in capital letters or is a link; or that text is actually 
visible on the screen as opposed to an Orca-specific message. The available 'voice types' in Orca include 
'default', 'uppercase', 'hyperlink', and 'system' -- each of which can be configured by the user to sound the 
way he/she finds most helpful. This string is displayed in the label for the combo box in which the user 
selects a voice type to configure.">_Voice type:</property>
+                                <property name="label" translatable="yes" comments="Translators: Having 
multiple voice types in Orca makes it possible for the user to more quickly identify properties of text 
non-visually, such as the fact that text is written in capital letters or is a link; or that text is actually 
visible on the screen as opposed to an Orca-specific message. The available voice types in Orca include: 
default, uppercase, hyperlink, and system -- each of which can be configured by the user to sound the way 
he/she finds most helpful. This string is displayed in the label for the combo box in which the user selects 
a voice type to configure.">_Voice type:</property>
                                 <property name="use_underline">True</property>
                                 <property name="justify">right</property>
                                 <property name="mnemonic_widget">voiceTypesCombo</property>
@@ -1117,7 +1230,7 @@
                       <object class="GtkLabel" id="voiceTypeSettingsLabel">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="label" translatable="yes" comments="Translators: Having multiple 
'voice types' in Orca makes it possible for the user to more quickly identify properties of text 
non-visually, such as the fact that text is written in capital letters or is a link; or that text is actually 
visible on the screen as opposed to an Orca-specific message. The available 'voice types' in Orca include 
'default', 'uppercase', 'hyperlink', and 'system' -- each of which can be configured by the user to sound the 
way he/she finds most helpful. This string is displayed in the label for the group of all of the controls 
associated with configuring a particular voice type.">Voice Type Settings</property>
+                        <property name="label" translatable="yes" comments="Translators: Having multiple 
voice types in Orca makes it possible for the user to more quickly identify properties of text non-visually, 
such as the fact that text is written in capital letters or is a link; or that text is actually visible on 
the screen as opposed to an Orca-specific message. The available voice types in Orca include: default, 
uppercase, hyperlink, and system -- each of which can be configured by the user to sound the way he/she finds 
most helpful. This string is displayed in the label for the group of all of the controls associated with 
configuring a particular voice type.">Voice Type Settings</property>
                         <attributes>
                           <attribute name="weight" value="bold"/>
                         </attributes>
@@ -1188,7 +1301,7 @@
                             </child>
                             <child>
                               <object class="GtkCheckButton" id="speakNumbersAsDigitsCheckButton">
-                                <property name="label" translatable="yes" comments="Translators: If this 
this setting is enabled, '123' will be spoken as the individual digits '1 2 3'; otherwise, it will be sent to 
the synthesizer and (likely) spoken as 'one hundred and twenty three'.">Speak _numbers as digits</property>
+                                <property name="label" translatable="yes" comments="Translators: If this 
setting is enabled, 123 will be spoken as the individual digits 1 2 3; otherwise, it will be sent to the 
synthesizer and (likely) spoken as one hundred and twenty three.">Speak _numbers as digits</property>
                                 <property name="use_action_appearance">False</property>
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
@@ -1585,7 +1698,7 @@
                                     <property name="can_focus">False</property>
                                     <child>
                                       <object class="GtkCheckButton" id="onlySpeakDisplayedTextCheckButton">
-                                        <property name="label" translatable="yes" comments="Translators: If 
this setting is enabled, Orca will only speak text which is actually displayed on the screen. It will NOT 
speak things like the role of an item (e.g. 'checkbox') or its state (e.g. 'not checked') or say 'mispelled' 
to indicate the presence of red squiggly spelling error lines -- things which Orca normally speaks. This 
setting is primarily intended for low vision users and sighted users with a learning disability.">Only speak 
displayed text</property>
+                                        <property name="label" translatable="yes" comments="Translators: If 
this setting is enabled, Orca will only speak text which is actually displayed on the screen. It will NOT 
speak things like the role of an item (e.g. checkbox) or its state (e.g. not checked) or say misspelled to 
indicate the presence of red squiggly spelling error lines -- things which Orca normally speaks. This setting 
is primarily intended for low vision users and sighted users with a learning disability.">Only speak 
displayed text</property>
                                         <property name="use_action_appearance">False</property>
                                         <property name="visible">True</property>
                                         <property name="can_focus">True</property>
@@ -1668,7 +1781,7 @@
                                         </child>
                                         <child>
                                           <object class="GtkCheckButton" 
id="enablePositionSpeakingCheckButton">
-                                            <property name="label" translatable="yes" comments="Translators: 
This checkbox toggles whether or not Orca says the child position (e.g., 'item 6 of 7').">Speak child 
p_osition</property>
+                                            <property name="label" translatable="yes" comments="Translators: 
This checkbox toggles whether or not Orca says the child position (e.g., item 6 of 7).">Speak child 
p_osition</property>
                                             <property name="use_action_appearance">False</property>
                                             <property name="visible">True</property>
                                             <property name="can_focus">True</property>
@@ -1708,7 +1821,7 @@
                                         </child>
                                         <child>
                                           <object class="GtkCheckButton" id="messagesAreDetailedCheckButton">
-                                            <property name="label" translatable="yes" comments="Translators: 
Orca's 'system' messages are similar in nature to notifications or announcements. They are most commonly used 
for Orca to communicate Orca-specific information to the user via speech, such as confirming the toggling of 
an Orca setting via command.  In instances where the message to be displayed is long/detailed, Orca provides 
a 'brief' alternative. Users who prefer that brief alternative can uncheck this checkbox.">_System messages 
are detailed</property>
+                                            <property name="label" translatable="yes" comments="Translators: 
Orca has system messages which are similar in nature to notifications or announcements. They are most 
commonly used for Orca to communicate Orca-specific information to the user via speech, such as confirming 
the toggling of an Orca setting via command.  In instances where the message to be displayed is 
long/detailed, Orca provides a brief alternative. Users who prefer that brief alternative can uncheck this 
checkbox.">_System messages are detailed</property>
                                             <property name="use_action_appearance">False</property>
                                             <property name="visible">True</property>
                                             <property name="can_focus">True</property>
@@ -1726,7 +1839,7 @@
                                         </child>
                                         <child>
                                           <object class="GtkCheckButton" id="useColorNamesCheckButton">
-                                            <property name="label" translatable="yes" comments="Translators: 
Orca has a command to present font and formatting information, including foreground and background color. The 
setting associated with this checkbox determines how Orca will speak colors: As rgb values or as names (e.g. 
'light blue').">S_peak colors as names</property>
+                                            <property name="label" translatable="yes" comments="Translators: 
Orca has a command to present font and formatting information, including foreground and background color. The 
setting associated with this checkbox determines how Orca will speak colors: As rgb values or as names (e.g. 
light blue).">S_peak colors as names</property>
                                             <property name="use_action_appearance">False</property>
                                             <property name="visible">True</property>
                                             <property name="can_focus">True</property>
@@ -1773,145 +1886,7 @@
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkFrame" id="progressBarUpdatesFrame">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="label_xalign">0</property>
-                            <property name="shadow_type">none</property>
-                            <child>
-                              <object class="GtkAlignment" id="progressBarUpdatesAlignment">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="left_padding">12</property>
-                                <child>
-                                  <object class="GtkGrid" id="progressBarUpdatesGrid">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <child>
-                                      <object class="GtkCheckButton" id="speechProgressBarCheckButton">
-                                        <property name="label" translatable="yes" 
context="ProgressBarUpdates" comments="Translators: This is an option in the Preferences dialog box related 
to the speaking of progress bar information. If checked, Orca will speak progress bar 
information.">Enable_d</property>
-                                        <property name="use_action_appearance">False</property>
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">True</property>
-                                        <property name="receives_default">False</property>
-                                        <property name="valign">start</property>
-                                        <property name="hexpand">True</property>
-                                        <property name="use_action_appearance">False</property>
-                                        <property name="use_underline">True</property>
-                                        <property name="active">True</property>
-                                        <property name="draw_indicator">True</property>
-                                        <signal name="toggled" handler="speechProgressBarChecked" 
swapped="no"/>
-                                      </object>
-                                      <packing>
-                                        <property name="left_attach">0</property>
-                                        <property name="top_attach">0</property>
-                                        <property name="width">1</property>
-                                        <property name="height">1</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkGrid" id="progressBarUpdatesOptionsGrid">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <child>
-                                          <object class="GtkLabel" id="speakProgressBarLabel">
-                                            <property name="visible">True</property>
-                                            <property name="can_focus">False</property>
-                                            <property name="xalign">0</property>
-                                            <property name="label" translatable="yes" context="ProgressBar" 
comments="Translators: Here this is a label for a spin button through which a user can customize the 
frequency in seconds an announcement should be made regarding the current value of a progress bar.">Frequency 
(secs):</property>
-                                            <property name="use_underline">True</property>
-                                            <property 
name="mnemonic_widget">speakProgressBarSpinButton</property>
-                                          </object>
-                                          <packing>
-                                            <property name="left_attach">0</property>
-                                            <property name="top_attach">0</property>
-                                            <property name="width">1</property>
-                                            <property name="height">1</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <object class="GtkLabel" id="progressBarVerbosityLabel">
-                                            <property name="visible">True</property>
-                                            <property name="can_focus">False</property>
-                                            <property name="xalign">0</property>
-                                            <property name="label" translatable="yes" comments="Translators: 
Orca has a setting which determines which progress bar updates should be announced. The options are all 
progress bars, only progress bars in the active application, or only progress bars in the current 
window.">Restrict to:</property>
-                                            <property name="use_underline">True</property>
-                                            <property name="mnemonic_widget">progressBarVerbosity</property>
-                                          </object>
-                                          <packing>
-                                            <property name="left_attach">0</property>
-                                            <property name="top_attach">1</property>
-                                            <property name="width">1</property>
-                                            <property name="height">1</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <object class="GtkComboBox" id="progressBarVerbosity">
-                                            <property name="visible">True</property>
-                                            <property name="can_focus">False</property>
-                                            <property name="model">model7</property>
-                                            <signal name="changed" handler="progressBarVerbosityChanged" 
swapped="no"/>
-                                            <child>
-                                              <object class="GtkCellRendererText" id="renderer33"/>
-                                              <attributes>
-                                                <attribute name="text">0</attribute>
-                                              </attributes>
-                                            </child>
-                                          </object>
-                                          <packing>
-                                            <property name="left_attach">1</property>
-                                            <property name="top_attach">1</property>
-                                            <property name="width">1</property>
-                                            <property name="height">1</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <object class="GtkSpinButton" id="speakProgressBarSpinButton">
-                                            <property name="visible">True</property>
-                                            <property name="can_focus">True</property>
-                                            <property name="invisible_char">●</property>
-                                            <property name="invisible_char_set">True</property>
-                                            <property name="adjustment">adjustment4</property>
-                                            <property name="climb_rate">1</property>
-                                            <property name="numeric">True</property>
-                                            <signal name="value-changed" 
handler="speakProgressBarValueChanged" swapped="no"/>
-                                          </object>
-                                          <packing>
-                                            <property name="left_attach">1</property>
-                                            <property name="top_attach">0</property>
-                                            <property name="width">1</property>
-                                            <property name="height">1</property>
-                                          </packing>
-                                        </child>
-                                      </object>
-                                      <packing>
-                                        <property name="left_attach">0</property>
-                                        <property name="top_attach">1</property>
-                                        <property name="width">1</property>
-                                        <property name="height">1</property>
-                                      </packing>
-                                    </child>
-                                  </object>
-                                </child>
-                              </object>
-                            </child>
-                            <child type="label">
-                              <object class="GtkLabel" id="progressBarUpdatesLabel">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="label" translatable="yes" comments="Translators: This is a 
label in the Preferences dialog box. It applies to several options related to which progress bars Orca should 
speak and how often Orca should speak them.">Progress Bar Updates</property>
-                                <attributes>
-                                  <attribute name="weight" value="bold"/>
-                                </attributes>
-                              </object>
-                            </child>
-                          </object>
-                          <packing>
-                            <property name="left_attach">0</property>
-                            <property name="top_attach">1</property>
-                            <property name="width">1</property>
-                            <property name="height">1</property>
-                          </packing>
+                          <placeholder/>
                         </child>
                       </object>
                       <packing>
@@ -2073,7 +2048,7 @@
                                         <property name="visible">True</property>
                                         <property name="can_focus">False</property>
                                         <property name="yalign">0.40999999642372131</property>
-                                        <property name="label" translatable="yes" comments="Translators: 
This string is associated with a combo box which allows the user to select the set of symbols to be used when 
Orca presents print strings on a refreshable braille display. Braille symbols vary from language to language 
due in part to what print letters exist for that language. The other reason braille symbols vary is due to 
which braille 'contractions' get used. Contractions are shorter forms of commonly-used letter combinations 
and words. For instance in English there is a single braille symbol for 'ing' (dots 3-4-6), and the letter 
'e' (dots 1-5) all by itself means 'every'. The list of rules which dictate what contractions should be used 
and whether or not they can be used in a particular context are stored in liblouis' tables.">Contraction 
_Table:</property>
+                                        <property name="label" translatable="yes" comments="Translators: 
This string is associated with a combo box which allows the user to select the set of symbols to be used when 
Orca presents print strings on a refreshable braille display. Braille symbols vary from language to language 
due in part to what print letters exist for that language. The other reason braille symbols vary is due to 
which braille contractions get used. Contractions are shorter forms of commonly-used letter combinations and 
words. For instance in English there is a single braille symbol for ing (dots 3-4-6), and the letter e (dots 
1-5) all by itself represents the word every. The list of rules which dictate what contractions should be 
used and whether or not they can be used in a particular context are stored in tables provided by 
liblouis.">Contraction _Table:</property>
                                         <property name="use_underline">True</property>
                                         <property name="mnemonic_widget">contractionTableCombo</property>
                                         <accessibility>
@@ -2225,7 +2200,7 @@
                                 <property name="column_spacing">20</property>
                                 <child>
                                   <object class="GtkRadioButton" id="brailleSelectionNoneButton">
-                                    <property name="label" translatable="yes" context="braille dots" 
comments="Translators: This option refers to the dot or dots in braille which will be used to 'underline' 
certain characters.">_None</property>
+                                    <property name="label" translatable="yes" context="braille dots" 
comments="Translators: This option refers to the dot or dots in braille which will be used to underline 
certain characters.">_None</property>
                                     <property name="use_action_appearance">False</property>
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
@@ -2343,7 +2318,7 @@
                                 <property name="column_spacing">20</property>
                                 <child>
                                   <object class="GtkRadioButton" id="brailleLinkNoneButton">
-                                    <property name="label" translatable="yes" context="braille dots" 
comments="Translators: This option refers to the dot or dots in braille which will be used to 'underline' 
certain characters.">_None</property>
+                                    <property name="label" translatable="yes" context="braille dots" 
comments="Translators: This option refers to the dot or dots in braille which will be used to underline 
certain characters.">_None</property>
                                     <property name="use_action_appearance">False</property>
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
@@ -2499,7 +2474,7 @@
                                         <property name="visible">True</property>
                                         <property name="can_focus">True</property>
                                         <property name="invisible_char">●</property>
-                                        <property name="adjustment">adjustment4</property>
+                                        <property name="adjustment">brailleFlashTimeAdjustment</property>
                                         <property name="climb_rate">1</property>
                                         <property name="numeric">True</property>
                                         <property name="value">5</property>
@@ -3395,7 +3370,7 @@
                                 <property name="can_focus">False</property>
                                 <child>
                                   <object class="GtkRadioButton" id="textBrailleNoneButton">
-                                    <property name="label" translatable="yes" context="braille dots" 
comments="Translators: This option refers to the dot or dots in braille which will be used to 'underline' 
certain characters.">_None</property>
+                                    <property name="label" translatable="yes" context="braille dots" 
comments="Translators: This option refers to the dot or dots in braille which will be used to underline 
certain characters.">_None</property>
                                     <property name="use_action_appearance">False</property>
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
diff --git a/src/orca/orca_gui_prefs.py b/src/orca/orca_gui_prefs.py
index aaa68fb..39d640f 100644
--- a/src/orca/orca_gui_prefs.py
+++ b/src/orca/orca_gui_prefs.py
@@ -1419,15 +1419,15 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
             indextime = TIME_FORMAT_24_HM_WITH_WORDS
         combobox3.set_active (indextime)
 
-        # Set the sensitivity of the "Update Interval" items, depending
-        # upon whether the "Speak progress bar updates" checkbox is checked.
-        #
-        enable = prefs["enableProgressBarUpdates"]
-        self.get_widget("speechProgressBarCheckButton").set_active(enable)
-        self.get_widget("progressBarUpdatesOptionsGrid").set_sensitive(enable)
+        self.get_widget("speakProgressBarUpdatesCheckButton").set_active(
+            prefs.get("speakProgressBarUpdates", settings.speakProgressBarUpdates))
+        self.get_widget("brailleProgressBarUpdatesCheckButton").set_active(
+            prefs.get("brailleProgressBarUpdates", settings.brailleProgressBarUpdates))
+        self.get_widget("beepProgressBarUpdatesCheckButton").set_active(
+            prefs.get("beepProgressBarUpdates", settings.beepProgressBarUpdates))
 
         interval = prefs["progressBarUpdateInterval"]
-        self.get_widget("speakProgressBarSpinButton").set_value(interval)
+        self.get_widget("progressBarUpdateIntervalSpinButton").set_value(interval)
 
         comboBox = self.get_widget("progressBarVerbosity")
         levels = [guilabels.PROGRESS_BAR_ALL,
@@ -1519,7 +1519,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         self.get_widget("flashIsDetailedCheckButton").set_active(enable)
 
         duration = prefs["brailleFlashTime"]
-        self.get_widget("speakProgressBarSpinButton").set_value(duration / 1000)
+        self.get_widget("brailleFlashTimeSpinButton").set_value(duration / 1000)
 
         # Key Echo pane.
         #
@@ -2505,28 +2505,9 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
             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' 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.get_widget("progressBarUpdatesOptionsGrid").set_sensitive(enable)
-
-    def speakProgressBarValueChanged(self, widget):
+    def progressBarUpdateIntervalValueChanged(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.
+           progressBarUpdateIntervalSpinButton GtkSpinButton widget.
 
         Arguments:
         - widget: the component that generated the signal.
diff --git a/src/orca/script_utilities.py b/src/orca/script_utilities.py
index 5d509e2..66f26ba 100644
--- a/src/orca/script_utilities.py
+++ b/src/orca/script_utilities.py
@@ -33,6 +33,7 @@ import locale
 import math
 import pyatspi
 import re
+import time
 
 from . import chnames
 from . import colornames
@@ -48,8 +49,11 @@ from . import orca_state
 from . import object_properties
 from . import pronunciation_dict
 from . import settings
+from . import settings_manager
 from . import text_attribute_names
 
+_settingsManager = settings_manager.getManager()
+
 #############################################################################
 #                                                                           #
 # Utilities                                                                 #
@@ -88,6 +92,7 @@ class Utilities:
         """
 
         self._script = script
+        self._activeProgressBars = {}
 
     #########################################################################
     #                                                                       #
@@ -749,6 +754,131 @@ class Utilities:
                 "region",
                 "search"]
 
+    def isProgressBar(self, obj):
+        if not (obj and obj.getRole() == pyatspi.ROLE_PROGRESS_BAR):
+            return False
+
+        try:
+            value = obj.queryValue()
+        except NotImplementedError:
+            msg = "ERROR: %s doesn't implement AtspiValue" % obj
+            debug.println(debug.LEVEL_INFO, msg)
+            return False
+        except:
+            msg = "ERROR: Exception getting value for %s" % obj
+            debug.println(debug.LEVEL_INFO, msg)
+            return False
+        else:
+            try:
+                if value.maximumValue == value.minimumValue:
+                    msg = "INFO: %s is busy indicator" % obj
+                    debug.println(debug.LEVEL_INFO, msg)
+                    return False
+            except:
+                msg = "INFO: %s is either busy indicator or broken" % obj
+                debug.println(debug.LEVEL_INFO, msg)
+                return False
+
+        return True
+
+    def isProgressBarUpdate(self, obj, event):
+        if not _settingsManager.getSetting('speakProgressBarUpdates') \
+           and not _settingsManager.getSetting('brailleProgressBarUpdates') \
+           and not _settingsManager.getSetting('beepProgressBarUpdates'):
+            return False, "Updates not enabled"
+
+        if not self.isProgressBar(obj):
+            return False, "Is not progress bar"
+
+        if self.hasNoSize(obj):
+            return False, "Has no size"
+
+        value = obj.queryValue()
+        percent = int((value.currentValue / (value.maximumValue - value.minimumValue)) * 100)
+        if percent == 100:
+            return True, "Percent is 100"
+
+        lastTime, lastValue = self.getProgressBarUpdateTimeAndValue(obj)
+        if percent == lastValue:
+            return False, "Value (%s) hasn't changed" % percent
+
+        interval = int(time.time() - lastTime)
+        if interval < int(_settingsManager.getSetting('progressBarUpdateInterval')):
+            return False, "Last update was only %is ago" % interval
+
+        isStatusBar = lambda x: x and x.getRole() == pyatspi.ROLE_STATUS_BAR
+        if pyatspi.findAncestor(obj, isStatusBar):
+            return False, "Is status bar descendant"
+
+        verbosity = _settingsManager.getSetting('progressBarVerbosity')
+        if verbosity == settings.PROGRESS_BAR_ALL:
+            return True, "Verbosity is all"
+
+        if verbosity == settings.PROGRESS_BAR_WINDOW:
+            topLevel = self.topLevelObject(obj)
+            if topLevel == orca_state.activeWindow:
+                return True, "Verbosity is window"
+            return False, "Window %s is not %s" % (topLevel, orca_state.activeWindow)
+
+        if verbosity == settings.PROGRESS_BAR_APPLICATION:
+            if event:
+                app = event.host_application
+            else:
+                app = obj.getApplication()
+            if app == orca_state.activeScript.app:
+                return True, "Verbosity is app"
+            return False, "App % is not %s" % (app, orca_state.activeScript.app)
+
+        return True, "Not handled by any other case"
+
+    def _cleanUpCachedProgressBars(self):
+        isValid = lambda x: not (self.isZombie(x) or self.isDead(x))
+        bars = list(filter(isValid, self._activeProgressBars))
+        self._activeProgressBars = {x:self._activeProgressBars.get(x) for x in bars}
+
+    def getProgressBarNumberAndCount(self, obj):
+        self._cleanUpCachedProgressBars()
+        if not obj in self._activeProgressBars:
+            self._activeProgressBars[obj] = 0.0, None
+
+        thisValue = self.getProgressBarUpdateTimeAndValue(obj)
+        index = list(self._activeProgressBars.values()).index(thisValue)
+        return index + 1, len(self._activeProgressBars)
+
+    def getMostRecentProgressBarUpdate(self):
+        self._cleanUpCachedProgressBars()
+        if not self._activeProgressBars.values():
+            return None, 0.0, None
+
+        sortedValues = sorted(self._activeProgressBars.values(), key=lambda x: x[0])
+        prevTime, prevValue = sortedValues[-1]
+        return list(self._activeProgressBars.keys())[-1], prevTime, prevValue
+
+    def getProgressBarUpdateTimeAndValue(self, obj):
+        if not obj in self._activeProgressBars:
+            self._activeProgressBars[obj] = 0.0, None
+        return self._activeProgressBars.get(obj)
+
+    def setProgressBarUpdateTimeAndValue(self, obj, lastTime=None, lastValue=None):
+        lastTime = lastTime or time.time()
+        lastValue = lastValue or self.getValueAsPercent(obj)
+        self._activeProgressBars[obj] = lastTime, lastValue
+
+    def getValueAsPercent(self, obj):
+        try:
+            value = obj.queryValue()
+            minval, val, maxval =  value.minimumValue, value.currentValue, value.maximumValue
+        except NotImplementedError:
+            msg = "ERROR: %s doesn't implement AtspiValue" % obj
+            debug.println(debug.LEVEL_INFO, msg)
+            return None
+        except:
+            msg = "ERROR: Exception getting value for %s" % obj
+            debug.println(debug.LEVEL_INFO, msg)
+            return None
+
+        return int((val / (maxval - minval)) * 100)
+
     def isStatic(self, obj):
         role = obj.getRole()
         try:
@@ -1565,6 +1695,19 @@ class Utilities:
 
         return rv
 
+    def hasNoSize(self, obj):
+        if not obj:
+            return False
+
+        try:
+            extents = obj.queryComponent().getExtents(0)
+        except:
+            msg = "ERROR: Exception getting extents for %s" % obj
+            debug.println(debug.LEVEL_INFO, msg)
+            return True
+
+        return not (extents.width and extents.height)
+
     def _hasNonDescendableDescendant(self, root):
         roles = [pyatspi.ROLE_PAGE_TAB_LIST,
                  pyatspi.ROLE_SPLIT_PANE,
diff --git a/src/orca/scripts/default.py b/src/orca/scripts/default.py
index 5964710..f3ce481 100644
--- a/src/orca/scripts/default.py
+++ b/src/orca/scripts/default.py
@@ -102,10 +102,6 @@ class Script(script.Script):
         #
         self._unicodeCurrencySymbols = []
 
-        # Used to determine whether progress bar value changes presented.
-        self.lastProgressBarTime = {}
-        self.lastProgressBarValue = {}
-
         self.lastSelectedMenu = None
 
         # A dictionary of non-standardly-named text attributes and their
@@ -843,12 +839,11 @@ class Script(script.Script):
         braille.setupKeyRanges(list(self.brailleBindings.keys()))
         speech.updatePunctuationLevel()
 
-    def updateBraille(self, obj, extraRegion=None):
+    def updateBraille(self, obj, **args):
         """Updates the braille display to show the give object.
 
         Arguments:
         - obj: the Accessible
-        - extra: extra Region to add to the end
         """
 
         if not _settingsManager.getSetting('enableBraille') \
@@ -859,21 +854,21 @@ class Script(script.Script):
         if not obj:
             return
 
-        self.clearBraille()
+        result, focusedRegion = self.brailleGenerator.generateBraille(obj, **args)
+        if not result:
+            return
 
+        self.clearBraille()
         line = self.getNewBrailleLine()
         braille.addLine(line)
+        self.addBrailleRegionsToLine(result, line)
 
-        result = self.brailleGenerator.generateBraille(obj)
-        self.addBrailleRegionsToLine(result[0], line)
-
+        extraRegion = args.get('extraRegion')
         if extraRegion:
             self.addBrailleRegionToLine(extraRegion, line)
-
-        if extraRegion:
             self.setBrailleFocus(extraRegion)
         else:
-            self.setBrailleFocus(result[1])
+            self.setBrailleFocus(focusedRegion)
 
         self.refreshBraille(True)
 
@@ -2803,22 +2798,27 @@ class Script(script.Script):
 
         obj = event.source
         role = obj.getRole()
-
         value = obj.queryValue()
         if "oldValue" in self.pointOfReference \
            and (value.currentValue == self.pointOfReference["oldValue"]):
             return
 
-        if role == pyatspi.ROLE_PROGRESS_BAR:
-            self.handleProgressBarUpdate(event, obj)
-            return
+        isProgressBarUpdate, msg = self.utilities.isProgressBarUpdate(obj, event)
+        msg = "DEFAULT: Is progress bar update: %s, %s" % (isProgressBarUpdate, msg)
+        debug.println(debug.LEVEL_INFO, msg)
 
-        if not self.utilities.isSameObject(obj, orca_state.locusOfFocus):
+        if not isProgressBarUpdate and obj != orca_state.locusOfFocus:
+            msg = "DEFAULT: Source != locusOfFocus (%s)" % orca_state.locusOfFocus
+            debug.println(debug.LEVEL_INFO, msg)
             return
 
+        if isProgressBarUpdate:
+            self.utilities.setProgressBarUpdateTimeAndValue(obj)
+
         self.pointOfReference["oldValue"] = value.currentValue
-        self.updateBraille(obj)
-        speech.speak(self.speechGenerator.generateSpeech(obj, alreadyFocused=True))
+        self.updateBraille(obj, isProgressBarUpdate=isProgressBarUpdate)
+        speech.speak(self.speechGenerator.generateSpeech(
+            obj, alreadyFocused=True, isProgressBarUpdate=isProgressBarUpdate))
 
     def onWindowActivated(self, event):
         """Called whenever a toplevel window is activated.
@@ -3223,113 +3223,6 @@ class Script(script.Script):
         word = self.utilities.adjustForRepeats(word)
         speech.speak(word, voice)
 
-    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/ settings.progressBarVerbosity matches the current location of the
-           progress bar.
-        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 _settingsManager.getSetting('enableProgressBarUpdates'):
-            makeAnnouncement = False
-            verbosity = _settingsManager.getSetting('progressBarVerbosity')
-            if verbosity == settings.PROGRESS_BAR_ALL:
-                makeAnnouncement = True
-            elif verbosity == settings.PROGRESS_BAR_WINDOW:
-                makeAnnouncement = self.utilities.isSameObject(
-                    self.utilities.topLevelObject(obj),
-                    self.utilities.activeWindow())
-            elif orca_state.locusOfFocus:
-                makeAnnouncement = self.utilities.isSameObject( \
-                    obj.getApplication(),
-                    orca_state.locusOfFocus.getApplication())
-
-            if makeAnnouncement:
-                currentTime = time.time()
-
-                # Check for defunct progress bars. Get rid of them if they
-                # are all defunct. Also find out which progress bar was
-                # the most recently updated.
-                #
-                defunctBars = 0
-                mostRecentUpdate = [obj, 0]
-                for key, value in list(self.lastProgressBarTime.items()):
-                    if value > mostRecentUpdate[1]:
-                        mostRecentUpdate = [key, value]
-                    try:
-                        isDefunct = \
-                            key.getState().contains(pyatspi.STATE_DEFUNCT)
-                    except:
-                        isDefunct = True
-                    if isDefunct:
-                        defunctBars += 1
-
-                if defunctBars == len(self.lastProgressBarTime):
-                    self.lastProgressBarTime = {}
-                    self.lastProgressBarValue = {}
-
-                # If this progress bar is not already known, create initial
-                # values for it.
-                #
-                if obj not in self.lastProgressBarTime:
-                    self.lastProgressBarTime[obj] = 0.0
-                if obj not in self.lastProgressBarValue:
-                    self.lastProgressBarValue[obj] = None
-
-                lastProgressBarTime = self.lastProgressBarTime[obj]
-                lastProgressBarValue = self.lastProgressBarValue[obj]
-                value = obj.queryValue()
-                try:
-                    if value.maximumValue == value.minimumValue:
-                        # This is a busy indicator and not a real progress bar.
-                        return
-                except:
-                    return
-                percentValue = int((value.currentValue / \
-                    (value.maximumValue - value.minimumValue)) * 100.0)
-
-                if (currentTime - lastProgressBarTime) > \
-                      _settingsManager.getSetting('progressBarUpdateInterval') \
-                   or percentValue == 100:
-                    if lastProgressBarValue != percentValue:
-                        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. But only speak the
-                        # index if this progress bar was not the most
-                        # recently updated to prevent chattiness.
-                        #
-                        if len(self.lastProgressBarTime) > 1:
-                            index = 0
-                            for key in list(self.lastProgressBarTime.keys()):
-                                if key == obj and key != mostRecentUpdate[0]:
-                                    label = messages.PROGRESS_BAR_NUMBER % (index + 1)
-                                    utterances.append(label)
-                                else:
-                                    index += 1
-
-                        utterances.extend(self.speechGenerator.generateSpeech(
-                            obj, alreadyFocused=True))
-
-                        speech.speak(utterances)
-
-                        self.lastProgressBarTime[obj] = currentTime
-                        self.lastProgressBarValue[obj] = percentValue
-
     def presentToolTip(self, obj):
         """
         Speaks the tooltip for the current object of interest.
diff --git a/src/orca/scripts/toolkits/Gecko/script.py b/src/orca/scripts/toolkits/Gecko/script.py
index 9625bbd..bf9fd8d 100644
--- a/src/orca/scripts/toolkits/Gecko/script.py
+++ b/src/orca/scripts/toolkits/Gecko/script.py
@@ -310,21 +310,3 @@ class Script(web.Script):
         msg = "GECKO: Passing along event to default script"
         debug.println(debug.LEVEL_INFO, msg)
         default.Script.onWindowDeactivated(self, event)
-
-    def handleProgressBarUpdate(self, event, obj):
-        """Determine whether this progress bar event should be spoken or not.
-        For Firefox, we don't want to speak the small "page load" progress
-        bar. All other Firefox progress bars get passed onto the parent
-        class for handling.
-
-        Arguments:
-        - event: if not None, the Event that caused this to happen
-        - obj:  the Accessible progress bar object.
-        """
-
-        rolesList = [pyatspi.ROLE_PROGRESS_BAR, \
-                     pyatspi.ROLE_STATUS_BAR, \
-                     pyatspi.ROLE_FRAME, \
-                     pyatspi.ROLE_APPLICATION]
-        if not self.utilities.hasMatchingHierarchy(event.source, rolesList):
-            default.Script.handleProgressBarUpdate(self, event, obj)
diff --git a/src/orca/scripts/toolkits/WebKitGtk/script.py b/src/orca/scripts/toolkits/WebKitGtk/script.py
index 1afeaa8..096ba9c 100644
--- a/src/orca/scripts/toolkits/WebKitGtk/script.py
+++ b/src/orca/scripts/toolkits/WebKitGtk/script.py
@@ -610,12 +610,11 @@ class Script(default.Script):
 
         return textLine
 
-    def updateBraille(self, obj, extraRegion=None):
+    def updateBraille(self, obj, **args):
         """Updates the braille display to show the given object.
 
         Arguments:
         - obj: the Accessible
-        - extra: extra Region to add to the end
         """
 
         if not _settingsManager.getSetting('enableBraille') \
@@ -629,7 +628,7 @@ class Script(default.Script):
         if not self.utilities.isWebKitGtk(obj) \
            or (not self.utilities.isInlineContainer(obj) \
                and not self.utilities.isTextListItem(obj)):
-            default.Script.updateBraille(self, obj, extraRegion)
+            default.Script.updateBraille(self, obj, **args)
             return
 
         brailleLine = self.getNewBrailleLine(clearBraille=True, addLine=True)
@@ -645,6 +644,7 @@ class Script(default.Script):
             self.addBrailleRegionsToLine(regions, brailleLine)
             self.setBrailleFocus(fRegion)
 
+        extraRegion = args.get('extraRegion')
         if extraRegion:
             self.addBrailleRegionToLine(extraRegion, brailleLine)
 
diff --git a/src/orca/scripts/web/script.py b/src/orca/scripts/web/script.py
index ed4defe..165e2c4 100644
--- a/src/orca/scripts/web/script.py
+++ b/src/orca/scripts/web/script.py
@@ -740,7 +740,7 @@ class Script(default.Script):
         self.displayContents(contents)
         self.speakContents(contents)
 
-    def updateBraille(self, obj, extraRegion=None):
+    def updateBraille(self, obj, **args):
         """Updates the braille display to show the given object."""
 
         if not _settingsManager.getSetting('enableBraille') \
@@ -750,7 +750,7 @@ class Script(default.Script):
 
         if not (self._lastCommandWasCaretNav or self._lastCommandWasStructNav) \
            or self._inFocusMode or not self.utilities.inDocumentContent():
-            super().updateBraille(obj, extraRegion)
+            super().updateBraille(obj, **args)
             return
 
         obj, offset = self.utilities.getCaretContext(documentFrame=None)
diff --git a/src/orca/scripts/web/script_utilities.py b/src/orca/scripts/web/script_utilities.py
index 3ffbcc5..9180454 100644
--- a/src/orca/scripts/web/script_utilities.py
+++ b/src/orca/scripts/web/script_utilities.py
@@ -2447,25 +2447,13 @@ class Utilities(script_utilities.Utilities):
 
     def hasNoSize(self, obj):
         if not (obj and self.inDocumentContent(obj)):
-            return False
+            return super().hasNoSize(obj)
 
         rv = self._hasNoSize.get(hash(obj))
         if rv is not None:
             return rv
 
-        try:
-            extents = obj.queryComponent().getExtents(0)
-        except:
-            msg = "WEB: Exception getting extents for %s" % obj
-            debug.println(debug.LEVEL_INFO, msg)
-            rv = True
-        else:
-            rv = not (extents.width and extents.height)
-            if rv:
-                msg = "WEB: %s has no size %s" % (obj, extents)
-                debug.println(debug.LEVEL_INFO, msg)
-
-        self._hasNoSize[hash(obj)] = rv
+        self._hasNoSize[hash(obj)] = super().hasNoSize(obj)
         return rv
 
     def doNotDescendForCaret(self, obj):
diff --git a/src/orca/settings.py b/src/orca/settings.py
index 46db573..be8060a 100644
--- a/src/orca/settings.py
+++ b/src/orca/settings.py
@@ -78,8 +78,10 @@ userCustomizableSettings = [
     "enabledSpokenTextAttributes",
     "enabledBrailledTextAttributes",
     "textAttributesBrailleIndicator",
-    "enableProgressBarUpdates",
     "profile",
+    "speakProgressBarUpdates",
+    "brailleProgressBarUpdates",
+    "beepProgressBarUpdates",
     "progressBarUpdateInterval",
     "progressBarVerbosity",
     "enableContractedBraille",
@@ -262,7 +264,9 @@ mouseDwellDelay            = 0
 mouseDwellMaxDrift         = 3
 
 # Progressbars
-enableProgressBarUpdates   = True
+speakProgressBarUpdates    = True
+brailleProgressBarUpdates  = False
+beepProgressBarUpdates     = False
 progressBarUpdateInterval  = 10
 progressBarVerbosity       = PROGRESS_BAR_APPLICATION
 
diff --git a/src/orca/speech_generator.py b/src/orca/speech_generator.py
index 8f90e8e..07a07c4 100644
--- a/src/orca/speech_generator.py
+++ b/src/orca/speech_generator.py
@@ -1708,6 +1708,33 @@ class SpeechGenerator(generator.Generator):
         result.extend(acss)
         return result
 
+    def _generateProgressBarIndex(self, obj, **args):
+        if not args.get('isProgressBarUpdate') \
+           or not _settingsManager.getSetting('speakProgressBarUpdates'):
+            return []
+
+        result = []
+        acc, updateTime, updateValue = self._script.utilities.getMostRecentProgressBarUpdate()
+        if acc != obj:
+            number, count = self._script.utilities.getProgressBarNumberAndCount(obj)
+            result = [messages.PROGRESS_BAR_NUMBER % (number)]
+            result.extend(self.voice(SYSTEM))
+
+        return result
+
+    def _generateProgressBarValue(self, obj, **args):
+        if args.get('isProgressBarUpdate') \
+           and not _settingsManager.getSetting('speakProgressBarUpdates'):
+            return []
+
+        result = []
+        percent = self._script.utilities.getValueAsPercent(obj)
+        if percent is not None:
+            result.append(messages.percentage(percent))
+            result.extend(self.voice(SYSTEM))
+
+        return result
+
     def _generateDefaultButton(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the default button in a dialog.



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]