From 814e210a7fd49e568ca99e02614aec140b072a1e Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Mon, 21 Oct 2024 16:48:01 -0400 Subject: [PATCH] Added ClassicPreferences plugin but it needs more work before it is functioning. --- .../ClassicPreferences.plugin | 14 + .../ClassicPreferences/ClassicPreferences.py | 84 + .../plugins/ClassicPreferences/Makefile.am | 10 + .../plugins/ClassicPreferences/__init__.py | 0 .../ClassicPreferences/cthulhu-setup.ui | 3432 ++++++++++++++++ .../ClassicPreferences/cthulhu_gui_prefs.py | 3636 +++++++++++++++++ .../ClassicPreferences/cthulhu_gui_profile.py | 163 + 7 files changed, 7339 insertions(+) create mode 100644 src/cthulhu/plugins/ClassicPreferences/ClassicPreferences.plugin create mode 100644 src/cthulhu/plugins/ClassicPreferences/ClassicPreferences.py create mode 100644 src/cthulhu/plugins/ClassicPreferences/Makefile.am create mode 100644 src/cthulhu/plugins/ClassicPreferences/__init__.py create mode 100644 src/cthulhu/plugins/ClassicPreferences/cthulhu-setup.ui create mode 100644 src/cthulhu/plugins/ClassicPreferences/cthulhu_gui_prefs.py create mode 100644 src/cthulhu/plugins/ClassicPreferences/cthulhu_gui_profile.py diff --git a/src/cthulhu/plugins/ClassicPreferences/ClassicPreferences.plugin b/src/cthulhu/plugins/ClassicPreferences/ClassicPreferences.plugin new file mode 100644 index 0000000..f04256e --- /dev/null +++ b/src/cthulhu/plugins/ClassicPreferences/ClassicPreferences.plugin @@ -0,0 +1,14 @@ +[Plugin] +Module=ClassicPreferences +Loader=python3 +Name=Classic Preferences UI +Description=The classic preferences dialog +Authors=Chrys chrys@linux-a11y.org +Website= +Version=1.0 +Copyright= +Builtin=true +Hidden=true +Depends= +Icon= +Help= diff --git a/src/cthulhu/plugins/ClassicPreferences/ClassicPreferences.py b/src/cthulhu/plugins/ClassicPreferences/ClassicPreferences.py new file mode 100644 index 0000000..98ae70b --- /dev/null +++ b/src/cthulhu/plugins/ClassicPreferences/ClassicPreferences.py @@ -0,0 +1,84 @@ +from cthulhu import plugin + +import gi, time +gi.require_version('Peas', '1.0') +from gi.repository import GObject +from gi.repository import Peas +import importlib, os +import cthulhu_gui_prefs + +class ClassicPreferences(GObject.Object, Peas.Activatable, plugin.Plugin): + #__gtype_name__ = 'ClassicPreferences' + + object = GObject.Property(type=GObject.Object) + def __init__(self): + plugin.Plugin.__init__(self) + def do_activate(self): + API = self.object + self.connectSignal("setup-inputeventhandlers-completed", self.setupCompatBinding) + #self.setupCompatBinding(API.app) + def setupCompatBinding(self, app): + cmdnames = app.getDynamicApiManager().getAPI('Cmdnames') + inputEventHandlers = app.getDynamicApiManager().getAPI('inputEventHandlers') + inputEventHandlers['preferencesSettingsHandler'] = app.getAPIHelper().createInputEventHandler(self.showPreferencesGUI, cmdnames.SHOW_PREFERENCES_GUI) + inputEventHandlers['appPreferencesSettingsHandler'] = app.getAPIHelper().createInputEventHandler(self.showAppPreferencesGUI, cmdnames.SHOW_APP_PREFERENCES_GUI) + def do_deactivate(self): + API = self.object + inputEventHandlers = API.app.getDynamicApiManager().getAPI('inputEventHandlers') + del inputEventHandlers['preferencesSettingsHandler'] + del inputEventHandlers['appPreferencesSettingsHandler'] + def do_update_state(self): + API = self.object + def showAppPreferencesGUI(self, script=None, inputEvent=None): + """Displays the user interface to configure the settings for a + specific applications within Cthulhu and set up those app-specific + user preferences using a GUI. + + Returns True to indicate the input event has been consumed. + """ + API = self.object + cthulhu_state = API.app.getDynamicApiManager().getAPI('CthulhuState') + settings = API.app.getDynamicApiManager().getAPI('Settings') + _settingsManager = API.app.getDynamicApiManager().getAPI('SettingsManager').getManager() + _scriptManager = API.app.getDynamicApiManager().getAPI('ScriptManager').getManager() + + prefs = {} + for key in settings.userCustomizableSettings: + prefs[key] = _settingsManager.getSetting(key) + + script = script or cthulhu_state.activeScript + self._showPreferencesUI(script, prefs) + return True + def showPreferencesGUI(self, script=None, inputEvent=None): + """Displays the user interface to configure Cthulhu and set up + user preferences using a GUI. + + Returns True to indicate the input event has been consumed. + """ + API = self.object + cthulhu_state = API.app.getDynamicApiManager().getAPI('CthulhuState') + settings = API.app.getDynamicApiManager().getAPI('Settings') + _settingsManager = API.app.getDynamicApiManager().getAPI('SettingsManager').getManager() + _scriptManager = API.app.getDynamicApiManager().getAPI('ScriptManager').getManager() + debug = API.app.getDynamicApiManager().getAPI('Debug') + prefs = _settingsManager.getGeneralSettings(_settingsManager.profile) + script = _scriptManager.getDefaultScript() + self._showPreferencesUI(script, prefs) + return True + def _showPreferencesUI(self, script, prefs): + API = self.object + cthulhu_state = API.app.getDynamicApiManager().getAPI('CthulhuState') + debug = API.app.getDynamicApiManager().getAPI('Debug') + cthulhu_platform = API.app.getDynamicApiManager().getAPI('CthulhuPlatform') + + if cthulhu_state.cthulhuOS: + cthulhu_state.cthulhuOS.showGUI() + return + + uiFile = os.path.join(self.getModuleDir(), + "cthulhu-setup.ui") + + cthulhu_state.cthulhuOS = cthulhu_gui_prefs.CthulhuSetupGUI(uiFile, "cthulhuSetupWindow", prefs, API.app) + cthulhu_state.cthulhuOS.init(script) + cthulhu_state.cthulhuOS.setTranslationContext(self.getTranslationContext()) + cthulhu_state.cthulhuOS.showGUI() diff --git a/src/cthulhu/plugins/ClassicPreferences/Makefile.am b/src/cthulhu/plugins/ClassicPreferences/Makefile.am new file mode 100644 index 0000000..9ea972c --- /dev/null +++ b/src/cthulhu/plugins/ClassicPreferences/Makefile.am @@ -0,0 +1,10 @@ +cthulhu_python_PYTHON = \ + __init__.py \ + ClassicPreferences.plugin \ + ClassicPreferences.py \ + cthulhu_gui_prefs.py \ + cthulhu_gui_profile.py \ + cthulhu-setup.ui + +cthulhu_pythondir=$(pkgpythondir)/plugins/ClassicPreferences + diff --git a/src/cthulhu/plugins/ClassicPreferences/__init__.py b/src/cthulhu/plugins/ClassicPreferences/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/cthulhu/plugins/ClassicPreferences/cthulhu-setup.ui b/src/cthulhu/plugins/ClassicPreferences/cthulhu-setup.ui new file mode 100644 index 0000000..f6a6c0a --- /dev/null +++ b/src/cthulhu/plugins/ClassicPreferences/cthulhu-setup.ui @@ -0,0 +1,3432 @@ + + + + + 9999 + 10 + 1 + 10 + + + + + + + + + + + + + + + + + + + + + Default + + + Uppercase + + + Hyperlink + + + System + + + + + + + + + + + Line + + + Sentence + + + + + + + + + + + All + + + Application + + + Window + + + + + + + + + + + Insert, KP_Insert + + + KP_Insert + + + Insert + + + Caps_Lock, Shift_Lock + + + + + + + + + + + + + 10 + 5 + 0.10000000149 + 1 + + + 9999 + 10 + 1 + 10 + + + 100 + 50 + 1 + 10 + + + 10 + 10 + 0.10000000149 + 1 + + + False + Screen Reader Preferences + normal + + + + + True + False + vertical + 3 + + + True + False + end + + + _Help + True + True + True + False + True + + + + False + False + 0 + + + + + _Apply + True + True + True + False + True + + + + False + False + 1 + + + + + _Cancel + True + True + True + False + True + + + + False + False + 2 + + + + + _OK + True + True + True + False + True + + + + False + False + 3 + + + + + False + True + end + 0 + + + + + True + True + + + True + False + 12 + 10 + 10 + + + True + False + 0 + none + + + True + False + 12 + + + True + False + + + _Desktop + True + True + False + True + True + True + + + + 0 + 0 + + + + + _Laptop + True + True + False + True + True + generalDesktopButton + + + + 0 + 1 + + + + + + + + + True + False + Keyboard Layout + + + + + + + + 0 + 0 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + False + True + + + 150 + True + False + 0 + Active _Profile: + True + availableProfilesComboBox1 + + + + + + 0 + 0 + + + + + True + False + 0 + Start-up Profile: + availableProfilesComboBox2 + + + + + + 0 + 1 + + + + + 200 + True + False + model9 + + + + 0 + + + + + 1 + 0 + + + + + True + False + model9 + + + + 0 + + + + + 1 + 1 + + + + + _Load + True + True + True + True + + + + 2 + 0 + + + + + Save _As + True + True + True + True + + + + 3 + 0 + + + + + _Remove + True + True + True + True + + + + 4 + 0 + + + + + + + + + + + + + + + + + + True + False + Profiles + + + + + + + + 0 + 3 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + False + + + _Present tooltips + True + True + False + True + 0 + True + + + + 0 + 0 + + + + + Speak object under mo_use + True + True + False + True + 0 + True + True + + + + 0 + 1 + + + + + + + + + True + False + Mouse + + + + + + + + 0 + 1 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + False + True + + + 150 + True + False + 0 + _Time format: + True + timeFormatCombo + + + + + + 0 + 0 + + + + + True + False + 0 + Dat_e format: + True + dateFormatCombo + + + + + + 0 + 1 + + + + + True + False + liststore1 + + + + + 0 + + + + + 1 + 0 + + + + + True + False + liststore2 + + + + + 0 + + + + + 1 + 1 + + + + + + + + + True + False + Time and Date + + + + + + + + 0 + 2 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + False + + + _Speak updates + True + True + False + start + True + True + 0 + True + True + + + + 0 + 0 + + + + + _Braille updates + True + True + False + True + True + True + + + + 0 + 1 + + + + + True + False + 10 + 20 + + + True + True + + 10 + progressBarUpdateIntervalAdjustment + 1 + True + 10 + + + + 1 + 0 + + + + + True + False + Frequency (secs): + True + progressBarUpdateIntervalSpinButton + 0 + + + 0 + 0 + + + + + True + False + Applies to: + True + progressBarVerbosity + 0 + + + 0 + 1 + + + + + True + False + model7 + + + + + 0 + + + + + 1 + 1 + + + + + 0 + 3 + + + + + Bee_p updates + True + True + False + start + True + True + 0 + True + True + + + + 0 + 2 + + + + + + + + + True + False + Progress Bar Updates + + + + + + + + 1 + 0 + 2 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + False + + + Enable _rewind and fast forward in Say All + True + True + False + True + 0 + True + + + + 0 + 0 + + + + + Enable _structural navigation in Say All + True + True + False + True + 0 + True + True + + + + 0 + 1 + + + + + True + False + 20 + 20 + + + -` + True + False + Say All B_y: + True + sayAllStyle + 0 + + + + + + 0 + 0 + + + + + True + False + model2 + + + + + 0 + + + + + 1 + 0 + + + + + 0 + 8 + + + + + Announce block_quotes in Say All + True + True + False + True + 0 + True + True + + + + 0 + 2 + + + + + Announce li_sts in Say All + True + True + False + True + 0 + True + True + + + + 0 + 5 + + + + + Announce _tables in Say All + True + True + False + True + 0 + True + True + + + + 0 + 7 + + + + + Announce _panels in Say All + True + True + False + True + 0 + True + True + + + + 0 + 6 + + + + + Announce _forms in Say All + True + True + False + True + 0 + True + True + + + + 0 + 3 + + + + + Announce land_marks in Say All + True + True + False + True + 0 + True + True + + + + 0 + 4 + + + + + + + + + True + False + Say All + + + + + + + + 1 + 2 + 2 + + + + + + + True + False + General + + + False + + + + + True + False + 12 + 10 + 40 + + + True + False + 0 + none + + + True + False + 12 + + + True + False + 12 + + + True + False + 1 + Vo_lume: + True + right + volumeScale + + + + + + 0 + 8 + + + + + True + True + volumeAdjustment + 1 + right + + + + + 1 + 8 + + + + + True + True + pitchAdjustment + 1 + right + + + + + 1 + 7 + + + + + True + False + 1 + Pi_tch: + True + right + pitchScale + + + 0 + 7 + + + + + True + True + rateAdjustment + 0 + 0 + right + + + + + 1 + 6 + + + + + True + False + 1 + _Rate: + True + right + rateScale + + + 0 + 6 + + + + + True + False + + + + 1 + 4 + + + + + True + False + 1 + _Person: + True + right + speechFamilies + + + 0 + 4 + + + + + True + False + + + + 1 + 3 + + + + + True + False + 1 + _Language: + True + right + speechLanguages + + + 0 + 3 + + + + + True + False + + + + 1 + 2 + + + + + True + False + 1 + Speech synthesi_zer: + True + right + speechServers + + + 0 + 2 + + + + + True + False + + + + 1 + 1 + + + + + True + False + 1 + Speech _system: + True + right + speechSystems + + + 0 + 1 + + + + + True + False + model1 + + + + + 0 + + + + + 1 + 0 + + + + + True + False + 1 + _Voice type: + True + right + voiceTypesCombo + + + 0 + 0 + + + + + True + False + _Capitalization style: + True + right + capitalizationStyle + 1 + + + 0 + 5 + + + + + True + False + + + + + 0 + + + + + 1 + 5 + + + + + + + + + True + False + Voice Type Settings + + + + + + + + 0 + 0 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + False + + + Break speech into ch_unks between pauses + True + True + False + True + True + True + + + + 0 + 0 + + + + + Speak multicase strings as wor_ds + True + True + False + True + True + True + + + + 0 + 1 + + + + + Speak _numbers as digits + True + True + False + True + True + True + + + + 0 + 2 + + + + + + + + + + + + True + False + Global Voice Settings + + + + + + + + 1 + 0 + + + + + 1 + + + + + True + False + Voice + + + 1 + False + + + + + True + False + 12 + 10 + 10 + + + _Enable speech + True + True + False + True + True + True + + + + 0 + 0 + + + + + True + False + vertical + 50 + 20 + + + True + False + 20 + 20 + + + True + False + 0 + none + + + True + False + 12 + + + True + False + + + Brie_f + True + True + False + True + True + True + + + + 0 + 0 + + + + + Ver_bose + True + True + False + True + True + speechBriefButton + + + + 0 + 1 + + + + + + + + + True + False + Verbosity + + + + + + + + 0 + 0 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + False + + + _None + True + True + False + True + True + True + + + + 0 + 0 + + + + + So_me + True + True + False + True + True + noneButton + + + + 0 + 1 + + + + + M_ost + True + True + False + True + True + noneButton + + + + 0 + 2 + + + + + _All + True + True + False + True + True + noneButton + + + + 0 + 3 + + + + + + + + + True + False + Punctuation Level + + + + + + + + 0 + 1 + + + + + 0 + 0 + + + + + True + False + 20 + 10 + + + True + False + 0 + none + + + True + False + 12 + + + True + False + + + Only speak displayed text + True + True + False + True + True + True + + + + 0 + 0 + + + + + True + False + + + Speak blank lines + True + True + False + True + True + True + + + + 0 + 0 + + + + + Speak _indentation and justification + True + True + False + True + True + + + + 0 + 1 + + + + + Spea_k object mnemonics + True + True + False + True + True + True + + + + 0 + 3 + + + + + Speak child p_osition + True + True + False + True + True + True + + + + 0 + 4 + + + + + Speak tutorial messages + True + True + False + True + True + True + + + + 0 + 5 + + + + + _System messages are detailed + True + True + False + True + 0 + True + True + + + + 0 + 7 + + + + + S_peak colors as names + True + True + False + True + 0 + True + True + + + + 0 + 8 + + + + + Announce block_quotes during navigation + True + True + False + True + 0 + True + True + + + + 0 + 9 + + + + + Announce _lists during navigation + True + True + False + True + 0 + True + True + + + + 0 + 12 + + + + + Announce _tables during navigation + True + True + False + True + 0 + True + True + + + + 0 + 14 + + + + + Speak _misspelled-word indicator + True + True + False + True + 0 + True + True + + + + 0 + 2 + + + + + Announce _panels during navigation + True + True + False + True + 0 + True + True + + + + 0 + 13 + + + + + Announce land_marks during navigation + True + True + False + True + 0 + True + True + + + + 0 + 11 + + + + + Announce _forms during navigation + True + True + False + True + 0 + True + True + + + + 0 + 10 + + + + + Speak _description + True + True + False + True + 0 + True + True + + + + 0 + 6 + + + + + 0 + 1 + + + + + Speak full row in sp_readsheets + True + True + False + True + 0 + True + True + + + + 0 + 4 + + + + + Speak full row in _document tables + True + True + False + True + 0 + True + True + + + + 0 + 3 + + + + + Speak full row in _GUI tables + True + True + False + True + 0 + True + True + + + + 0 + 2 + + + + + + + + + True + False + Spoken Context + + + + + + + + 0 + 0 + + + + + + + + 1 + 0 + + + + + 0 + 1 + + + + + 2 + + + + + True + False + Speech + + + 2 + False + + + + + True + False + 12 + 10 + 10 + + + True + False + + + Enable Braille _support + True + True + False + True + True + True + + + + 0 + 0 + + + + + 0 + 0 + + + + + True + False + 10 + 10 + + + True + False + 0 + none + + + True + False + 12 + + + True + False + + + _Abbreviated role names + True + True + False + True + True + + + + 0 + 1 + + + + + Disable _end of line symbol + True + True + False + True + True + + + + 0 + 0 + + + + + True + False + 20 + 20 + + + True + False + 0.40999999642372131 + Contraction _Table: + True + contractionTableCombo + + + + + + 0 + 0 + + + + + True + False + + + + 1 + 0 + + + + + 0 + 4 + + + + + _Enable Contracted Braille + True + True + False + True + True + + + + 0 + 3 + + + + + Enable _word wrap + True + True + False + True + True + + + + 0 + 2 + + + + + + + + + True + False + Display Settings + + + + + + + + 0 + 0 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + False + + + Brie_f + True + True + False + True + True + True + + + + 0 + 0 + + + + + Ver_bose + True + True + False + True + True + brailleBriefButton + + + + 0 + 1 + + + + + + + + + True + False + Verbosity + + + + + + + + 0 + 1 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + False + 20 + + + _None + True + True + False + True + True + True + + + + 0 + 0 + + + + + Dot _7 + True + True + False + True + True + brailleSelectionNoneButton + + + + 0 + 1 + + + + + Dot _8 + True + True + False + True + True + brailleSelectionNoneButton + + + + 1 + 0 + + + + + Dots 7 an_d 8 + True + True + False + True + True + brailleSelectionNoneButton + + + + 1 + 1 + + + + + + + + + True + False + Selection Indicator + + + + + + + + 0 + 2 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + False + 20 + + + _None + True + True + False + True + True + True + + + + 0 + 0 + + + + + Dot _7 + True + True + False + True + True + brailleLinkNoneButton + + + + 0 + 1 + + + + + Dot _8 + True + True + False + True + True + brailleLinkNoneButton + + + + 1 + 0 + + + + + Dots 7 an_d 8 + True + True + False + True + True + brailleLinkNoneButton + + + + 1 + 1 + + + + + + + + + True + False + Hyperlink Indicator + + + + + + + + 0 + 3 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + False + + + Enable flash _messages + True + True + False + True + 0 + True + True + + + + 0 + 0 + + + + + True + False + + + True + False + D_uration (secs): + True + brailleFlashTimeSpinButton + 0 + + + 0 + 0 + + + + + True + True + + brailleFlashTimeAdjustment + 1 + True + 5 + + + + 1 + 0 + + + + + 0 + 3 + + + + + Messages are _persistent + True + True + False + True + 0 + True + True + + + + 0 + 2 + + + + + Messages are _detailed + True + True + False + True + 0 + True + True + + + + 0 + 1 + + + + + + + + + True + False + Flash Message Settings + end + + + + + + + + 1 + 0 + + + + + + + + + + + + + + 0 + 1 + + + + + 3 + + + + + True + False + Braille + + + 3 + False + + + + + True + False + 12 + + + Enable _key echo + True + True + False + True + True + + + + 0 + 0 + + + + + True + False + 25 + + + True + False + + + Enable _alphabetic keys + True + True + False + True + True + + + + 0 + 0 + + + + + Enable n_umeric keys + True + True + False + True + True + + + + 0 + 1 + + + + + Enable _punctuation keys + True + True + False + True + True + + + + 0 + 2 + + + + + Enable _space + True + True + False + True + True + + + + 0 + 3 + + + + + Enable _modifier keys + True + True + False + True + True + + + + 0 + 4 + + + + + Enable _function keys + True + True + False + True + True + + + + 0 + 5 + + + + + Enable ac_tion keys + True + True + False + True + True + + + + 0 + 6 + + + + + Enable _navigation keys + True + True + False + True + True + + + + 0 + 7 + + + + + Enable non-spacing _diacritical keys + True + True + False + True + True + + + + 0 + 8 + + + + + + + + + + 0 + 1 + + + + + Enable echo by cha_racter + True + True + False + True + True + + + + 0 + 2 + + + + + Enable echo by _word + True + True + False + True + True + + + + 0 + 3 + + + + + Enable echo by _sentence + True + True + False + True + True + + + + 0 + 4 + + + + + 4 + + + + + True + False + Echo + + + 4 + False + + + + + True + False + 12 + 10 + 10 + + + True + False + 10 + 10 + + + True + False + Screen Reader _Modifier Key(s): + True + cthulhuModifierComboBox + + + + + + 0 + 0 + + + + + True + False + model8 + + + + + 0 + + + + + 1 + 0 + + + + + 0 + 0 + + + + + True + True + True + True + in + + + True + True + True + + + + + + + + 0 + 1 + + + + + 5 + + + + + True + False + Key Bindings + + + 5 + False + + + + + True + False + + + True + False + True + True + 5 + 0 + none + + + True + False + 12 + + + True + True + 5 + in + + + True + True + True + + + + + + + + + + + + True + False + Pronunciation Dictionary + + + + + + + + 0 + 0 + + + + + True + False + + + _New entry + True + True + True + 5 + True + + + + 0 + 0 + + + + + _Delete + True + True + True + 5 + True + + + + 1 + 0 + + + + + 0 + 1 + + + + + 6 + + + + + True + False + Pronunciation + + + 6 + False + + + + + True + False + True + True + + + True + False + True + True + 5 + 0 + none + + + True + False + 12 + + + True + False + True + True + + + True + False + + + _Speak all + True + True + 5 + True + + + + 0 + 0 + + + + + Speak _none + True + True + 5 + True + + + + 1 + 0 + + + + + _Reset + True + True + True + 5 + True + + + + 2 + 0 + + + + + 0 + 1 + + + + + True + True + True + True + 5 + in + + + True + True + True + + + + + + + + 0 + 0 + + + + + + + + + True + False + Text attributes + + + + + + + + 0 + 0 + + + + + True + False + + + True + False + 5 + 0 + none + + + True + False + 12 + + + True + False + + + Move to _bottom + True + True + True + 5 + True + + + + 0 + 3 + + + + + Move _down one + True + True + True + 5 + True + + + + 0 + 2 + + + + + Move _up one + True + True + True + 5 + True + + + + 0 + 1 + + + + + Move to _top + True + True + True + 5 + True + + + + 0 + 0 + + + + + + + + + True + False + Adjust selected attribute + + + + + + + + 0 + 0 + + + + + True + False + 5 + 0 + none + + + True + False + 12 + + + True + False + + + _None + True + True + False + True + True + True + + + + 0 + 0 + + + + + Dot _7 + True + True + False + True + True + textBrailleNoneButton + + + + 0 + 1 + + + + + Dot _8 + True + True + False + True + True + textBrailleNoneButton + + + + 0 + 2 + + + + + Dots 7 an_d 8 + True + True + False + True + True + textBrailleNoneButton + + + + 0 + 3 + + + + + + + + + True + False + Braille Indicator + + + + + + + + 0 + 1 + + + + + 1 + 0 + + + + + 7 + + + + + True + False + Text Attributes + + + 7 + False + + + + + True + True + 3 + 1 + + + + + + helpButton + applyButton + cancelButton + okButton + + + diff --git a/src/cthulhu/plugins/ClassicPreferences/cthulhu_gui_prefs.py b/src/cthulhu/plugins/ClassicPreferences/cthulhu_gui_prefs.py new file mode 100644 index 0000000..2cc78d5 --- /dev/null +++ b/src/cthulhu/plugins/ClassicPreferences/cthulhu_gui_prefs.py @@ -0,0 +1,3636 @@ +# Cthulhu +# +# Copyright 2005-2009 Sun Microsystems Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., Franklin Street, Fifth Floor, +# Boston MA 02110-1301 USA. + +"""Displays a GUI for the user to set Cthulhu preferences.""" + +__id__ = "$Id$" +__version__ = "$Revision$" +__date__ = "$Date$" +__copyright__ = "Copyright (c) 2005-2009 Sun Microsystems Inc." +__license__ = "LGPL" + +import sys, inspect +import gi +gi.require_version("Atspi", "2.0") +from gi.repository import Atspi + +import os +from gi.repository import Gdk +from gi.repository import GLib +from gi.repository import Gtk +from gi.repository import GObject +from gi.repository import Pango +import time + +currPath = os.path.dirname(os.path.realpath(os.path.abspath(inspect.getfile(inspect.currentframe())))) + +# compatibility layer, see CthulhuSetupGUI.__init__ +acss = None +guilabels = None +cthulhu_gui_profile = None +from cthulhu import cthulhu_gtkbuilder +cthulhu_platform = None +settings = None +debug = None +cthulhu = None +event_manager = None +input_event = None +cthulhu_state = None +settings_manager = None +keybindings = None +speech = None +messages = None +braille = None +speechserver = None +pronunciation_dict = None +text_attribute_names = None +tablesdir = None +_settingsManager = None +AXObject = None + +try: + import louis +except ImportError: + louis = None + + +(HANDLER, DESCRIP, MOD_MASK1, MOD_USED1, KEY1, CLICK_COUNT1, OLDTEXT1, \ + TEXT1, MODIF, EDITABLE) = list(range(10)) + +(NAME, IS_SPOKEN, IS_BRAILLED, VALUE) = list(range(4)) + +(ACTUAL, REPLACEMENT) = list(range(2)) + +# Must match the order of voice types in the GtkBuilder file. +# +(DEFAULT, UPPERCASE, HYPERLINK, SYSTEM) = list(range(4)) + +# Must match the order that the timeFormatCombo is populated. +# +(TIME_FORMAT_LOCALE, TIME_FORMAT_12_HM, TIME_FORMAT_12_HMS, TIME_FORMAT_24_HMS, + TIME_FORMAT_24_HMS_WITH_WORDS, TIME_FORMAT_24_HM, + TIME_FORMAT_24_HM_WITH_WORDS) = list(range(7)) + +# Must match the order that the dateFormatCombo is populated. +# +(DATE_FORMAT_LOCALE, DATE_FORMAT_NUMBERS_DM, DATE_FORMAT_NUMBERS_MD, + DATE_FORMAT_NUMBERS_DMY, DATE_FORMAT_NUMBERS_MDY, DATE_FORMAT_NUMBERS_YMD, + DATE_FORMAT_FULL_DM, DATE_FORMAT_FULL_MD, DATE_FORMAT_FULL_DMY, + DATE_FORMAT_FULL_MDY, DATE_FORMAT_FULL_YMD, DATE_FORMAT_ABBREVIATED_DM, + DATE_FORMAT_ABBREVIATED_MD, DATE_FORMAT_ABBREVIATED_DMY, + DATE_FORMAT_ABBREVIATED_MDY, DATE_FORMAT_ABBREVIATED_YMD) = list(range(16)) + +class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): + + def __init__(self, fileName, windowName, prefsDict, app): + """Initialize the Cthulhu configuration GUI. + + Arguments: + - fileName: name of the GtkBuilder file. + - windowName: name of the component to get from the GtkBuilder file. + - prefsDict: dictionary of preferences to use during initialization + """ + self.app = app + global acss + global guilabels + global cthulhu_gtkbuilder + global cthulhu_gui_profile + global cthulhu_platform + global settings + global debug + global cthulhu + global event_manager + global input_event + global cthulhu_state + global settings_manager + global keybindings + global speech + global messages + global braille + global speechserver + global pronunciation_dict + global text_attribute_names + global _settingsManager + global tablesdir + global louis + global AXObject + cthulhu = self.app.getDynamicApiManager().getAPI('Cthulhu') + acss = self.app.getDynamicApiManager().getAPI('Acss') + guilabels = self.app.getDynamicApiManager().getAPI('GuiLabels') + cthulhu_platform = self.app.getDynamicApiManager().getAPI('CthulhuPlatform') + settings = self.app.getDynamicApiManager().getAPI('Settings') + debug= self.app.getDynamicApiManager().getAPI('Debug') + input_event = self.app.getDynamicApiManager().getAPI('InputEvent') + cthulhu_state = self.app.getDynamicApiManager().getAPI('CthulhuState') + settings_manager = self.app.getDynamicApiManager().getAPI('SettingsManager') + keybindings = self.app.getDynamicApiManager().getAPI('Keybindings') + speech = self.app.getDynamicApiManager().getAPI('Speech') + messages = self.app.getDynamicApiManager().getAPI('Messages') + braille = self.app.getDynamicApiManager().getAPI('Braille') + speechserver = self.app.getDynamicApiManager().getAPI('SpeechServer') + pronunciation_dict = self.app.getDynamicApiManager().getAPI('PronunciationDict') + text_attribute_names = self.app.getDynamicApiManager().getAPI('TextAttributeNames') + cthulhu_gui_profile = self.app.getAPIHelper().importModule('cthulhu_gui_profile', currPath + '/cthulhu_gui_profile.py') + AXObject = self.app.getDynamicApiManager().getAPI('AXObject') + event_manager = self.app.getDynamicApiManager().getAPI('EventManager') + try: + tablesdir = cthulhu_platform.tablesdir + except: + pass + + if louis and not tablesdir: + louis = None + + _settingsManager = settings_manager.getManager() + cthulhu_gtkbuilder.GtkBuilderWrapper.__init__(self, fileName, windowName) + self.prefsDict = prefsDict + + self._defaultProfile = ['Default', 'default'] + self.bbindings = None + self.cellRendererText = None + self.defaultVoice = None + self.disableKeyGrabPref = None + self.getTextAttributesView = None + self.hyperlinkVoice = None + self.initializingSpeech = None + self.kbindings = None + self.keyBindingsModel = None + self.keyBindView = None + self.newBinding = None + self.pendingKeyBindings = None + self.planeCellRendererText = None + self.pronunciationModel = None + self.pronunciationView = None + self.screenHeight = None + self.screenWidth = None + self.speechFamiliesChoice = None + self.speechFamiliesChoices = None + self.speechFamiliesModel = None + self.speechLanguagesChoice = None + self.speechLanguagesChoices = None + self.speechLanguagesModel = None + self.speechFamilies = [] + self.speechServersChoice = None + self.speechServersChoices = None + self.speechServersModel = None + self.speechSystemsChoice = None + self.speechSystemsChoices = None + self.speechSystemsModel = None + self.systemVoice = None + self.uppercaseVoice = None + self.window = None + self.workingFactories = None + self.savedGain = None + self.savedPitch = None + self.savedRate = None + self._isInitialSetup = False + self.selectedFamilyChoices = {} + self.selectedLanguageChoices = {} + self.profilesCombo = None + self.profilesComboModel = None + self.startingProfileCombo = None + self._capturedKey = [] + self.script = None + self.translationContext = None + + def setTranslationContext(self, translationContext): + self.translationContext = translationContext + global _, cthulhu_gui_profile + _ = translationContext.gettext + if cthulhu_gui_profile != None: + cthulhu_gui_profile.setTranslationContext(translationContext) + def init(self, script): + """Initialize the Cthulhu configuration GUI. Read the users current + set of preferences and set the GUI state to match. Setup speech + support and populate the combo box lists on the Speech Tab pane + accordingly. + """ + + self.script = script + + # Restore the default rate/pitch/gain, + # in case the user played with the sliders. + # + try: + voices = _settingsManager.getSetting('voices') + defaultVoice = voices[settings.DEFAULT_VOICE] + except KeyError: + defaultVoice = {} + try: + self.savedGain = defaultVoice[acss.ACSS.GAIN] + except KeyError: + self.savedGain = 10.0 + try: + self.savedPitch = defaultVoice[acss.ACSS.AVERAGE_PITCH] + except KeyError: + self.savedPitch = 5.0 + try: + self.savedRate = defaultVoice[acss.ACSS.RATE] + except KeyError: + self.savedRate = 50.0 + + # ***** Key Bindings treeview initialization ***** + + self.keyBindView = self.get_widget("keyBindingsTreeview") + + if self.keyBindView.get_columns(): + for column in self.keyBindView.get_columns(): + self.keyBindView.remove_column(column) + + self.keyBindingsModel = Gtk.TreeStore( + GObject.TYPE_STRING, # Handler name + GObject.TYPE_STRING, # Human Readable Description + GObject.TYPE_STRING, # Modifier mask 1 + GObject.TYPE_STRING, # Used Modifiers 1 + GObject.TYPE_STRING, # Modifier key name 1 + GObject.TYPE_STRING, # Click count 1 + GObject.TYPE_STRING, # Original Text of the Key Binding Shown 1 + GObject.TYPE_STRING, # Text of the Key Binding Shown 1 + GObject.TYPE_BOOLEAN, # Key Modified by User + GObject.TYPE_BOOLEAN) # Row with fields editable or not + + self.planeCellRendererText = Gtk.CellRendererText() + + self.cellRendererText = Gtk.CellRendererText() + self.cellRendererText.set_property("ellipsize", Pango.EllipsizeMode.END) + + # HANDLER - invisble column + # + column = Gtk.TreeViewColumn("Handler", + self.planeCellRendererText, + text=HANDLER) + column.set_resizable(True) + column.set_visible(False) + column.set_sort_column_id(HANDLER) + self.keyBindView.append_column(column) + + # DESCRIP + # + column = Gtk.TreeViewColumn(guilabels.KB_HEADER_FUNCTION, + self.cellRendererText, + text=DESCRIP) + column.set_resizable(True) + column.set_min_width(380) + column.set_sort_column_id(DESCRIP) + self.keyBindView.append_column(column) + + # MOD_MASK1 - invisble column + # + column = Gtk.TreeViewColumn("Mod.Mask 1", + self.planeCellRendererText, + text=MOD_MASK1) + column.set_visible(False) + column.set_resizable(True) + column.set_sort_column_id(MOD_MASK1) + self.keyBindView.append_column(column) + + # MOD_USED1 - invisble column + # + column = Gtk.TreeViewColumn("Use Mod.1", + self.planeCellRendererText, + text=MOD_USED1) + column.set_visible(False) + column.set_resizable(True) + column.set_sort_column_id(MOD_USED1) + self.keyBindView.append_column(column) + + # KEY1 - invisble column + # + column = Gtk.TreeViewColumn("Key1", + self.planeCellRendererText, + text=KEY1) + column.set_resizable(True) + column.set_visible(False) + column.set_sort_column_id(KEY1) + self.keyBindView.append_column(column) + + # CLICK_COUNT1 - invisble column + # + column = Gtk.TreeViewColumn("ClickCount1", + self.planeCellRendererText, + text=CLICK_COUNT1) + column.set_resizable(True) + column.set_visible(False) + column.set_sort_column_id(CLICK_COUNT1) + self.keyBindView.append_column(column) + + # OLDTEXT1 - invisble column which will store a copy of the + # original keybinding in TEXT1 prior to the Apply or OK + # buttons being pressed. This will prevent automatic + # resorting each time a cell is edited. + # + column = Gtk.TreeViewColumn("OldText1", + self.planeCellRendererText, + text=OLDTEXT1) + column.set_resizable(True) + column.set_visible(False) + column.set_sort_column_id(OLDTEXT1) + self.keyBindView.append_column(column) + + # TEXT1 + # + rendererText = Gtk.CellRendererText() + rendererText.connect("editing-started", + self.editingKey, + self.keyBindingsModel) + rendererText.connect("editing-canceled", + self.editingCanceledKey) + rendererText.connect('edited', + self.editedKey, + self.keyBindingsModel, + MOD_MASK1, MOD_USED1, KEY1, CLICK_COUNT1, TEXT1) + + column = Gtk.TreeViewColumn(guilabels.KB_HEADER_KEY_BINDING, + rendererText, + text=TEXT1, + editable=EDITABLE) + + column.set_resizable(True) + column.set_sort_column_id(OLDTEXT1) + self.keyBindView.append_column(column) + + # MODIF + # + rendererToggle = Gtk.CellRendererToggle() + rendererToggle.connect('toggled', + self.keyModifiedToggle, + self.keyBindingsModel, + MODIF) + column = Gtk.TreeViewColumn(guilabels.KB_MODIFIED, + rendererToggle, + active=MODIF, + activatable=EDITABLE) + #column.set_visible(False) + column.set_resizable(True) + column.set_sort_column_id(MODIF) + self.keyBindView.append_column(column) + + # EDITABLE - invisble column + # + rendererToggle = Gtk.CellRendererToggle() + rendererToggle.set_property('activatable', False) + column = Gtk.TreeViewColumn("Modified", + rendererToggle, + active=EDITABLE) + column.set_visible(False) + column.set_resizable(True) + column.set_sort_column_id(EDITABLE) + self.keyBindView.append_column(column) + + # Populates the treeview with all the keybindings: + # + self._populateKeyBindings() + + self.window = self.get_widget("cthulhuSetupWindow") + + self._setKeyEchoItems() + + self.speechSystemsModel = \ + self._initComboBox(self.get_widget("speechSystems")) + self.speechServersModel = \ + self._initComboBox(self.get_widget("speechServers")) + self.speechLanguagesModel = \ + self._initComboBox(self.get_widget("speechLanguages")) + self.speechFamiliesModel = \ + self._initComboBox(self.get_widget("speechFamilies")) + self._initSpeechState() + + # TODO - JD: Will this ever be the case?? + self._isInitialSetup = \ + not os.path.exists(_settingsManager.getPrefsDir()) + + appPage = self.script.getAppPreferencesGUI() + if appPage: + label = Gtk.Label(label=AXObject.get_name(self.script.app)) + self.get_widget("notebook").append_page(appPage, label) + + self._initGUIState() + + def _getACSSForVoiceType(self, voiceType): + """Return the ACSS value for the given voice type. + + Arguments: + - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM + + Returns the voice dictionary for the given voice type. + """ + + if voiceType == DEFAULT: + voiceACSS = self.defaultVoice + elif voiceType == UPPERCASE: + voiceACSS = self.uppercaseVoice + elif voiceType == HYPERLINK: + voiceACSS = self.hyperlinkVoice + elif voiceType == SYSTEM: + voiceACSS = self.systemVoice + else: + voiceACSS = self.defaultVoice + + return voiceACSS + + def writeUserPreferences(self): + """Write out the user's generic Cthulhu preferences. + """ + pronunciationDict = self.getModelDict(self.pronunciationModel) + keyBindingsDict = self.getKeyBindingsModelDict(self.keyBindingsModel) + self.prefsDict.update(self.script.getPreferencesFromGUI()) + _settingsManager.saveSettings(self.script, + self.prefsDict, + pronunciationDict, + keyBindingsDict) + + def _getKeyValueForVoiceType(self, voiceType, key, useDefault=True): + """Look for the value of the given key in the voice dictionary + for the given voice type. + + Arguments: + - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM + - key: the key to look for in the voice dictionary. + - useDefault: if True, and the key isn't found for the given voice + type, the look for it in the default voice dictionary + as well. + + Returns the value of the given key, or None if it's not set. + """ + + if voiceType == DEFAULT: + voice = self.defaultVoice + elif voiceType == UPPERCASE: + voice = self.uppercaseVoice + if key not in voice: + if not useDefault: + return None + voice = self.defaultVoice + elif voiceType == HYPERLINK: + voice = self.hyperlinkVoice + if key not in voice: + if not useDefault: + return None + voice = self.defaultVoice + elif voiceType == SYSTEM: + voice = self.systemVoice + if key not in voice: + if not useDefault: + return None + voice = self.defaultVoice + else: + voice = self.defaultVoice + + if key in voice: + return voice[key] + else: + return None + + def _getFamilyNameForVoiceType(self, voiceType): + """Gets the name of the voice family for the given voice type. + + Arguments: + - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM + + Returns the name of the voice family for the given voice type, + or None if not set. + """ + + familyName = None + family = self._getKeyValueForVoiceType(voiceType, acss.ACSS.FAMILY) + + if family and speechserver.VoiceFamily.NAME in family: + familyName = family[speechserver.VoiceFamily.NAME] + + return familyName + + def _setFamilyNameForVoiceType(self, voiceType, name, language, dialect, variant): + """Sets the name of the voice family for the given voice type. + + Arguments: + - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM + - name: the name of the voice family to set. + - language: the locale of the voice family to set. + - dialect: the dialect of the voice family to set. + """ + + family = self._getKeyValueForVoiceType(voiceType, + acss.ACSS.FAMILY, + False) + + voiceACSS = self._getACSSForVoiceType(voiceType) + if family: + family[speechserver.VoiceFamily.NAME] = name + family[speechserver.VoiceFamily.LANG] = language + family[speechserver.VoiceFamily.DIALECT] = dialect + family[speechserver.VoiceFamily.VARIANT] = variant + else: + voiceACSS[acss.ACSS.FAMILY] = {} + voiceACSS[acss.ACSS.FAMILY][speechserver.VoiceFamily.NAME] = name + voiceACSS[acss.ACSS.FAMILY][speechserver.VoiceFamily.LANG] = language + voiceACSS[acss.ACSS.FAMILY][speechserver.VoiceFamily.DIALECT] = dialect + voiceACSS[acss.ACSS.FAMILY][speechserver.VoiceFamily.VARIANT] = variant + voiceACSS['established'] = True + + #settings.voices[voiceType] = voiceACSS + + def _getRateForVoiceType(self, voiceType): + """Gets the speaking rate value for the given voice type. + + Arguments: + - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM + + Returns the rate value for the given voice type, or None if + not set. + """ + + return self._getKeyValueForVoiceType(voiceType, acss.ACSS.RATE) + + def _setRateForVoiceType(self, voiceType, value): + """Sets the speaking rate value for the given voice type. + + Arguments: + - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM + - value: the rate value to set. + """ + + voiceACSS = self._getACSSForVoiceType(voiceType) + voiceACSS[acss.ACSS.RATE] = value + voiceACSS['established'] = True + #settings.voices[voiceType] = voiceACSS + + def _getPitchForVoiceType(self, voiceType): + """Gets the pitch value for the given voice type. + + Arguments: + - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM + + Returns the pitch value for the given voice type, or None if + not set. + """ + + return self._getKeyValueForVoiceType(voiceType, + acss.ACSS.AVERAGE_PITCH) + + def _setPitchForVoiceType(self, voiceType, value): + """Sets the pitch value for the given voice type. + + Arguments: + - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM + - value: the pitch value to set. + """ + + voiceACSS = self._getACSSForVoiceType(voiceType) + voiceACSS[acss.ACSS.AVERAGE_PITCH] = value + voiceACSS['established'] = True + #settings.voices[voiceType] = voiceACSS + + def _getVolumeForVoiceType(self, voiceType): + """Gets the volume (gain) value for the given voice type. + + Arguments: + - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM + + Returns the volume (gain) value for the given voice type, or + None if not set. + """ + + return self._getKeyValueForVoiceType(voiceType, acss.ACSS.GAIN) + + def _setVolumeForVoiceType(self, voiceType, value): + """Sets the volume (gain) value for the given voice type. + + Arguments: + - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM + - value: the volume (gain) value to set. + """ + + voiceACSS = self._getACSSForVoiceType(voiceType) + voiceACSS[acss.ACSS.GAIN] = value + voiceACSS['established'] = True + #settings.voices[voiceType] = voiceACSS + + def _setVoiceSettingsForVoiceType(self, voiceType): + """Sets the family, rate, pitch and volume GUI components based + on the given voice type. + + Arguments: + - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM + """ + + familyName = self._getFamilyNameForVoiceType(voiceType) + self._setSpeechFamiliesChoice(familyName) + + rate = self._getRateForVoiceType(voiceType) + if rate is not None: + self.get_widget("rateScale").set_value(rate) + else: + self.get_widget("rateScale").set_value(50.0) + + pitch = self._getPitchForVoiceType(voiceType) + if pitch is not None: + self.get_widget("pitchScale").set_value(pitch) + else: + self.get_widget("pitchScale").set_value(5.0) + + volume = self._getVolumeForVoiceType(voiceType) + if volume is not None: + self.get_widget("volumeScale").set_value(volume) + else: + self.get_widget("volumeScale").set_value(10.0) + + def _setSpeechFamiliesChoice(self, familyName): + """Sets the active item in the families ("Person:") combo box + to the given family name. + + Arguments: + - familyName: the family name to use to set the active combo box item. + """ + + if len(self.speechFamilies) == 0: + return + + languageSet = False + familySet = False + for family in self.speechFamilies: + name = family[speechserver.VoiceFamily.NAME] + if name == familyName: + lang = family[speechserver.VoiceFamily.LANG] + dialect = family[speechserver.VoiceFamily.DIALECT] + + if dialect: + language = lang + '-' + dialect + else: + language = lang + + i = 0 + for languageChoice in self.speechLanguagesChoices: + if languageChoice == language: + self.get_widget("speechLanguages").set_active(i) + self.speechLanguagesChoice = self.speechLanguagesChoices[i] + languageSet = True + + self._setupFamilies() + + i = 0 + for familyChoice in self.speechFamiliesChoices: + name = familyChoice[speechserver.VoiceFamily.NAME] + if name == familyName: + self.get_widget("speechFamilies").set_active(i) + self.speechFamiliesChoice = self.speechFamiliesChoices[i] + familySet = True + break + i += 1 + + break + + i += 1 + + break + + if not languageSet: + debug.println(debug.LEVEL_FINEST, + f"Could not find speech language match for {familyName}") + self.get_widget("speechLanguages").set_active(0) + self.speechLanguagesChoice = self.speechLanguagesChoices[0] + + if languageSet: + self.selectedLanguageChoices[self.speechServersChoice] = i + + if not familySet: + debug.println(debug.LEVEL_FINEST, + f"Could not find speech family match for {familyName}") + self.get_widget("speechFamilies").set_active(0) + self.speechFamiliesChoice = self.speechFamiliesChoices[0] + + if familySet: + self.selectedFamilyChoices[self.speechServersChoice, + self.speechLanguagesChoice] = i + + def _setupFamilies(self): + """Gets the list of voice variants for the current speech server and + current language. + If there are variants, get the information associated with + each voice variant and add an entry for it to the variants + GtkComboBox list. + """ + + combobox = self.get_widget("speechFamilies") + combobox.set_model(None) + self.speechFamiliesModel.clear() + + currentLanguage = self.speechLanguagesChoice + + i = 0 + self.speechFamiliesChoices = [] + for family in self.speechFamilies: + lang = family[speechserver.VoiceFamily.LANG] + dialect = family[speechserver.VoiceFamily.DIALECT] + + if dialect: + language = lang + '-' + dialect + else: + language = lang + + if language != currentLanguage: + continue + + name = family[speechserver.VoiceFamily.NAME] + self.speechFamiliesChoices.append(family) + self.speechFamiliesModel.append((i, name)) + i += 1 + + combobox.set_model(self.speechFamiliesModel) + if i == 0: + msg = f"No speech family was available for {str(currentLanguage)}." + debug.println(debug.LEVEL_SEVERE, msg, True) + debug.printStack(debug.LEVEL_FINEST) + self.speechFamiliesChoice = None + return + + # If user manually selected a family for the current speech server + # this choice it's restored. In other case the first family + # (usually the default one) is selected + # + selectedIndex = 0 + if (self.speechServersChoice, self.speechLanguagesChoice) \ + in self.selectedFamilyChoices: + selectedIndex = self.selectedFamilyChoices[self.speechServersChoice, + self.speechLanguagesChoice] + + self.get_widget("speechFamilies").set_active(selectedIndex) + + def _setSpeechLanguagesChoice(self, languageName): + """Sets the active item in the languages ("Language:") combo box + to the given language name. + + Arguments: + - languageName: the language name to use to set the active combo box item. + """ + + if len(self.speechLanguagesChoices) == 0: + return + + valueSet = False + i = 0 + for language in self.speechLanguagesChoices: + if language == languageName: + self.get_widget("speechLanguages").set_active(i) + self.speechLanguagesChoice = self.speechLanguagesChoices[i] + valueSet = True + break + i += 1 + + if not valueSet: + debug.println(debug.LEVEL_FINEST, + f"Could not find speech language match for {languageName}") + self.get_widget("speechLanguages").set_active(0) + self.speechLanguagesChoice = self.speechLanguagesChoices[0] + + if valueSet: + self.selectedLanguageChoices[self.speechServersChoice] = i + + self._setupFamilies() + + def _setupVoices(self): + """Gets the list of voices for the current speech server. + If there are families, get the information associated with + each voice family and add an entry for it to the families + GtkComboBox list. + """ + + combobox = self.get_widget("speechLanguages") + combobox.set_model(None) + self.speechLanguagesModel.clear() + self.speechFamilies = self.speechServersChoice.getVoiceFamilies() + self.speechLanguagesChoices = [] + + if len(self.speechFamilies) == 0: + debug.println(debug.LEVEL_SEVERE, "No speech voice was available.") + debug.printStack(debug.LEVEL_FINEST) + self.speechLanguagesChoice = None + return + + done = {} + i = 0 + for family in self.speechFamilies: + lang = family[speechserver.VoiceFamily.LANG] + dialect = family[speechserver.VoiceFamily.DIALECT] + if (lang,dialect) in done: + continue + done[lang,dialect] = True + + if dialect: + language = lang + '-' + dialect + else: + language = lang + + # TODO: get translated language name from CLDR or such + msg = language + if msg == "": + # Unsupported locale + msg = "default language" + + self.speechLanguagesChoices.append(language) + self.speechLanguagesModel.append((i, msg)) + i += 1 + + # If user manually selected a language for the current speech server + # this choice it's restored. In other case the first language + # (usually the default one) is selected + # + selectedIndex = 0 + if self.speechServersChoice in self.selectedLanguageChoices: + selectedIndex = self.selectedLanguageChoices[self.speechServersChoice] + + combobox.set_model(self.speechLanguagesModel) + + self.get_widget("speechLanguages").set_active(selectedIndex) + if self.initializingSpeech: + self.speechLanguagesChoice = self.speechLanguagesChoices[selectedIndex] + + self._setupFamilies() + + # The family name will be selected as part of selecting the + # voice type. Whenever the families change, we'll reset the + # voice type selection to the first one ("Default"). + # + comboBox = self.get_widget("voiceTypesCombo") + types = [guilabels.SPEECH_VOICE_TYPE_DEFAULT, + guilabels.SPEECH_VOICE_TYPE_UPPERCASE, + guilabels.SPEECH_VOICE_TYPE_HYPERLINK, + guilabels.SPEECH_VOICE_TYPE_SYSTEM] + self.populateComboBox(comboBox, types) + comboBox.set_active(DEFAULT) + voiceType = comboBox.get_active() + self._setVoiceSettingsForVoiceType(voiceType) + + def _setSpeechServersChoice(self, serverInfo): + """Sets the active item in the speech servers combo box to the + given server. + + Arguments: + - serversChoices: the list of available speech servers. + - serverInfo: the speech server to use to set the active combo + box item. + """ + + if len(self.speechServersChoices) == 0: + return + + # We'll fallback to whatever we happen to be using in the event + # that this preference has never been set. + # + if not serverInfo: + serverInfo = speech.getInfo() + + valueSet = False + i = 0 + for server in self.speechServersChoices: + if serverInfo == server.getInfo(): + self.get_widget("speechServers").set_active(i) + self.speechServersChoice = server + valueSet = True + break + i += 1 + + if not valueSet: + debug.println(debug.LEVEL_FINEST, + f"Could not find speech server match for {repr(serverInfo)}") + self.get_widget("speechServers").set_active(0) + self.speechServersChoice = self.speechServersChoices[0] + + self._setupVoices() + + def _setupSpeechServers(self): + """Gets the list of speech servers for the current speech factory. + If there are servers, get the information associated with each + speech server and add an entry for it to the speechServers + GtkComboBox list. Set the current choice to be the first item. + """ + + combobox = self.get_widget("speechServers") + combobox.set_model(None) + self.speechServersModel.clear() + self.speechServersChoices = \ + self.speechSystemsChoice.SpeechServer.getSpeechServers() + if len(self.speechServersChoices) == 0: + debug.println(debug.LEVEL_SEVERE, "Speech not available.") + debug.printStack(debug.LEVEL_FINEST) + self.speechServersChoice = None + self.speechLanguagesChoices = [] + self.speechLanguagesChoice = None + self.speechFamiliesChoices = [] + self.speechFamiliesChoice = None + combobox.set_model(self.speechServersModel) + return + + i = 0 + for server in self.speechServersChoices: + name = server.getInfo()[0] + self.speechServersModel.append((i, name)) + i += 1 + + combobox.set_model(self.speechServersModel) + self._setSpeechServersChoice(self.prefsDict["speechServerInfo"]) + + def _setSpeechSystemsChoice(self, systemName): + """Set the active item in the speech systems combo box to the + given system name. + + Arguments: + - factoryChoices: the list of available speech factories (systems). + - systemName: the speech system name to use to set the active combo + box item. + """ + + systemName = systemName.strip("'") + + if len(self.speechSystemsChoices) == 0: + self.speechSystemsChoice = None + return + + valueSet = False + i = 0 + for speechSystem in self.speechSystemsChoices: + name = speechSystem.__name__ + if name.endswith(systemName): + self.get_widget("speechSystems").set_active(i) + self.speechSystemsChoice = self.speechSystemsChoices[i] + valueSet = True + break + i += 1 + + if not valueSet: + debug.println(debug.LEVEL_FINEST, + f"Could not find speech system match for {systemName}") + self.get_widget("speechSystems").set_active(0) + self.speechSystemsChoice = self.speechSystemsChoices[0] + + self._setupSpeechServers() + + def _setupSpeechSystems(self, factories): + """Sets up the speech systems combo box and sets the selection + to the preferred speech system. + + Arguments: + -factories: the list of known speech factories (working or not) + """ + + combobox = self.get_widget("speechSystems") + combobox.set_model(None) + self.speechSystemsModel.clear() + self.workingFactories = [] + for factory in factories: + try: + servers = factory.SpeechServer.getSpeechServers() + if len(servers): + self.workingFactories.append(factory) + except Exception: + debug.printException(debug.LEVEL_FINEST) + + self.speechSystemsChoices = [] + if len(self.workingFactories) == 0: + debug.println(debug.LEVEL_SEVERE, "Speech not available.") + debug.printStack(debug.LEVEL_FINEST) + self.speechSystemsChoice = None + self.speechServersChoices = [] + self.speechServersChoice = None + self.speechLanguagesChoices = [] + self.speechLanguagesChoice = None + self.speechFamiliesChoices = [] + self.speechFamiliesChoice = None + combobox.set_model(self.speechSystemsModel) + return + + i = 0 + for workingFactory in self.workingFactories: + self.speechSystemsChoices.append(workingFactory) + name = workingFactory.SpeechServer.getFactoryName() + self.speechSystemsModel.append((i, name)) + i += 1 + + combobox.set_model(self.speechSystemsModel) + if self.prefsDict["speechServerFactory"]: + self._setSpeechSystemsChoice(self.prefsDict["speechServerFactory"]) + else: + self.speechSystemsChoice = None + + debug.println( + debug.LEVEL_FINEST, + f"cthulhu_gui_prefs._setupSpeechSystems: speechSystemsChoice: {self.speechSystemsChoice}") + + def _initSpeechState(self): + """Initialize the various speech components. + """ + + voices = self.prefsDict["voices"] + self.defaultVoice = acss.ACSS(voices.get(settings.DEFAULT_VOICE)) + self.uppercaseVoice = acss.ACSS(voices.get(settings.UPPERCASE_VOICE)) + self.hyperlinkVoice = acss.ACSS(voices.get(settings.HYPERLINK_VOICE)) + self.systemVoice = acss.ACSS(voices.get(settings.SYSTEM_VOICE)) + + # Just a note on general naming pattern: + # + # * = The name of the combobox + # *Model = the name of the comobox model + # *Choices = the Cthulhu/speech python objects + # *Choice = a value from *Choices + # + # Where * = speechSystems, speechServers, speechLanguages, speechFamilies + # + factories = _settingsManager.getSpeechServerFactories() + if len(factories) == 0 or not self.prefsDict.get('enableSpeech', True): + self.workingFactories = [] + self.speechSystemsChoice = None + self.speechServersChoices = [] + self.speechServersChoice = None + self.speechLanguagesChoices = [] + self.speechLanguagesChoice = None + self.speechFamiliesChoices = [] + self.speechFamiliesChoice = None + return + + try: + speech.init() + except Exception: + self.workingFactories = [] + self.speechSystemsChoice = None + self.speechServersChoices = [] + self.speechServersChoice = None + self.speechLanguagesChoices = [] + self.speechLanguagesChoice = None + self.speechFamiliesChoices = [] + self.speechFamiliesChoice = None + return + + # This cascades into systems->servers->voice_type->families... + # + self.initializingSpeech = True + self._setupSpeechSystems(factories) + self.initializingSpeech = False + + def _setSpokenTextAttributes(self, view, setAttributes, + state, moveToTop=False): + """Given a set of spoken text attributes, update the model used by the + text attribute tree view. + + Arguments: + - view: the text attribute tree view. + - setAttributes: the list of spoken text attributes to update. + - state: the state (True or False) that they all should be set to. + - moveToTop: if True, move these attributes to the top of the list. + """ + + model = view.get_model() + view.set_model(None) + + [attrList, attrDict] = \ + self.script.utilities.stringToKeysAndDict(setAttributes) + [allAttrList, allAttrDict] = self.script.utilities.stringToKeysAndDict( + _settingsManager.getSetting('allTextAttributes')) + + for i in range(0, len(attrList)): + for path in range(0, len(allAttrList)): + localizedKey = text_attribute_names.getTextAttributeName( + attrList[i], self.script) + localizedValue = text_attribute_names.getTextAttributeName( + attrDict[attrList[i]], self.script) + if localizedKey == model[path][NAME]: + thisIter = model.get_iter(path) + model.set_value(thisIter, NAME, localizedKey) + model.set_value(thisIter, IS_SPOKEN, state) + model.set_value(thisIter, VALUE, localizedValue) + if moveToTop: + thisIter = model.get_iter(path) + otherIter = model.get_iter(i) + model.move_before(thisIter, otherIter) + break + + view.set_model(model) + + def _setBrailledTextAttributes(self, view, setAttributes, state): + """Given a set of brailled text attributes, update the model used + by the text attribute tree view. + + Arguments: + - view: the text attribute tree view. + - setAttributes: the list of brailled text attributes to update. + - state: the state (True or False) that they all should be set to. + """ + + model = view.get_model() + view.set_model(None) + + [attrList, attrDict] = \ + self.script.utilities.stringToKeysAndDict(setAttributes) + [allAttrList, allAttrDict] = self.script.utilities.stringToKeysAndDict( + _settingsManager.getSetting('allTextAttributes')) + + for i in range(0, len(attrList)): + for path in range(0, len(allAttrList)): + localizedKey = text_attribute_names.getTextAttributeName( + attrList[i], self.script) + if localizedKey == model[path][NAME]: + thisIter = model.get_iter(path) + model.set_value(thisIter, IS_BRAILLED, state) + break + + view.set_model(model) + + def _getAppNameForAttribute(self, attributeName): + """Converts the given Atk attribute name into the application's + equivalent. This is necessary because an application or toolkit + (e.g. Gecko) might invent entirely new names for the same text + attributes. + + Arguments: + - attribName: The name of the text attribute + + Returns the application's equivalent name if found or attribName + otherwise. + """ + + return self.script.utilities.getAppNameForAttribute(attributeName) + + def _updateTextDictEntry(self): + """The user has updated the text attribute list in some way. Update + the "enabledSpokenTextAttributes" and "enabledBrailledTextAttributes" + preference strings to reflect the current state of the corresponding + text attribute lists. + """ + + model = self.getTextAttributesView.get_model() + spokenAttrStr = "" + brailledAttrStr = "" + noRows = model.iter_n_children(None) + for path in range(0, noRows): + localizedKey = model[path][NAME] + key = text_attribute_names.getTextAttributeKey(localizedKey) + + # Convert the normalized, Atk attribute name back into what + # the app/toolkit uses. + # + key = self._getAppNameForAttribute(key) + + localizedValue = model[path][VALUE] + value = text_attribute_names.getTextAttributeKey(localizedValue) + + if model[path][IS_SPOKEN]: + spokenAttrStr += key + ":" + value + "; " + if model[path][IS_BRAILLED]: + brailledAttrStr += key + ":" + value + "; " + + self.prefsDict["enabledSpokenTextAttributes"] = spokenAttrStr + self.prefsDict["enabledBrailledTextAttributes"] = brailledAttrStr + + def contractedBrailleToggled(self, checkbox): + grid = self.get_widget('contractionTableGrid') + grid.set_sensitive(checkbox.get_active()) + self.prefsDict["enableContractedBraille"] = checkbox.get_active() + + def contractionTableComboChanged(self, combobox): + model = combobox.get_model() + myIter = combobox.get_active_iter() + self.prefsDict["brailleContractionTable"] = model[myIter][1] + + def flashPersistenceToggled(self, checkbox): + grid = self.get_widget('flashMessageDurationGrid') + grid.set_sensitive(not checkbox.get_active()) + self.prefsDict["flashIsPersistent"] = checkbox.get_active() + + def textAttributeSpokenToggled(self, cell, path, model): + """The user has toggled the state of one of the text attribute + checkboxes to be spoken. Update our model to reflect this, then + update the "enabledSpokenTextAttributes" preference string. + + Arguments: + - cell: the cell that changed. + - path: the path of that cell. + - model: the model that the cell is part of. + """ + + thisIter = model.get_iter(path) + model.set(thisIter, IS_SPOKEN, not model[path][IS_SPOKEN]) + self._updateTextDictEntry() + + def textAttributeBrailledToggled(self, cell, path, model): + """The user has toggled the state of one of the text attribute + checkboxes to be brailled. Update our model to reflect this, + then update the "enabledBrailledTextAttributes" preference string. + + Arguments: + - cell: the cell that changed. + - path: the path of that cell. + - model: the model that the cell is part of. + """ + + thisIter = model.get_iter(path) + model.set(thisIter, IS_BRAILLED, not model[path][IS_BRAILLED]) + self._updateTextDictEntry() + + def textAttrValueEdited(self, cell, path, new_text, model): + """The user has edited the value of one of the text attributes. + Update our model to reflect this, then update the + "enabledSpokenTextAttributes" and "enabledBrailledTextAttributes" + preference strings. + + Arguments: + - cell: the cell that changed. + - path: the path of that cell. + - new_text: the new text attribute value string. + - model: the model that the cell is part of. + """ + + thisIter = model.get_iter(path) + model.set(thisIter, VALUE, new_text) + self._updateTextDictEntry() + + def textAttrCursorChanged(self, widget): + """Set the search column in the text attribute tree view + depending upon which column the user currently has the cursor in. + """ + + [path, focusColumn] = self.getTextAttributesView.get_cursor() + if focusColumn: + noColumns = len(self.getTextAttributesView.get_columns()) + for i in range(0, noColumns): + col = self.getTextAttributesView.get_column(i) + if focusColumn == col: + self.getTextAttributesView.set_search_column(i) + break + + def _createTextAttributesTreeView(self): + """Create the text attributes tree view. The view is the + textAttributesTreeView GtkTreeView widget. The view will consist + of a list containing three columns: + IS_SPOKEN - a checkbox whose state indicates whether this text + attribute will be spoken or not. + NAME - the text attribute name. + VALUE - if set, (and this attributes is enabled for speaking), + then this attribute will be spoken unless it equals + this value. + """ + + self.getTextAttributesView = self.get_widget("textAttributesTreeView") + + if self.getTextAttributesView.get_columns(): + for column in self.getTextAttributesView.get_columns(): + self.getTextAttributesView.remove_column(column) + + model = Gtk.ListStore(GObject.TYPE_STRING, + GObject.TYPE_BOOLEAN, + GObject.TYPE_BOOLEAN, + GObject.TYPE_STRING) + + # Initially setup the list store model based on the values of all + # the known text attributes. + # + [allAttrList, allAttrDict] = self.script.utilities.stringToKeysAndDict( + _settingsManager.getSetting('allTextAttributes')) + for i in range(0, len(allAttrList)): + thisIter = model.append() + localizedKey = text_attribute_names.getTextAttributeName( + allAttrList[i], self.script) + localizedValue = text_attribute_names.getTextAttributeName( + allAttrDict[allAttrList[i]], self.script) + model.set_value(thisIter, NAME, localizedKey) + model.set_value(thisIter, IS_SPOKEN, False) + model.set_value(thisIter, IS_BRAILLED, False) + model.set_value(thisIter, VALUE, localizedValue) + + self.getTextAttributesView.set_model(model) + + # Attribute Name column (NAME). + column = Gtk.TreeViewColumn(guilabels.TEXT_ATTRIBUTE_NAME) + column.set_min_width(250) + column.set_resizable(True) + renderer = Gtk.CellRendererText() + column.pack_end(renderer, True) + column.add_attribute(renderer, 'text', NAME) + self.getTextAttributesView.insert_column(column, 0) + + # Attribute Speak column (IS_SPOKEN). + speakAttrColumnLabel = guilabels.PRESENTATION_SPEAK + column = Gtk.TreeViewColumn(speakAttrColumnLabel) + renderer = Gtk.CellRendererToggle() + column.pack_start(renderer, False) + column.add_attribute(renderer, 'active', IS_SPOKEN) + renderer.connect("toggled", + self.textAttributeSpokenToggled, + model) + self.getTextAttributesView.insert_column(column, 1) + column.clicked() + + # Attribute Mark in Braille column (IS_BRAILLED). + markAttrColumnLabel = guilabels.PRESENTATION_MARK_IN_BRAILLE + column = Gtk.TreeViewColumn(markAttrColumnLabel) + renderer = Gtk.CellRendererToggle() + column.pack_start(renderer, False) + column.add_attribute(renderer, 'active', IS_BRAILLED) + renderer.connect("toggled", + self.textAttributeBrailledToggled, + model) + self.getTextAttributesView.insert_column(column, 2) + column.clicked() + + # Attribute Value column (VALUE) + column = Gtk.TreeViewColumn(guilabels.PRESENTATION_PRESENT_UNLESS) + renderer = Gtk.CellRendererText() + renderer.set_property('editable', True) + column.pack_end(renderer, True) + column.add_attribute(renderer, 'text', VALUE) + renderer.connect("edited", self.textAttrValueEdited, model) + + self.getTextAttributesView.insert_column(column, 4) + + # Check all the enabled (spoken) text attributes. + # + self._setSpokenTextAttributes( + self.getTextAttributesView, + _settingsManager.getSetting('enabledSpokenTextAttributes'), + True, True) + + # Check all the enabled (brailled) text attributes. + # + self._setBrailledTextAttributes( + self.getTextAttributesView, + _settingsManager.getSetting('enabledBrailledTextAttributes'), + True) + + # Connect a handler for when the user changes columns within the + # view, so that we can adjust the search column for item lookups. + # + self.getTextAttributesView.connect("cursor_changed", + self.textAttrCursorChanged) + + def pronActualValueEdited(self, cell, path, new_text, model): + """The user has edited the value of one of the actual strings in + the pronunciation dictionary. Update our model to reflect this. + + Arguments: + - cell: the cell that changed. + - path: the path of that cell. + - new_text: the new pronunciation dictionary actual string. + - model: the model that the cell is part of. + """ + + thisIter = model.get_iter(path) + model.set(thisIter, ACTUAL, new_text) + + def pronReplacementValueEdited(self, cell, path, new_text, model): + """The user has edited the value of one of the replacement strings + in the pronunciation dictionary. Update our model to reflect this. + + Arguments: + - cell: the cell that changed. + - path: the path of that cell. + - new_text: the new pronunciation dictionary replacement string. + - model: the model that the cell is part of. + """ + + thisIter = model.get_iter(path) + model.set(thisIter, REPLACEMENT, new_text) + + def pronunciationFocusChange(self, widget, event, isFocused): + """Callback for the pronunciation tree's focus-{in,out}-event signal.""" + + _settingsManager.setSetting('usePronunciationDictionary', not isFocused) + + def pronunciationCursorChanged(self, widget): + """Set the search column in the pronunciation dictionary tree view + depending upon which column the user currently has the cursor in. + """ + + [path, focusColumn] = self.pronunciationView.get_cursor() + if focusColumn: + noColumns = len(self.pronunciationView.get_columns()) + for i in range(0, noColumns): + col = self.pronunciationView.get_column(i) + if focusColumn == col: + self.pronunciationView.set_search_column(i) + break + + def _createPronunciationTreeView(self): + """Create the pronunciation dictionary tree view. The view is the + pronunciationTreeView GtkTreeView widget. The view will consist + of a list containing two columns: + ACTUAL - the actual text string (word). + REPLACEMENT - the string that is used to pronounce that word. + """ + + self.pronunciationView = self.get_widget("pronunciationTreeView") + + if self.pronunciationView.get_columns(): + for column in self.pronunciationView.get_columns(): + self.pronunciationView.remove_column(column) + + model = Gtk.ListStore(GObject.TYPE_STRING, + GObject.TYPE_STRING) + + # Initially setup the list store model based on the values of all + # existing entries in the pronunciation dictionary -- unless it's + # the default script. + # + if not self.script.app: + _profile = self.prefsDict.get('activeProfile')[1] + pronDict = _settingsManager.getPronunciations(_profile) + else: + pronDict = pronunciation_dict.pronunciation_dict + for pronKey in sorted(pronDict.keys()): + thisIter = model.append() + try: + actual, replacement = pronDict[pronKey] + except Exception: + # Try to do something sensible for the previous format of + # pronunciation dictionary entries. See bug #464754 for + # more details. + # + actual = pronKey + replacement = pronDict[pronKey] + model.set(thisIter, + ACTUAL, actual, + REPLACEMENT, replacement) + + self.pronunciationView.set_model(model) + + # Pronunciation Dictionary actual string (word) column (ACTUAL). + column = Gtk.TreeViewColumn(guilabels.DICTIONARY_ACTUAL_STRING) + column.set_min_width(250) + column.set_resizable(True) + renderer = Gtk.CellRendererText() + renderer.set_property('editable', True) + column.pack_end(renderer, True) + column.add_attribute(renderer, 'text', ACTUAL) + renderer.connect("edited", self.pronActualValueEdited, model) + self.pronunciationView.insert_column(column, 0) + + # Pronunciation Dictionary replacement string column (REPLACEMENT) + column = Gtk.TreeViewColumn(guilabels.DICTIONARY_REPLACEMENT_STRING) + renderer = Gtk.CellRendererText() + renderer.set_property('editable', True) + column.pack_end(renderer, True) + column.add_attribute(renderer, 'text', REPLACEMENT) + renderer.connect("edited", self.pronReplacementValueEdited, model) + self.pronunciationView.insert_column(column, 1) + + self.pronunciationModel = model + + # Connect a handler for when the user changes columns within the + # view, so that we can adjust the search column for item lookups. + # + self.pronunciationView.connect("cursor_changed", + self.pronunciationCursorChanged) + + self.pronunciationView.connect( + "focus_in_event", self.pronunciationFocusChange, True) + self.pronunciationView.connect( + "focus_out_event", self.pronunciationFocusChange, False) + + def _initGUIState(self): + """Adjust the settings of the various components on the + configuration GUI depending upon the users preferences. + """ + + prefs = self.prefsDict + + # Speech pane. + # + enable = prefs["enableSpeech"] + self.get_widget("speechSupportCheckButton").set_active(enable) + self.get_widget("speechOptionsGrid").set_sensitive(enable) + + enable = prefs["onlySpeakDisplayedText"] + self.get_widget("onlySpeakDisplayedTextCheckButton").set_active(enable) + self.get_widget("contextOptionsGrid").set_sensitive(not enable) + + if prefs["verbalizePunctuationStyle"] == \ + settings.PUNCTUATION_STYLE_NONE: + self.get_widget("noneButton").set_active(True) + elif prefs["verbalizePunctuationStyle"] == \ + settings.PUNCTUATION_STYLE_SOME: + self.get_widget("someButton").set_active(True) + elif prefs["verbalizePunctuationStyle"] == \ + settings.PUNCTUATION_STYLE_MOST: + self.get_widget("mostButton").set_active(True) + else: + self.get_widget("allButton").set_active(True) + + if prefs["speechVerbosityLevel"] == settings.VERBOSITY_LEVEL_BRIEF: + self.get_widget("speechBriefButton").set_active(True) + else: + self.get_widget("speechVerboseButton").set_active(True) + + self.get_widget("onlySpeakDisplayedTextCheckButton").set_active( + prefs["onlySpeakDisplayedText"]) + + self.get_widget("enableSpeechIndentationCheckButton").set_active(\ + prefs["enableSpeechIndentation"]) + + self.get_widget("speakBlankLinesCheckButton").set_active(\ + prefs["speakBlankLines"]) + self.get_widget("speakMultiCaseStringsAsWordsCheckButton").set_active(\ + prefs["speakMultiCaseStringsAsWords"]) + self.get_widget("speakNumbersAsDigitsCheckButton").set_active( + prefs.get("speakNumbersAsDigits", settings.speakNumbersAsDigits)) + self.get_widget("enableTutorialMessagesCheckButton").set_active(\ + prefs["enableTutorialMessages"]) + self.get_widget("enablePauseBreaksCheckButton").set_active(\ + prefs["enablePauseBreaks"]) + self.get_widget("enablePositionSpeakingCheckButton").set_active(\ + prefs["enablePositionSpeaking"]) + self.get_widget("enableMnemonicSpeakingCheckButton").set_active(\ + prefs["enableMnemonicSpeaking"]) + self.get_widget("speakMisspelledIndicatorCheckButton").set_active( + prefs.get("speakMisspelledIndicator", settings.speakMisspelledIndicator)) + self.get_widget("speakDescriptionCheckButton").set_active( + prefs.get("speakDescription", settings.speakDescription)) + self.get_widget("speakContextBlockquoteCheckButton").set_active( + prefs.get("speakContextBlockquote", settings.speakContextList)) + self.get_widget("speakContextLandmarkCheckButton").set_active( + prefs.get("speakContextLandmark", settings.speakContextLandmark)) + self.get_widget("speakContextNonLandmarkFormCheckButton").set_active( + prefs.get("speakContextNonLandmarkForm", settings.speakContextNonLandmarkForm)) + self.get_widget("speakContextListCheckButton").set_active( + prefs.get("speakContextList", settings.speakContextList)) + self.get_widget("speakContextPanelCheckButton").set_active( + prefs.get("speakContextPanel", settings.speakContextPanel)) + self.get_widget("speakContextTableCheckButton").set_active( + prefs.get("speakContextTable", settings.speakContextTable)) + + enable = prefs.get("messagesAreDetailed", settings.messagesAreDetailed) + self.get_widget("messagesAreDetailedCheckButton").set_active(enable) + + enable = prefs.get("useColorNames", settings.useColorNames) + self.get_widget("useColorNamesCheckButton").set_active(enable) + + enable = prefs.get("readFullRowInGUITable", settings.readFullRowInGUITable) + self.get_widget("readFullRowInGUITableCheckButton").set_active(enable) + + enable = prefs.get("readFullRowInDocumentTable", settings.readFullRowInDocumentTable) + self.get_widget("readFullRowInDocumentTableCheckButton").set_active(enable) + + enable = prefs.get("readFullRowInSpreadSheet", settings.readFullRowInSpreadSheet) + self.get_widget("readFullRowInSpreadSheetCheckButton").set_active(enable) + + style = prefs.get("capitalizationStyle", settings.capitalizationStyle) + combobox = self.get_widget("capitalizationStyle") + options = [guilabels.CAPITALIZATION_STYLE_NONE, + guilabels.CAPITALIZATION_STYLE_ICON, + guilabels.CAPITALIZATION_STYLE_SPELL] + self.populateComboBox(combobox, options) + if style == settings.CAPITALIZATION_STYLE_ICON: + value = guilabels.CAPITALIZATION_STYLE_ICON + elif style == settings.CAPITALIZATION_STYLE_SPELL: + value = guilabels.CAPITALIZATION_STYLE_SPELL + else: + value = guilabels.CAPITALIZATION_STYLE_NONE + combobox.set_active(options.index(value)) + + combobox2 = self.get_widget("dateFormatCombo") + sdtime = time.strftime + ltime = time.localtime + self.populateComboBox(combobox2, + [sdtime(messages.DATE_FORMAT_LOCALE, ltime()), + sdtime(messages.DATE_FORMAT_NUMBERS_DM, ltime()), + sdtime(messages.DATE_FORMAT_NUMBERS_MD, ltime()), + sdtime(messages.DATE_FORMAT_NUMBERS_DMY, ltime()), + sdtime(messages.DATE_FORMAT_NUMBERS_MDY, ltime()), + sdtime(messages.DATE_FORMAT_NUMBERS_YMD, ltime()), + sdtime(messages.DATE_FORMAT_FULL_DM, ltime()), + sdtime(messages.DATE_FORMAT_FULL_MD, ltime()), + sdtime(messages.DATE_FORMAT_FULL_DMY, ltime()), + sdtime(messages.DATE_FORMAT_FULL_MDY, ltime()), + sdtime(messages.DATE_FORMAT_FULL_YMD, ltime()), + sdtime(messages.DATE_FORMAT_ABBREVIATED_DM, ltime()), + sdtime(messages.DATE_FORMAT_ABBREVIATED_MD, ltime()), + sdtime(messages.DATE_FORMAT_ABBREVIATED_DMY, ltime()), + sdtime(messages.DATE_FORMAT_ABBREVIATED_MDY, ltime()), + sdtime(messages.DATE_FORMAT_ABBREVIATED_YMD, ltime()) + ]) + + indexdate = DATE_FORMAT_LOCALE + dateFormat = self.prefsDict["presentDateFormat"] + if dateFormat == messages.DATE_FORMAT_LOCALE: + indexdate = DATE_FORMAT_LOCALE + elif dateFormat == messages.DATE_FORMAT_NUMBERS_DM: + indexdate = DATE_FORMAT_NUMBERS_DM + elif dateFormat == messages.DATE_FORMAT_NUMBERS_MD: + indexdate = DATE_FORMAT_NUMBERS_MD + elif dateFormat == messages.DATE_FORMAT_NUMBERS_DMY: + indexdate = DATE_FORMAT_NUMBERS_DMY + elif dateFormat == messages.DATE_FORMAT_NUMBERS_MDY: + indexdate = DATE_FORMAT_NUMBERS_MDY + elif dateFormat == messages.DATE_FORMAT_NUMBERS_YMD: + indexdate = DATE_FORMAT_NUMBERS_YMD + elif dateFormat == messages.DATE_FORMAT_FULL_DM: + indexdate = DATE_FORMAT_FULL_DM + elif dateFormat == messages.DATE_FORMAT_FULL_MD: + indexdate = DATE_FORMAT_FULL_MD + elif dateFormat == messages.DATE_FORMAT_FULL_DMY: + indexdate = DATE_FORMAT_FULL_DMY + elif dateFormat == messages.DATE_FORMAT_FULL_MDY: + indexdate = DATE_FORMAT_FULL_MDY + elif dateFormat == messages.DATE_FORMAT_FULL_YMD: + indexdate = DATE_FORMAT_FULL_YMD + elif dateFormat == messages.DATE_FORMAT_ABBREVIATED_DM: + indexdate = DATE_FORMAT_ABBREVIATED_DM + elif dateFormat == messages.DATE_FORMAT_ABBREVIATED_MD: + indexdate = DATE_FORMAT_ABBREVIATED_MD + elif dateFormat == messages.DATE_FORMAT_ABBREVIATED_DMY: + indexdate = DATE_FORMAT_ABBREVIATED_DMY + elif dateFormat == messages.DATE_FORMAT_ABBREVIATED_MDY: + indexdate = DATE_FORMAT_ABBREVIATED_MDY + elif dateFormat == messages.DATE_FORMAT_ABBREVIATED_YMD: + indexdate = DATE_FORMAT_ABBREVIATED_YMD + combobox2.set_active (indexdate) + + combobox3 = self.get_widget("timeFormatCombo") + self.populateComboBox(combobox3, + [sdtime(messages.TIME_FORMAT_LOCALE, ltime()), + sdtime(messages.TIME_FORMAT_12_HM, ltime()), + sdtime(messages.TIME_FORMAT_12_HMS, ltime()), + sdtime(messages.TIME_FORMAT_24_HMS, ltime()), + sdtime(messages.TIME_FORMAT_24_HMS_WITH_WORDS, ltime()), + sdtime(messages.TIME_FORMAT_24_HM, ltime()), + sdtime(messages.TIME_FORMAT_24_HM_WITH_WORDS, ltime())]) + indextime = TIME_FORMAT_LOCALE + timeFormat = self.prefsDict["presentTimeFormat"] + if timeFormat == messages.TIME_FORMAT_LOCALE: + indextime = TIME_FORMAT_LOCALE + elif timeFormat == messages.TIME_FORMAT_12_HM: + indextime = TIME_FORMAT_12_HM + elif timeFormat == messages.TIME_FORMAT_12_HMS: + indextime = TIME_FORMAT_12_HMS + elif timeFormat == messages.TIME_FORMAT_24_HMS: + indextime = TIME_FORMAT_24_HMS + elif timeFormat == messages.TIME_FORMAT_24_HMS_WITH_WORDS: + indextime = TIME_FORMAT_24_HMS_WITH_WORDS + elif timeFormat == messages.TIME_FORMAT_24_HM: + indextime = TIME_FORMAT_24_HM + elif timeFormat == messages.TIME_FORMAT_24_HM_WITH_WORDS: + indextime = TIME_FORMAT_24_HM_WITH_WORDS + combobox3.set_active (indextime) + + 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("progressBarUpdateIntervalSpinButton").set_value(interval) + + comboBox = self.get_widget("progressBarVerbosity") + levels = [guilabels.PROGRESS_BAR_ALL, + guilabels.PROGRESS_BAR_APPLICATION, + guilabels.PROGRESS_BAR_WINDOW] + self.populateComboBox(comboBox, levels) + comboBox.set_active(prefs["progressBarVerbosity"]) + + enable = prefs["enableMouseReview"] + self.get_widget("enableMouseReviewCheckButton").set_active(enable) + + # Braille pane. + # + self.get_widget("enableBrailleCheckButton").set_active( \ + prefs["enableBraille"]) + state = prefs["brailleRolenameStyle"] == \ + settings.BRAILLE_ROLENAME_STYLE_SHORT + self.get_widget("abbrevRolenames").set_active(state) + + self.get_widget("disableBrailleEOLCheckButton").set_active( + prefs["disableBrailleEOL"]) + + if louis is None: + self.get_widget( \ + "contractedBrailleCheckButton").set_sensitive(False) + else: + self.get_widget("contractedBrailleCheckButton").set_active( \ + prefs["enableContractedBraille"]) + # Set up contraction table combo box and set it to the + # currently used one. + # + tablesCombo = self.get_widget("contractionTableCombo") + tableDict = braille.listTables() + selectedTableIter = None + selectedTable = prefs["brailleContractionTable"] or \ + braille.getDefaultTable() + if tableDict: + tablesModel = Gtk.ListStore(str, str) + names = sorted(tableDict.keys()) + for name in names: + fname = tableDict[name] + it = tablesModel.append([name, fname]) + if os.path.join(braille.tablesdir, fname) == \ + selectedTable: + selectedTableIter = it + cell = self.planeCellRendererText + tablesCombo.clear() + tablesCombo.pack_start(cell, True) + tablesCombo.add_attribute(cell, 'text', 0) + tablesCombo.set_model(tablesModel) + if selectedTableIter: + tablesCombo.set_active_iter(selectedTableIter) + else: + tablesCombo.set_active(0) + else: + tablesCombo.set_sensitive(False) + if prefs["brailleVerbosityLevel"] == settings.VERBOSITY_LEVEL_BRIEF: + self.get_widget("brailleBriefButton").set_active(True) + else: + self.get_widget("brailleVerboseButton").set_active(True) + + self.get_widget("enableBrailleWordWrapCheckButton").set_active( + prefs.get("enableBrailleWordWrap", settings.enableBrailleWordWrap)) + + selectionIndicator = prefs["brailleSelectorIndicator"] + if selectionIndicator == settings.BRAILLE_UNDERLINE_7: + self.get_widget("brailleSelection7Button").set_active(True) + elif selectionIndicator == settings.BRAILLE_UNDERLINE_8: + self.get_widget("brailleSelection8Button").set_active(True) + elif selectionIndicator == settings.BRAILLE_UNDERLINE_BOTH: + self.get_widget("brailleSelectionBothButton").set_active(True) + else: + self.get_widget("brailleSelectionNoneButton").set_active(True) + + linkIndicator = prefs["brailleLinkIndicator"] + if linkIndicator == settings.BRAILLE_UNDERLINE_7: + self.get_widget("brailleLink7Button").set_active(True) + elif linkIndicator == settings.BRAILLE_UNDERLINE_8: + self.get_widget("brailleLink8Button").set_active(True) + elif linkIndicator == settings.BRAILLE_UNDERLINE_BOTH: + self.get_widget("brailleLinkBothButton").set_active(True) + else: + self.get_widget("brailleLinkNoneButton").set_active(True) + + enable = prefs.get("enableFlashMessages", settings.enableFlashMessages) + self.get_widget("enableFlashMessagesCheckButton").set_active(enable) + + enable = prefs.get("flashIsPersistent", settings.flashIsPersistent) + self.get_widget("flashIsPersistentCheckButton").set_active(enable) + + enable = prefs.get("flashIsDetailed", settings.flashIsDetailed) + self.get_widget("flashIsDetailedCheckButton").set_active(enable) + + duration = prefs["brailleFlashTime"] + self.get_widget("brailleFlashTimeSpinButton").set_value(duration / 1000) + + # Key Echo pane. + # + self.get_widget("keyEchoCheckButton").set_active( \ + prefs["enableKeyEcho"]) + self.get_widget("enableAlphabeticKeysCheckButton").set_active( + prefs.get("enableAlphabeticKeys", settings.enableAlphabeticKeys)) + self.get_widget("enableNumericKeysCheckButton").set_active( + prefs.get("enableNumericKeys", settings.enableNumericKeys)) + self.get_widget("enablePunctuationKeysCheckButton").set_active( + prefs.get("enablePunctuationKeys", settings.enablePunctuationKeys)) + self.get_widget("enableSpaceCheckButton").set_active( + prefs.get("enableSpace", settings.enableSpace)) + self.get_widget("enableModifierKeysCheckButton").set_active( \ + prefs["enableModifierKeys"]) + self.get_widget("enableFunctionKeysCheckButton").set_active( \ + prefs["enableFunctionKeys"]) + self.get_widget("enableActionKeysCheckButton").set_active( \ + prefs["enableActionKeys"]) + self.get_widget("enableNavigationKeysCheckButton").set_active( \ + prefs["enableNavigationKeys"]) + self.get_widget("enableDiacriticalKeysCheckButton").set_active( \ + prefs["enableDiacriticalKeys"]) + self.get_widget("enableEchoByCharacterCheckButton").set_active( \ + prefs["enableEchoByCharacter"]) + self.get_widget("enableEchoByWordCheckButton").set_active( \ + prefs["enableEchoByWord"]) + self.get_widget("enableEchoBySentenceCheckButton").set_active( \ + prefs["enableEchoBySentence"]) + + # Text attributes pane. + # + self._createTextAttributesTreeView() + + brailleIndicator = prefs["textAttributesBrailleIndicator"] + if brailleIndicator == settings.BRAILLE_UNDERLINE_7: + self.get_widget("textBraille7Button").set_active(True) + elif brailleIndicator == settings.BRAILLE_UNDERLINE_8: + self.get_widget("textBraille8Button").set_active(True) + elif brailleIndicator == settings.BRAILLE_UNDERLINE_BOTH: + self.get_widget("textBrailleBothButton").set_active(True) + else: + self.get_widget("textBrailleNoneButton").set_active(True) + + # Pronunciation dictionary pane. + # + self._createPronunciationTreeView() + + # General pane. + # + self.get_widget("presentToolTipsCheckButton").set_active( + prefs["presentToolTips"]) + + if prefs["keyboardLayout"] == settings.GENERAL_KEYBOARD_LAYOUT_DESKTOP: + self.get_widget("generalDesktopButton").set_active(True) + else: + self.get_widget("generalLaptopButton").set_active(True) + + combobox = self.get_widget("sayAllStyle") + self.populateComboBox(combobox, [guilabels.SAY_ALL_STYLE_LINE, + guilabels.SAY_ALL_STYLE_SENTENCE]) + combobox.set_active(prefs["sayAllStyle"]) + self.get_widget("rewindAndFastForwardInSayAllCheckButton").set_active( + prefs.get("rewindAndFastForwardInSayAll", settings.rewindAndFastForwardInSayAll)) + self.get_widget("structNavInSayAllCheckButton").set_active( + prefs.get("structNavInSayAll", settings.structNavInSayAll)) + self.get_widget("sayAllContextBlockquoteCheckButton").set_active( + prefs.get("sayAllContextBlockquote", settings.sayAllContextBlockquote)) + self.get_widget("sayAllContextLandmarkCheckButton").set_active( + prefs.get("sayAllContextLandmark", settings.sayAllContextLandmark)) + self.get_widget("sayAllContextNonLandmarkFormCheckButton").set_active( + prefs.get("sayAllContextNonLandmarkForm", settings.sayAllContextNonLandmarkForm)) + self.get_widget("sayAllContextListCheckButton").set_active( + prefs.get("sayAllContextList", settings.sayAllContextList)) + self.get_widget("sayAllContextPanelCheckButton").set_active( + prefs.get("sayAllContextPanel", settings.sayAllContextPanel)) + self.get_widget("sayAllContextTableCheckButton").set_active( + prefs.get("sayAllContextTable", settings.sayAllContextTable)) + + # Cthulhu User Profiles + # + self.profilesCombo = self.get_widget('availableProfilesComboBox1') + self.startingProfileCombo = self.get_widget('availableProfilesComboBox2') + self.profilesComboModel = self.get_widget('model9') + self.__initProfileCombo() + if self.script.app: + self.get_widget('profilesFrame').set_sensitive(False) + + def __initProfileCombo(self): + """Adding available profiles and setting active as the active one""" + + availableProfiles = self.__getAvailableProfiles() + self.profilesComboModel.clear() + + if not len(availableProfiles): + self.profilesComboModel.append(self._defaultProfile) + else: + for profile in availableProfiles: + self.profilesComboModel.append(profile) + + activeProfile = self.prefsDict.get('activeProfile') or self._defaultProfile + startingProfile = self.prefsDict.get('startingProfile') or self._defaultProfile + + activeProfileIter = self.getComboBoxIndex(self.profilesCombo, + activeProfile[0]) + startingProfileIter = self.getComboBoxIndex(self.startingProfileCombo, + startingProfile[0]) + self.profilesCombo.set_active(activeProfileIter) + self.startingProfileCombo.set_active(startingProfileIter) + + def __getAvailableProfiles(self): + """Get available user profiles.""" + return _settingsManager.availableProfiles() + + def _updateCthulhuModifier(self): + combobox = self.get_widget("cthulhuModifierComboBox") + keystring = ", ".join(self.prefsDict["cthulhuModifierKeys"]) + combobox.set_active(self.getComboBoxIndex(combobox, keystring)) + + def populateComboBox(self, combobox, items): + """Populates the combobox with the items provided. + + Arguments: + - combobox: the GtkComboBox to populate + - items: the list of strings with which to populate it + """ + + model = Gtk.ListStore(str) + for item in items: + model.append([item]) + combobox.set_model(model) + + def getComboBoxIndex(self, combobox, searchStr, col=0): + """ For each of the entries in the given combo box, look for searchStr. + Return the index of the entry if searchStr is found. + + Arguments: + - combobox: the GtkComboBox to search. + - searchStr: the string to search for. + + Returns the index of the first entry in combobox with searchStr, or + 0 if not found. + """ + + model = combobox.get_model() + myiter = model.get_iter_first() + for i in range(0, len(model)): + name = model.get_value(myiter, col) + if name == searchStr: + return i + myiter = model.iter_next(myiter) + + return 0 + + def getComboBoxList(self, combobox): + """Get the list of values from the active combox + """ + active = combobox.get_active() + model = combobox.get_model() + activeIter = model.get_iter(active) + activeLabel = model.get_value(activeIter, 0) + activeName = model.get_value(activeIter, 1) + return [activeLabel, activeName] + + def getKeyBindingsModelDict(self, model, modifiedOnly=True): + modelDict = {} + node = model.get_iter_first() + while node: + child = model.iter_children(node) + while child: + key, modified = model.get(child, HANDLER, MODIF) + if modified or not modifiedOnly: + value = [] + value.append(list(model.get( + child, KEY1, MOD_MASK1, MOD_USED1, CLICK_COUNT1))) + modelDict[key] = value + child = model.iter_next(child) + node = model.iter_next(node) + + return modelDict + + def getModelDict(self, model): + """Get the list of values from a list[str,str] model + """ + pronunciation_dict.pronunciation_dict = {} + currentIter = model.get_iter_first() + while currentIter is not None: + key, value = model.get(currentIter, ACTUAL, REPLACEMENT) + if key and value: + pronunciation_dict.setPronunciation(key, value) + currentIter = model.iter_next(currentIter) + modelDict = pronunciation_dict.pronunciation_dict + return modelDict + + def showGUI(self): + """Show the Cthulhu configuration GUI window. This assumes that + the GUI has already been created. + """ + + cthulhuSetupWindow = self.get_widget("cthulhuSetupWindow") + + accelGroup = Gtk.AccelGroup() + cthulhuSetupWindow.add_accel_group(accelGroup) + helpButton = self.get_widget("helpButton") + (keyVal, modifierMask) = Gtk.accelerator_parse("F1") + helpButton.add_accelerator("clicked", + accelGroup, + keyVal, + modifierMask, + 0) + + try: + ts = cthulhu_state.lastInputEvent.timestamp + except Exception: + ts = 0 + if ts == 0: + ts = Gtk.get_current_event_time() + cthulhuSetupWindow.present_with_time(ts) + + # We always want to re-order the text attributes page so that enabled + # items are consistently at the top. + # + self._setSpokenTextAttributes( + self.getTextAttributesView, + _settingsManager.getSetting('enabledSpokenTextAttributes'), + True, True) + + if self.script.app: + title = guilabels.PREFERENCES_APPLICATION_TITLE % AXObject.get_name(self.script.app) + cthulhuSetupWindow.set_title(title) + + cthulhuSetupWindow.show() + + def _initComboBox(self, combobox): + """Initialize the given combo box to take a list of int/str pairs. + + Arguments: + - combobox: the GtkComboBox to initialize. + """ + + cell = Gtk.CellRendererText() + combobox.pack_start(cell, True) + # We only want to display one column; not two. + # + try: + columnToDisplay = combobox.get_cells()[0] + combobox.add_attribute(columnToDisplay, 'text', 1) + except Exception: + combobox.add_attribute(cell, 'text', 1) + model = Gtk.ListStore(int, str) + combobox.set_model(model) + + # Force the display comboboxes to be left aligned. + # + if isinstance(combobox, Gtk.ComboBoxText): + size = combobox.size_request() + cell.set_fixed_size(size[0] - 29, -1) + + return model + + def _setKeyEchoItems(self): + """[In]sensitize the checkboxes for the various types of key echo, + depending upon whether the value of the key echo check button is set. + """ + + enable = self.get_widget("keyEchoCheckButton").get_active() + self.get_widget("enableAlphabeticKeysCheckButton").set_sensitive(enable) + self.get_widget("enableNumericKeysCheckButton").set_sensitive(enable) + self.get_widget("enablePunctuationKeysCheckButton").set_sensitive(enable) + self.get_widget("enableSpaceCheckButton").set_sensitive(enable) + self.get_widget("enableModifierKeysCheckButton").set_sensitive(enable) + self.get_widget("enableFunctionKeysCheckButton").set_sensitive(enable) + self.get_widget("enableActionKeysCheckButton").set_sensitive(enable) + self.get_widget("enableNavigationKeysCheckButton").set_sensitive(enable) + self.get_widget("enableDiacriticalKeysCheckButton").set_sensitive( \ + enable) + + def _presentMessage(self, text, interrupt=False): + """If the text field is not None, presents the given text, optionally + interrupting anything currently being spoken. + + Arguments: + - text: the text to present + - interrupt: if True, interrupt any speech currently being spoken + """ + + self.script.speakMessage(text, interrupt=interrupt) + try: + self.script.displayBrailleMessage(text, flashTime=-1) + except Exception: + pass + + def _createNode(self, appName): + """Create a new root node in the TreeStore model with the name of the + application. + + Arguments: + - appName: the name of the TreeStore Node (the same of the application) + """ + + model = self.keyBindingsModel + + myiter = model.append(None) + model.set_value(myiter, DESCRIP, appName) + model.set_value(myiter, MODIF, False) + + return myiter + + def _getIterOf(self, appName): + """Returns the Gtk.TreeIter of the TreeStore model + that matches the application name passed as argument + + Arguments: + - appName: a string with the name of the application of the node wanted + it's the same that the field DESCRIP of the model treeStore + """ + + model = self.keyBindingsModel + + for row in model: + if ((model.iter_depth(row.iter) == 0) \ + and (row[DESCRIP] == appName)): + return row.iter + + return None + + def _clickCountToString(self, clickCount): + """Given a numeric clickCount, returns a string for inclusion + in the list of keybindings. + + Argument: + - clickCount: the number of clicks associated with the keybinding. + """ + + clickCountString = "" + if clickCount == 2: + clickCountString = f" ({guilabels.CLICK_COUNT_DOUBLE})" + elif clickCount == 3: + clickCountString = f" ({guilabels.CLICK_COUNT_TRIPLE})" + + return clickCountString + + def _insertRow(self, handl, kb, parent=None, modif=False): + """Appends a new row with the new keybinding data to the treeview + + Arguments: + - handl: the name of the handler associated to the keyBinding + - kb: the new keybinding. + - parent: the parent node of the treeview, where to append the kb + - modif: whether to check the modified field or not. + + Returns a Gtk.TreeIter pointing at the new row. + """ + + model = self.keyBindingsModel + + if parent is None: + parent = self._getIterOf(guilabels.KB_GROUP_DEFAULT) + + if parent is not None: + myiter = model.append(parent) + if not kb.keysymstring: + text = None + else: + clickCount = self._clickCountToString(kb.click_count) + keysymstring = kb.keysymstring + text = keybindings.getModifierNames(kb.modifiers) \ + + keysymstring \ + + clickCount + + model.set_value(myiter, HANDLER, handl) + model.set_value(myiter, DESCRIP, kb.handler.description) + model.set_value(myiter, MOD_MASK1, str(kb.modifier_mask)) + model.set_value(myiter, MOD_USED1, str(kb.modifiers)) + model.set_value(myiter, KEY1, kb.keysymstring) + model.set_value(myiter, CLICK_COUNT1, str(kb.click_count)) + if text is not None: + model.set_value(myiter, OLDTEXT1, text) + model.set_value(myiter, TEXT1, text) + model.set_value(myiter, MODIF, modif) + model.set_value(myiter, EDITABLE, True) + + return myiter + else: + return None + + def _insertRowBraille(self, handl, com, inputEvHand, + parent=None, modif=False): + """Appends a new row with the new braille binding data to the treeview + + Arguments: + - handl: the name of the handler associated to the brailleBinding + - com: the BrlTTY command + - inputEvHand: the inputEventHandler with the new brailleBinding + - parent: the parent node of the treeview, where to append the kb + - modif: whether to check the modified field or not. + + Returns a Gtk.TreeIter pointing at the new row. + """ + + model = self.keyBindingsModel + + if parent is None: + parent = self._getIterOf(guilabels.KB_GROUP_BRAILLE) + + if parent is not None: + myiter = model.append(parent) + model.set_value(myiter, HANDLER, handl) + model.set_value(myiter, DESCRIP, inputEvHand.description) + model.set_value(myiter, KEY1, str(com)) + model.set_value(myiter, TEXT1, braille.command_name[com]) + model.set_value(myiter, MODIF, modif) + model.set_value(myiter, EDITABLE, False) + return myiter + else: + return None + + def _markModified(self): + """ Mark as modified the user custom key bindings: + """ + + try: + self.script.setupInputEventHandlers() + keyBinds = keybindings.KeyBindings() + keyBinds = _settingsManager.overrideKeyBindings(self.script, keyBinds) + keyBind = keybindings.KeyBinding(None, None, None, None) + treeModel = self.keyBindingsModel + + myiter = treeModel.get_iter_first() + while myiter is not None: + iterChild = treeModel.iter_children(myiter) + while iterChild is not None: + descrip = treeModel.get_value(iterChild, DESCRIP) + keyBind.handler = \ + input_event.InputEventHandler(None, descrip) + if keyBinds.hasKeyBinding(keyBind, + typeOfSearch="description"): + treeModel.set_value(iterChild, MODIF, True) + iterChild = treeModel.iter_next(iterChild) + myiter = treeModel.iter_next(myiter) + except Exception: + debug.printException(debug.LEVEL_SEVERE) + + def _populateKeyBindings(self, clearModel=True): + """Fills the TreeView with the list of Cthulhu keybindings + + Arguments: + - clearModel: if True, initially clear out the key bindings model. + """ + + self.keyBindView.set_model(None) + self.keyBindView.set_headers_visible(False) + self.keyBindView.hide() + if clearModel: + self.keyBindingsModel.clear() + self.kbindings = None + + appName = AXObject.get_name(self.script.app) + iterApp = self._createNode(appName) + iterCthulhu = self._createNode(guilabels.KB_GROUP_DEFAULT) + iterUnbound = self._createNode(guilabels.KB_GROUP_UNBOUND) + iterNotificationPresenter = self._createNode(guilabels.KB_GROUP_NOTIFICATIONS) + iterFlatReviewPresenter = self._createNode(guilabels.KB_GROUP_FLAT_REVIEW) + iterSpeechAndVerbosity = self._createNode(guilabels.KB_GROUP_SPEECH_VERBOSITY) + iterDateAndTime = self._createNode(guilabels.KB_GROUP_DATE_AND_TIME) + iterBookmarks = self._createNode(guilabels.KB_GROUP_BOOKMARKS) + iterObjectNav = self._createNode(guilabels.KB_GROUP_OBJECT_NAVIGATION) + iterWhereAmIPresenter = self._createNode(guilabels.KB_GROUP_WHERE_AM_I) + iterLearnMode = self._createNode(guilabels.KB_GROUP_LEARN_MODE) + iterMouseReviewer = self._createNode(guilabels.KB_GROUP_MOUSE_REVIEW) + iterActionPresenter = self._createNode(guilabels.KB_GROUP_ACTIONS) + + if not self.kbindings: + self.kbindings = keybindings.KeyBindings() + self.script.setupInputEventHandlers() + allKeyBindings = self.script.getKeyBindings() + defKeyBindings = self.script.getDefaultKeyBindings() + npKeyBindings = self.script.getNotificationPresenter().get_bindings() + svKeyBindings = self.script.getSpeechAndVerbosityManager().get_bindings() + dtKeyBindings = self.script.getDateAndTimePresenter().get_bindings() + bmKeyBindings = self.script.getBookmarks().get_bindings() + onKeyBindings = self.script.getObjectNavigator().get_bindings() + lmKeyBindings = self.script.getLearnModePresenter().get_bindings() + mrKeyBindings = keybindings.KeyBindings() + if self.app.getDynamicApiManager().getAPI('MouseReviewer'): + mrKeyBindings = self.app.getDynamicApiManager().getAPI('MouseReviewer').get_bindings() + acKeyBindings = self.script.getActionPresenter().get_bindings() + + layout = _settingsManager.getSetting('keyboardLayout') + isDesktop = layout == settings.GENERAL_KEYBOARD_LAYOUT_DESKTOP + frKeyBindings = self.script.getFlatReviewPresenter().get_bindings(isDesktop) + waiKeyBindings = self.script.getWhereAmIPresenter().get_bindings(isDesktop) + + for kb in allKeyBindings.keyBindings: + if not self.kbindings.hasKeyBinding(kb, "strict"): + handl = self.script.getInputEventHandlerKey(kb.handler) + if npKeyBindings.hasKeyBinding(kb, "description"): + self._insertRow(handl, kb, iterNotificationPresenter) + elif onKeyBindings.hasKeyBinding(kb, "description"): + self._insertRow(handl, kb, iterObjectNav) + elif frKeyBindings.hasKeyBinding(kb, "description"): + self._insertRow(handl, kb, iterFlatReviewPresenter) + elif waiKeyBindings.hasKeyBinding(kb, "description"): + self._insertRow(handl, kb, iterWhereAmIPresenter) + elif svKeyBindings.hasKeyBinding(kb, "description"): + self._insertRow(handl, kb, iterSpeechAndVerbosity) + elif dtKeyBindings.hasKeyBinding(kb, "description"): + self._insertRow(handl, kb, iterDateAndTime) + elif bmKeyBindings.hasKeyBinding(kb, "description"): + self._insertRow(handl, kb, iterBookmarks) + elif lmKeyBindings.hasKeyBinding(kb, "description"): + self._insertRow(handl, kb, iterLearnMode) + elif acKeyBindings.hasKeyBinding(kb, "description"): + self._insertRow(handl, kb, iterActionPresenter) + elif mrKeyBindings.hasKeyBinding(kb, "description"): + self._insertRow(handl, kb, iterMouseReviewer) + elif not defKeyBindings.hasKeyBinding(kb, "description"): + self._insertRow(handl, kb, iterApp) + elif kb.keysymstring: + self._insertRow(handl, kb, iterCthulhu) + else: + self._insertRow(handl, kb, iterUnbound) + self.kbindings.add(kb) + + if not self.keyBindingsModel.iter_has_child(iterApp): + self.keyBindingsModel.remove(iterApp) + + if not self.keyBindingsModel.iter_has_child(iterUnbound): + self.keyBindingsModel.remove(iterUnbound) + + self._updateCthulhuModifier() + self._markModified() + iterBB = self._createNode(guilabels.KB_GROUP_BRAILLE) + self.bbindings = self.script.getBrailleBindings() + for com, inputEvHand in self.bbindings.items(): + handl = self.script.getInputEventHandlerKey(inputEvHand) + self._insertRowBraille(handl, com, inputEvHand, iterBB) + + self.keyBindView.set_model(self.keyBindingsModel) + self.keyBindView.set_headers_visible(True) + self.keyBindingsModel.set_sort_column_id(OLDTEXT1, Gtk.SortType.ASCENDING) + self.keyBindView.show() + + # Keep track of new/unbound keybindings that have yet to be applied. + # + self.pendingKeyBindings = {} + + def _cleanupSpeechServers(self): + """Remove unwanted factories and drivers for the current active + factory, when the user dismisses the Cthulhu Preferences dialog.""" + + for workingFactory in self.workingFactories: + if not (workingFactory == self.speechSystemsChoice): + workingFactory.SpeechServer.shutdownActiveServers() + else: + servers = workingFactory.SpeechServer.getSpeechServers() + for server in servers: + if not (server == self.speechServersChoice): + server.shutdown() + + def speechSupportChecked(self, widget): + """Signal handler for the "toggled" signal for the + speechSupportCheckButton GtkCheckButton widget. The user has + [un]checked the 'Enable Speech' checkbox. Set the 'enableSpeech' + preference to the new value. Set the rest of the speech pane items + [in]sensensitive depending upon whether this checkbox is checked. + + Arguments: + - widget: the component that generated the signal. + """ + + enable = widget.get_active() + self.prefsDict["enableSpeech"] = enable + self.get_widget("speechOptionsGrid").set_sensitive(enable) + + def onlySpeakDisplayedTextToggled(self, widget): + """Signal handler for the "toggled" signal for the GtkCheckButton + onlySpeakDisplayedText. In addition to updating the preferences, + set the sensitivity of the contextOptionsGrid. + + Arguments: + - widget: the component that generated the signal. + """ + + enable = widget.get_active() + self.prefsDict["onlySpeakDisplayedText"] = enable + self.get_widget("contextOptionsGrid").set_sensitive(not enable) + + def speechSystemsChanged(self, widget): + """Signal handler for the "changed" signal for the speechSystems + GtkComboBox widget. The user has selected a different speech + system. Clear the existing list of speech servers, and setup + a new list of speech servers based on the new choice. Setup a + new list of voices for the first speech server in the list. + + Arguments: + - widget: the component that generated the signal. + """ + + if self.initializingSpeech: + return + + selectedIndex = widget.get_active() + self.speechSystemsChoice = self.speechSystemsChoices[selectedIndex] + self._setupSpeechServers() + + def speechServersChanged(self, widget): + """Signal handler for the "changed" signal for the speechServers + GtkComboBox widget. The user has selected a different speech + server. Clear the existing list of voices, and setup a new + list of voices based on the new choice. + + Arguments: + - widget: the component that generated the signal. + """ + + if self.initializingSpeech: + return + + selectedIndex = widget.get_active() + self.speechServersChoice = self.speechServersChoices[selectedIndex] + + # Whenever the speech servers change, we need to make sure we + # clear whatever family was in use by the current voice types. + # Otherwise, we can end up with family names from one server + # bleeding over (e.g., "Paul" from Fonix ends up getting in + # the "Default" voice type after we switch to eSpeak). + # + try: + del self.defaultVoice[acss.ACSS.FAMILY] + del self.uppercaseVoice[acss.ACSS.FAMILY] + del self.hyperlinkVoice[acss.ACSS.FAMILY] + del self.systemVoice[acss.ACSS.FAMILY] + except Exception: + pass + + self._setupVoices() + + def speechLanguagesChanged(self, widget): + """Signal handler for the "value_changed" signal for the languages + GtkComboBox widget. The user has selected a different voice + language. Save the new voice language name based on the new choice. + + Arguments: + - widget: the component that generated the signal. + """ + + if self.initializingSpeech: + return + + selectedIndex = widget.get_active() + try: + self.speechLanguagesChoice = self.speechLanguagesChoices[selectedIndex] + if (self.speechServersChoice, self.speechLanguagesChoice) in \ + self.selectedFamilyChoices: + i = self.selectedFamilyChoices[self.speechServersChoice, \ + self.speechLanguagesChoice] + family = self.speechFamiliesChoices[i] + name = family[speechserver.VoiceFamily.NAME] + language = family[speechserver.VoiceFamily.LANG] + dialect = family[speechserver.VoiceFamily.DIALECT] + variant = family[speechserver.VoiceFamily.VARIANT] + voiceType = self.get_widget("voiceTypesCombo").get_active() + self._setFamilyNameForVoiceType(voiceType, name, language, dialect, variant) + except Exception: + debug.printException(debug.LEVEL_SEVERE) + + # Remember the last family manually selected by the user for the + # current speech server. + # + if not selectedIndex == -1: + self.selectedLanguageChoices[self.speechServersChoice] = selectedIndex + + self._setupFamilies() + + def speechFamiliesChanged(self, widget): + """Signal handler for the "value_changed" signal for the families + GtkComboBox widget. The user has selected a different voice + family. Save the new voice family name based on the new choice. + + Arguments: + - widget: the component that generated the signal. + """ + + if self.initializingSpeech: + return + + selectedIndex = widget.get_active() + try: + family = self.speechFamiliesChoices[selectedIndex] + name = family[speechserver.VoiceFamily.NAME] + language = family[speechserver.VoiceFamily.LANG] + dialect = family[speechserver.VoiceFamily.DIALECT] + variant = family[speechserver.VoiceFamily.VARIANT] + voiceType = self.get_widget("voiceTypesCombo").get_active() + self._setFamilyNameForVoiceType(voiceType, name, language, dialect, variant) + except Exception: + debug.printException(debug.LEVEL_SEVERE) + + # Remember the last family manually selected by the user for the + # current speech server. + # + if not selectedIndex == -1: + self.selectedFamilyChoices[self.speechServersChoice, \ + self.speechLanguagesChoice] = selectedIndex + + def voiceTypesChanged(self, widget): + """Signal handler for the "changed" signal for the voiceTypes + GtkComboBox widget. The user has selected a different voice + type. Setup the new family, rate, pitch and volume component + values based on the new choice. + + Arguments: + - widget: the component that generated the signal. + """ + + if self.initializingSpeech: + return + + voiceType = widget.get_active() + self._setVoiceSettingsForVoiceType(voiceType) + + def rateValueChanged(self, widget): + """Signal handler for the "value_changed" signal for the rateScale + GtkScale widget. The user has changed the current rate value. + Save the new rate value based on the currently selected voice + type. + + Arguments: + - widget: the component that generated the signal. + """ + + rate = widget.get_value() + voiceType = self.get_widget("voiceTypesCombo").get_active() + self._setRateForVoiceType(voiceType, rate) + voices = _settingsManager.getSetting('voices') + voices[settings.DEFAULT_VOICE][acss.ACSS.RATE] = rate + _settingsManager.setSetting('voices', voices) + + def pitchValueChanged(self, widget): + """Signal handler for the "value_changed" signal for the pitchScale + GtkScale widget. The user has changed the current pitch value. + Save the new pitch value based on the currently selected voice + type. + + Arguments: + - widget: the component that generated the signal. + """ + + pitch = widget.get_value() + voiceType = self.get_widget("voiceTypesCombo").get_active() + self._setPitchForVoiceType(voiceType, pitch) + voices = _settingsManager.getSetting('voices') + voices[settings.DEFAULT_VOICE][acss.ACSS.AVERAGE_PITCH] = pitch + _settingsManager.setSetting('voices', voices) + + def volumeValueChanged(self, widget): + """Signal handler for the "value_changed" signal for the voiceScale + GtkScale widget. The user has changed the current volume value. + Save the new volume value based on the currently selected voice + type. + + Arguments: + - widget: the component that generated the signal. + """ + + volume = widget.get_value() + voiceType = self.get_widget("voiceTypesCombo").get_active() + self._setVolumeForVoiceType(voiceType, volume) + voices = _settingsManager.getSetting('voices') + voices[settings.DEFAULT_VOICE][acss.ACSS.GAIN] = volume + _settingsManager.setSetting('voices', voices) + + def checkButtonToggled(self, widget): + """Signal handler for "toggled" signal for basic GtkCheckButton + widgets. The user has altered the state of the checkbox. + Set the preference to the new value. + + Arguments: + - widget: the component that generated the signal. + """ + + # To use this default handler please make sure: + # The name of the setting that will be changed is: settingName + # The id of the widget in the ui should be: settingNameCheckButton + # + settingName = Gtk.Buildable.get_name(widget) + # strip "CheckButton" from the end. + settingName = settingName[:-11] + self.prefsDict[settingName] = widget.get_active() + + def keyEchoChecked(self, widget): + """Signal handler for the "toggled" signal for the + keyEchoCheckbutton GtkCheckButton widget. The user has + [un]checked the 'Enable Key Echo' checkbox. Set the + 'enableKeyEcho' preference to the new value. [In]sensitize + the checkboxes for the various types of key echo, depending + upon whether this value is checked or unchecked. + + Arguments: + - widget: the component that generated the signal. + """ + + self.prefsDict["enableKeyEcho"] = widget.get_active() + self._setKeyEchoItems() + + def brailleSelectionChanged(self, widget): + """Signal handler for the "toggled" signal for the + brailleSelectionNoneButton, brailleSelection7Button, + brailleSelection8Button or brailleSelectionBothButton + GtkRadioButton widgets. The user has toggled the braille + selection indicator value. If this signal was generated + as the result of a radio button getting selected (as + opposed to a radio button losing the selection), set the + 'brailleSelectorIndicator' preference to the new value. + + Arguments: + - widget: the component that generated the signal. + """ + + if widget.get_active(): + if widget.get_label() == guilabels.BRAILLE_DOT_7: + self.prefsDict["brailleSelectorIndicator"] = \ + settings.BRAILLE_UNDERLINE_7 + elif widget.get_label() == guilabels.BRAILLE_DOT_8: + self.prefsDict["brailleSelectorIndicator"] = \ + settings.BRAILLE_UNDERLINE_8 + elif widget.get_label() == guilabels.BRAILLE_DOT_7_8: + self.prefsDict["brailleSelectorIndicator"] = \ + settings.BRAILLE_UNDERLINE_BOTH + else: + self.prefsDict["brailleSelectorIndicator"] = \ + settings.BRAILLE_UNDERLINE_NONE + + def brailleLinkChanged(self, widget): + """Signal handler for the "toggled" signal for the + brailleLinkNoneButton, brailleLink7Button, + brailleLink8Button or brailleLinkBothButton + GtkRadioButton widgets. The user has toggled the braille + link indicator value. If this signal was generated + as the result of a radio button getting selected (as + opposed to a radio button losing the selection), set the + 'brailleLinkIndicator' preference to the new value. + + Arguments: + - widget: the component that generated the signal. + """ + + if widget.get_active(): + if widget.get_label() == guilabels.BRAILLE_DOT_7: + self.prefsDict["brailleLinkIndicator"] = \ + settings.BRAILLE_UNDERLINE_7 + elif widget.get_label() == guilabels.BRAILLE_DOT_8: + self.prefsDict["brailleLinkIndicator"] = \ + settings.BRAILLE_UNDERLINE_8 + elif widget.get_label() == guilabels.BRAILLE_DOT_7_8: + self.prefsDict["brailleLinkIndicator"] = \ + settings.BRAILLE_UNDERLINE_BOTH + else: + self.prefsDict["brailleLinkIndicator"] = \ + settings.BRAILLE_UNDERLINE_NONE + + def brailleIndicatorChanged(self, widget): + """Signal handler for the "toggled" signal for the + textBrailleNoneButton, textBraille7Button, textBraille8Button + or textBrailleBothButton GtkRadioButton widgets. The user has + toggled the text attributes braille indicator value. If this signal + was generated as the result of a radio button getting selected + (as opposed to a radio button losing the selection), set the + 'textAttributesBrailleIndicator' preference to the new value. + + Arguments: + - widget: the component that generated the signal. + """ + + if widget.get_active(): + if widget.get_label() == guilabels.BRAILLE_DOT_7: + self.prefsDict["textAttributesBrailleIndicator"] = \ + settings.BRAILLE_UNDERLINE_7 + elif widget.get_label() == guilabels.BRAILLE_DOT_8: + self.prefsDict["textAttributesBrailleIndicator"] = \ + settings.BRAILLE_UNDERLINE_8 + elif widget.get_label() == guilabels.BRAILLE_DOT_7_8: + self.prefsDict["textAttributesBrailleIndicator"] = \ + settings.BRAILLE_UNDERLINE_BOTH + else: + self.prefsDict["textAttributesBrailleIndicator"] = \ + settings.BRAILLE_UNDERLINE_NONE + + def punctuationLevelChanged(self, widget): + """Signal handler for the "toggled" signal for the noneButton, + someButton or allButton GtkRadioButton widgets. The user has + toggled the speech punctuation level value. If this signal + was generated as the result of a radio button getting selected + (as opposed to a radio button losing the selection), set the + 'verbalizePunctuationStyle' preference to the new value. + + Arguments: + - widget: the component that generated the signal. + """ + + if widget.get_active(): + if widget.get_label() == guilabels.PUNCTUATION_STYLE_NONE: + self.prefsDict["verbalizePunctuationStyle"] = \ + settings.PUNCTUATION_STYLE_NONE + elif widget.get_label() == guilabels.PUNCTUATION_STYLE_SOME: + self.prefsDict["verbalizePunctuationStyle"] = \ + settings.PUNCTUATION_STYLE_SOME + elif widget.get_label() == guilabels.PUNCTUATION_STYLE_MOST: + self.prefsDict["verbalizePunctuationStyle"] = \ + settings.PUNCTUATION_STYLE_MOST + else: + self.prefsDict["verbalizePunctuationStyle"] = \ + settings.PUNCTUATION_STYLE_ALL + + def cthulhuModifierChanged(self, widget): + """Signal handler for the changed signal for the cthulhuModifierComboBox + Set the 'cthulhuModifierKeys' preference to the new value. + + Arguments: + - widget: the component that generated the signal. + """ + + model = widget.get_model() + myIter = widget.get_active_iter() + cthulhuModifier = model[myIter][0] + self.prefsDict["cthulhuModifierKeys"] = cthulhuModifier.split(', ') + + def progressBarVerbosityChanged(self, widget): + """Signal handler for the changed signal for the progressBarVerbosity + GtkComboBox widget. Set the 'progressBarVerbosity' preference to + the new value. + + Arguments: + - widget: the component that generated the signal. + """ + + model = widget.get_model() + myIter = widget.get_active_iter() + progressBarVerbosity = model[myIter][0] + if progressBarVerbosity == guilabels.PROGRESS_BAR_ALL: + self.prefsDict["progressBarVerbosity"] = \ + settings.PROGRESS_BAR_ALL + elif progressBarVerbosity == guilabels.PROGRESS_BAR_WINDOW: + self.prefsDict["progressBarVerbosity"] = \ + settings.PROGRESS_BAR_WINDOW + else: + self.prefsDict["progressBarVerbosity"] = \ + settings.PROGRESS_BAR_APPLICATION + + def capitalizationStyleChanged(self, widget): + model = widget.get_model() + myIter = widget.get_active_iter() + capitalizationStyle = model[myIter][0] + if capitalizationStyle == guilabels.CAPITALIZATION_STYLE_ICON: + self.prefsDict["capitalizationStyle"] = settings.CAPITALIZATION_STYLE_ICON + elif capitalizationStyle == guilabels.CAPITALIZATION_STYLE_SPELL: + self.prefsDict["capitalizationStyle"] = settings.CAPITALIZATION_STYLE_SPELL + else: + self.prefsDict["capitalizationStyle"] = settings.CAPITALIZATION_STYLE_NONE + self.script.speechAndVerbosityManager.update_capitalization_style() + + def sayAllStyleChanged(self, widget): + """Signal handler for the "changed" signal for the sayAllStyle + GtkComboBox widget. Set the 'sayAllStyle' preference to the + new value. + + Arguments: + - widget: the component that generated the signal. + """ + + model = widget.get_model() + myIter = widget.get_active_iter() + sayAllStyle = model[myIter][0] + if sayAllStyle == guilabels.SAY_ALL_STYLE_LINE: + self.prefsDict["sayAllStyle"] = settings.SAYALL_STYLE_LINE + elif sayAllStyle == guilabels.SAY_ALL_STYLE_SENTENCE: + self.prefsDict["sayAllStyle"] = settings.SAYALL_STYLE_SENTENCE + + def dateFormatChanged(self, widget): + """Signal handler for the "changed" signal for the dateFormat + GtkComboBox widget. Set the 'dateFormat' preference to the + new value. + + Arguments: + - widget: the component that generated the signal. + """ + + dateFormatCombo = widget.get_active() + if dateFormatCombo == DATE_FORMAT_LOCALE: + newFormat = messages.DATE_FORMAT_LOCALE + elif dateFormatCombo == DATE_FORMAT_NUMBERS_DM: + newFormat = messages.DATE_FORMAT_NUMBERS_DM + elif dateFormatCombo == DATE_FORMAT_NUMBERS_MD: + newFormat = messages.DATE_FORMAT_NUMBERS_MD + elif dateFormatCombo == DATE_FORMAT_NUMBERS_DMY: + newFormat = messages.DATE_FORMAT_NUMBERS_DMY + elif dateFormatCombo == DATE_FORMAT_NUMBERS_MDY: + newFormat = messages.DATE_FORMAT_NUMBERS_MDY + elif dateFormatCombo == DATE_FORMAT_NUMBERS_YMD: + newFormat = messages.DATE_FORMAT_NUMBERS_YMD + elif dateFormatCombo == DATE_FORMAT_FULL_DM: + newFormat = messages.DATE_FORMAT_FULL_DM + elif dateFormatCombo == DATE_FORMAT_FULL_MD: + newFormat = messages.DATE_FORMAT_FULL_MD + elif dateFormatCombo == DATE_FORMAT_FULL_DMY: + newFormat = messages.DATE_FORMAT_FULL_DMY + elif dateFormatCombo == DATE_FORMAT_FULL_MDY: + newFormat = messages.DATE_FORMAT_FULL_MDY + elif dateFormatCombo == DATE_FORMAT_FULL_YMD: + newFormat = messages.DATE_FORMAT_FULL_YMD + elif dateFormatCombo == DATE_FORMAT_ABBREVIATED_DM: + newFormat = messages.DATE_FORMAT_ABBREVIATED_DM + elif dateFormatCombo == DATE_FORMAT_ABBREVIATED_MD: + newFormat = messages.DATE_FORMAT_ABBREVIATED_MD + elif dateFormatCombo == DATE_FORMAT_ABBREVIATED_DMY: + newFormat = messages.DATE_FORMAT_ABBREVIATED_DMY + elif dateFormatCombo == DATE_FORMAT_ABBREVIATED_MDY: + newFormat = messages.DATE_FORMAT_ABBREVIATED_MDY + elif dateFormatCombo == DATE_FORMAT_ABBREVIATED_YMD: + newFormat = messages.DATE_FORMAT_ABBREVIATED_YMD + self.prefsDict["presentDateFormat"] = newFormat + + def timeFormatChanged(self, widget): + """Signal handler for the "changed" signal for the timeFormat + GtkComboBox widget. Set the 'timeFormat' preference to the + new value. + + Arguments: + - widget: the component that generated the signal. + """ + + timeFormatCombo = widget.get_active() + if timeFormatCombo == TIME_FORMAT_LOCALE: + newFormat = messages.TIME_FORMAT_LOCALE + elif timeFormatCombo == TIME_FORMAT_12_HM: + newFormat = messages.TIME_FORMAT_12_HM + elif timeFormatCombo == TIME_FORMAT_12_HMS: + newFormat = messages.TIME_FORMAT_12_HMS + elif timeFormatCombo == TIME_FORMAT_24_HMS: + newFormat = messages.TIME_FORMAT_24_HMS + elif timeFormatCombo == TIME_FORMAT_24_HMS_WITH_WORDS: + newFormat = messages.TIME_FORMAT_24_HMS_WITH_WORDS + elif timeFormatCombo == TIME_FORMAT_24_HM: + newFormat = messages.TIME_FORMAT_24_HM + elif timeFormatCombo == TIME_FORMAT_24_HM_WITH_WORDS: + newFormat = messages.TIME_FORMAT_24_HM_WITH_WORDS + self.prefsDict["presentTimeFormat"] = newFormat + + def speechVerbosityChanged(self, widget): + """Signal handler for the "toggled" signal for the speechBriefButton, + or speechVerboseButton GtkRadioButton widgets. The user has + toggled the speech verbosity level value. If this signal was + generated as the result of a radio button getting selected + (as opposed to a radio button losing the selection), set the + 'speechVerbosityLevel' preference to the new value. + + Arguments: + - widget: the component that generated the signal. + """ + + if widget.get_active(): + if widget.get_label() == guilabels.VERBOSITY_LEVEL_BRIEF: + self.prefsDict["speechVerbosityLevel"] = \ + settings.VERBOSITY_LEVEL_BRIEF + else: + self.prefsDict["speechVerbosityLevel"] = \ + settings.VERBOSITY_LEVEL_VERBOSE + + def progressBarUpdateIntervalValueChanged(self, widget): + """Signal handler for the "value_changed" signal for the + progressBarUpdateIntervalSpinButton GtkSpinButton widget. + + Arguments: + - widget: the component that generated the signal. + """ + + self.prefsDict["progressBarUpdateInterval"] = widget.get_value_as_int() + + def brailleFlashTimeValueChanged(self, widget): + self.prefsDict["brailleFlashTime"] = widget.get_value_as_int() * 1000 + + def abbrevRolenamesChecked(self, widget): + """Signal handler for the "toggled" signal for the abbrevRolenames + GtkCheckButton widget. The user has [un]checked the 'Abbreviated + Rolenames' checkbox. Set the 'brailleRolenameStyle' preference + to the new value. + + Arguments: + - widget: the component that generated the signal. + """ + + if widget.get_active(): + self.prefsDict["brailleRolenameStyle"] = \ + settings.BRAILLE_ROLENAME_STYLE_SHORT + else: + self.prefsDict["brailleRolenameStyle"] = \ + settings.BRAILLE_ROLENAME_STYLE_LONG + + def brailleVerbosityChanged(self, widget): + """Signal handler for the "toggled" signal for the brailleBriefButton, + or brailleVerboseButton GtkRadioButton widgets. The user has + toggled the braille verbosity level value. If this signal was + generated as the result of a radio button getting selected + (as opposed to a radio button losing the selection), set the + 'brailleVerbosityLevel' preference to the new value. + + Arguments: + - widget: the component that generated the signal. + """ + + if widget.get_active(): + if widget.get_label() == guilabels.VERBOSITY_LEVEL_BRIEF: + self.prefsDict["brailleVerbosityLevel"] = \ + settings.VERBOSITY_LEVEL_BRIEF + else: + self.prefsDict["brailleVerbosityLevel"] = \ + settings.VERBOSITY_LEVEL_VERBOSE + + def keyModifiedToggle(self, cell, path, model, col): + """When the user changes a checkbox field (boolean field)""" + + model[path][col] = not model[path][col] + return + + def editingKey(self, cell, editable, path, treeModel): + """Starts user input of a Key for a selected key binding""" + + self._presentMessage(messages.KB_ENTER_NEW_KEY) + cthulhu_state.capturingKeys = True + editable.connect('key-press-event', self.kbKeyPressed) + return + + def editingCanceledKey(self, editable): + """Stops user input of a Key for a selected key binding""" + + cthulhu_state.capturingKeys = False + self._capturedKey = [] + return + + def _processKeyCaptured(self, keyPressedEvent): + """Called when a new key event arrives and we are capturing keys. + (used for key bindings redefinition) + """ + + # We want the keyname rather than the printable character. + # If it's not on the keypad, get the name of the unshifted + # character. (i.e. "1" instead of "!") + # + keycode = keyPressedEvent.hardware_keycode + keymap = Gdk.Keymap.get_default() + entries_for_keycode = keymap.get_entries_for_keycode(keycode) + entries = entries_for_keycode[-1] + eventString = Gdk.keyval_name(entries[0]) + eventState = keyPressedEvent.state + + cthulhuMods = settings.cthulhuModifierKeys + if eventString in cthulhuMods: + self._capturedKey = ['', keybindings.CTHULHU_MODIFIER_MASK, 0] + return False + + modifierKeys = ['Alt_L', 'Alt_R', 'Control_L', 'Control_R', + 'Shift_L', 'Shift_R', 'Meta_L', 'Meta_R', + 'Num_Lock', 'Caps_Lock', 'Shift_Lock'] + if eventString in modifierKeys: + return False + + eventState = eventState & Gtk.accelerator_get_default_mod_mask() + if not self._capturedKey \ + or eventString in ['Return', 'Escape']: + self._capturedKey = [eventString, eventState, 1] + return True + + string, modifiers, clickCount = self._capturedKey + isCthulhuModifier = modifiers & keybindings.CTHULHU_MODIFIER_MASK + if isCthulhuModifier: + eventState |= keybindings.CTHULHU_MODIFIER_MASK + self._capturedKey = [eventString, eventState, clickCount + 1] + + return True + + def kbKeyPressed(self, editable, event): + """Special handler for the key_pressed events when editing the + keybindings. This lets us control what gets inserted into the + entry. + """ + + keyProcessed = self._processKeyCaptured(event) + if not keyProcessed: + return True + + if not self._capturedKey: + return False + + keyName, modifiers, clickCount = self._capturedKey + if not keyName or keyName in ["Return", "Escape"]: + return False + + isCthulhuModifier = modifiers & keybindings.CTHULHU_MODIFIER_MASK + if keyName in ["Delete", "BackSpace"] and not isCthulhuModifier: + editable.set_text("") + self._presentMessage(messages.KB_DELETED) + self._capturedKey = [] + self.newBinding = None + return True + + self.newBinding = keybindings.KeyBinding(keyName, + keybindings.defaultModifierMask, + modifiers, + None, + clickCount) + modifierNames = keybindings.getModifierNames(modifiers) + clickCountString = self._clickCountToString(clickCount) + newString = modifierNames + keyName + clickCountString + description = self.pendingKeyBindings.get(newString) + + if description is None: + + def match(x): + return x.keysymstring == keyName and x.modifiers == modifiers \ + and x.click_count == clickCount and x.handler + + matches = list(filter(match, self.kbindings.keyBindings)) + if matches: + description = matches[0].handler.description + + if description: + msg = messages.KB_ALREADY_BOUND % description + delay = int(1000 * settings.doubleClickTimeout) + GLib.timeout_add(delay, self._presentMessage, msg) + else: + msg = messages.KB_CAPTURED % newString + editable.set_text(newString) + self._presentMessage(msg) + + return True + + def editedKey(self, cell, path, new_text, treeModel, + modMask, modUsed, key, click_count, text): + """The user changed the key for a Keybinding: update the model of + the treeview. + """ + + cthulhu_state.capturingKeys = False + self._capturedKey = [] + myiter = treeModel.get_iter_from_string(path) + try: + originalBinding = treeModel.get_value(myiter, text) + except Exception: + originalBinding = '' + modified = (originalBinding != new_text) + + try: + string = self.newBinding.keysymstring + mods = self.newBinding.modifiers + clickCount = self.newBinding.click_count + except Exception: + string = '' + mods = 0 + clickCount = 1 + + mods = mods & Gdk.ModifierType.MODIFIER_MASK + if mods & (1 << Atspi.ModifierType.SHIFTLOCK) \ + and mods & keybindings.CTHULHU_MODIFIER_MASK: + mods ^= (1 << Atspi.ModifierType.SHIFTLOCK) + + treeModel.set(myiter, + modMask, str(keybindings.defaultModifierMask), + modUsed, str(int(mods)), + key, string, + text, new_text, + click_count, str(clickCount), + MODIF, modified) + speech.stop() + if new_text: + message = messages.KB_CAPTURED_CONFIRMATION % new_text + description = treeModel.get_value(myiter, DESCRIP) + self.pendingKeyBindings[new_text] = description + else: + message = messages.KB_DELETED_CONFIRMATION + + if modified: + self._presentMessage(message) + self.pendingKeyBindings[originalBinding] = "" + + return + + def presentToolTipsChecked(self, widget): + """Signal handler for the "toggled" signal for the + presentToolTipsCheckButton GtkCheckButton widget. + The user has [un]checked the 'Present ToolTips' + checkbox. Set the 'presentToolTips' + preference to the new value if the user can present tooltips. + + Arguments: + - widget: the component that generated the signal. + """ + + self.prefsDict["presentToolTips"] = widget.get_active() + + def keyboardLayoutChanged(self, widget): + """Signal handler for the "toggled" signal for the generalDesktopButton, + or generalLaptopButton GtkRadioButton widgets. The user has + toggled the keyboard layout value. If this signal was + generated as the result of a radio button getting selected + (as opposed to a radio button losing the selection), set the + 'keyboardLayout' preference to the new value. Also set the + matching list of Cthulhu modifier keys + + Arguments: + - widget: the component that generated the signal. + """ + + if widget.get_active(): + if widget.get_label() == guilabels.KEYBOARD_LAYOUT_DESKTOP: + self.prefsDict["keyboardLayout"] = \ + settings.GENERAL_KEYBOARD_LAYOUT_DESKTOP + self.prefsDict["cthulhuModifierKeys"] = \ + settings.DESKTOP_MODIFIER_KEYS + else: + self.prefsDict["keyboardLayout"] = \ + settings.GENERAL_KEYBOARD_LAYOUT_LAPTOP + self.prefsDict["cthulhuModifierKeys"] = \ + settings.LAPTOP_MODIFIER_KEYS + + def pronunciationAddButtonClicked(self, widget): + """Signal handler for the "clicked" signal for the + pronunciationAddButton GtkButton widget. The user has clicked + the Add button on the Pronunciation pane. A new row will be + added to the end of the pronunciation dictionary list. Both the + actual and replacement strings will initially be set to an empty + string. Focus will be moved to that row. + + Arguments: + - widget: the component that generated the signal. + """ + + model = self.pronunciationView.get_model() + thisIter = model.append() + model.set(thisIter, ACTUAL, "", REPLACEMENT, "") + path = model.get_path(thisIter) + col = self.pronunciationView.get_column(0) + self.pronunciationView.grab_focus() + self.pronunciationView.set_cursor(path, col, True) + + def pronunciationDeleteButtonClicked(self, widget): + """Signal handler for the "clicked" signal for the + pronunciationDeleteButton GtkButton widget. The user has clicked + the Delete button on the Pronunciation pane. The row in the + pronunciation dictionary list with focus will be deleted. + + Arguments: + - widget: the component that generated the signal. + """ + + model, oldIter = self.pronunciationView.get_selection().get_selected() + model.remove(oldIter) + + def textSelectAllButtonClicked(self, widget): + """Signal handler for the "clicked" signal for the + textSelectAllButton GtkButton widget. The user has clicked + the Speak all button. Check all the text attributes and + then update the "enabledSpokenTextAttributes" and + "enabledBrailledTextAttributes" preference strings. + + Arguments: + - widget: the component that generated the signal. + """ + + attributes = _settingsManager.getSetting('allTextAttributes') + self._setSpokenTextAttributes( + self.getTextAttributesView, attributes, True) + self._setBrailledTextAttributes( + self.getTextAttributesView, attributes, True) + self._updateTextDictEntry() + + def textUnselectAllButtonClicked(self, widget): + """Signal handler for the "clicked" signal for the + textUnselectAllButton GtkButton widget. The user has clicked + the Speak none button. Uncheck all the text attributes and + then update the "enabledSpokenTextAttributes" and + "enabledBrailledTextAttributes" preference strings. + + Arguments: + - widget: the component that generated the signal. + """ + + attributes = _settingsManager.getSetting('allTextAttributes') + self._setSpokenTextAttributes( + self.getTextAttributesView, attributes, False) + self._setBrailledTextAttributes( + self.getTextAttributesView, attributes, False) + self._updateTextDictEntry() + + def textResetButtonClicked(self, widget): + """Signal handler for the "clicked" signal for the + textResetButton GtkButton widget. The user has clicked + the Reset button. Reset all the text attributes to their + initial state and then update the "enabledSpokenTextAttributes" + and "enabledBrailledTextAttributes" preference strings. + + Arguments: + - widget: the component that generated the signal. + """ + + attributes = _settingsManager.getSetting('allTextAttributes') + self._setSpokenTextAttributes( + self.getTextAttributesView, attributes, False) + self._setBrailledTextAttributes( + self.getTextAttributesView, attributes, False) + + attributes = _settingsManager.getSetting('enabledSpokenTextAttributes') + self._setSpokenTextAttributes( + self.getTextAttributesView, attributes, True) + + attributes = \ + _settingsManager.getSetting('enabledBrailledTextAttributes') + self._setBrailledTextAttributes( + self.getTextAttributesView, attributes, True) + + self._updateTextDictEntry() + + def textMoveToTopButtonClicked(self, widget): + """Signal handler for the "clicked" signal for the + textMoveToTopButton GtkButton widget. The user has clicked + the Move to top button. Move the selected rows in the text + attribute view to the very top of the list and then update + the "enabledSpokenTextAttributes" and "enabledBrailledTextAttributes" + preference strings. + + Arguments: + - widget: the component that generated the signal. + """ + + textSelection = self.getTextAttributesView.get_selection() + [model, paths] = textSelection.get_selected_rows() + for path in paths: + thisIter = model.get_iter(path) + model.move_after(thisIter, None) + self._updateTextDictEntry() + + def textMoveUpOneButtonClicked(self, widget): + """Signal handler for the "clicked" signal for the + textMoveUpOneButton GtkButton widget. The user has clicked + the Move up one button. Move the selected rows in the text + attribute view up one row in the list and then update the + "enabledSpokenTextAttributes" and "enabledBrailledTextAttributes" + preference strings. + + Arguments: + - widget: the component that generated the signal. + """ + + textSelection = self.getTextAttributesView.get_selection() + [model, paths] = textSelection.get_selected_rows() + for path in paths: + thisIter = model.get_iter(path) + indices = path.get_indices() + if indices[0]: + otherIter = model.iter_nth_child(None, indices[0]-1) + model.swap(thisIter, otherIter) + self._updateTextDictEntry() + + def textMoveDownOneButtonClicked(self, widget): + """Signal handler for the "clicked" signal for the + textMoveDownOneButton GtkButton widget. The user has clicked + the Move down one button. Move the selected rows in the text + attribute view down one row in the list and then update the + "enabledSpokenTextAttributes" and "enabledBrailledTextAttributes" + preference strings. + + Arguments: + - widget: the component that generated the signal. + """ + + textSelection = self.getTextAttributesView.get_selection() + [model, paths] = textSelection.get_selected_rows() + noRows = model.iter_n_children(None) + for path in paths: + thisIter = model.get_iter(path) + indices = path.get_indices() + if indices[0] < noRows-1: + otherIter = model.iter_next(thisIter) + model.swap(thisIter, otherIter) + self._updateTextDictEntry() + + def textMoveToBottomButtonClicked(self, widget): + """Signal handler for the "clicked" signal for the + textMoveToBottomButton GtkButton widget. The user has clicked + the Move to bottom button. Move the selected rows in the text + attribute view to the bottom of the list and then update the + "enabledSpokenTextAttributes" and "enabledBrailledTextAttributes" + preference strings. + + Arguments: + - widget: the component that generated the signal. + """ + + textSelection = self.getTextAttributesView.get_selection() + [model, paths] = textSelection.get_selected_rows() + for path in paths: + thisIter = model.get_iter(path) + model.move_before(thisIter, None) + self._updateTextDictEntry() + + def helpButtonClicked(self, widget): + """Signal handler for the "clicked" signal for the helpButton + GtkButton widget. The user has clicked the Help button. + + Arguments: + - widget: the component that generated the signal. + """ + + self.script.learnModePresenter.show_help(page="preferences") + + def restoreSettings(self): + """Restore the settings we saved away when opening the preferences + dialog.""" + # Restore the default rate/pitch/gain, + # in case the user played with the sliders. + # + voices = _settingsManager.getSetting('voices') + defaultVoice = voices[settings.DEFAULT_VOICE] + defaultVoice[acss.ACSS.GAIN] = self.savedGain + defaultVoice[acss.ACSS.AVERAGE_PITCH] = self.savedPitch + defaultVoice[acss.ACSS.RATE] = self.savedRate + + def saveBasicSettings(self): + if not self._isInitialSetup: + self.restoreSettings() + + enable = self.get_widget("speechSupportCheckButton").get_active() + self.prefsDict["enableSpeech"] = enable + + if self.speechSystemsChoice: + self.prefsDict["speechServerFactory"] = \ + self.speechSystemsChoice.__name__ + + if self.speechServersChoice: + self.prefsDict["speechServerInfo"] = \ + self.speechServersChoice.getInfo() + + if self.defaultVoice is not None: + self.prefsDict["voices"] = { + settings.DEFAULT_VOICE: acss.ACSS(self.defaultVoice), + settings.UPPERCASE_VOICE: acss.ACSS(self.uppercaseVoice), + settings.HYPERLINK_VOICE: acss.ACSS(self.hyperlinkVoice), + settings.SYSTEM_VOICE: acss.ACSS(self.systemVoice), + } + + def applyButtonClicked(self, widget): + """Signal handler for the "clicked" signal for the applyButton + GtkButton widget. The user has clicked the Apply button. + Write out the users preferences. If GNOME accessibility hadn't + previously been enabled, warn the user that they will need to + log out. Shut down any active speech servers that were started. + Reload the users preferences to get the new speech, braille and + key echo value to take effect. Do not dismiss the configuration + window. + + Arguments: + - widget: the component that generated the signal. + """ + + msg = "PREFERENCES DIALOG: Apply button clicked" + debug.println(debug.LEVEL_ALL, msg, True) + + self.saveBasicSettings() + activeProfile = self.getComboBoxList(self.profilesCombo) + startingProfile = self.getComboBoxList(self.startingProfileCombo) + + self.prefsDict['profile'] = activeProfile + self.prefsDict['activeProfile'] = activeProfile + self.prefsDict['startingProfile'] = startingProfile + _settingsManager.setStartingProfile(startingProfile) + + self.writeUserPreferences() + self.app.getDynamicApiManager().getAPI('LoadUserSettings')(self.script) + braille.checkBrailleSetting() + self._initSpeechState() + self._populateKeyBindings() + self.__initProfileCombo() + + msg = "PREFERENCES DIALOG: Handling Apply button click complete" + debug.println(debug.LEVEL_ALL, msg, True) + + def cancelButtonClicked(self, widget): + """Signal handler for the "clicked" signal for the cancelButton + GtkButton widget. The user has clicked the Cancel button. + Don't write out the preferences. Destroy the configuration window. + + Arguments: + - widget: the component that generated the signal. + """ + + msg = "PREFERENCES DIALOG: Cancel button clicked" + debug.println(debug.LEVEL_ALL, msg, True) + + self.windowClosed(widget) + self.get_widget("cthulhuSetupWindow").destroy() + + msg = "PREFERENCES DIALOG: Handling Cancel button click complete" + debug.println(debug.LEVEL_ALL, msg, True) + + def okButtonClicked(self, widget=None): + """Signal handler for the "clicked" signal for the okButton + GtkButton widget. The user has clicked the OK button. + Write out the users preferences. If GNOME accessibility hadn't + previously been enabled, warn the user that they will need to + log out. Shut down any active speech servers that were started. + Reload the users preferences to get the new speech, braille and + key echo value to take effect. Hide the configuration window. + + Arguments: + - widget: the component that generated the signal. + """ + + msg = "PREFERENCES DIALOG: OK button clicked" + debug.println(debug.LEVEL_ALL, msg, True) + + self.applyButtonClicked(widget) + self._cleanupSpeechServers() + self.get_widget("cthulhuSetupWindow").destroy() + + msg = "PREFERENCES DIALOG: Handling OK button click complete" + debug.println(debug.LEVEL_ALL, msg, True) + + def windowClosed(self, widget): + """Signal handler for the "closed" signal for the cthulhuSetupWindow + GtkWindow widget. This is effectively the same as pressing the + cancel button, except the window is destroyed for us. + + Arguments: + - widget: the component that generated the signal. + """ + + msg = "PREFERENCES DIALOG: Window is being closed" + debug.println(debug.LEVEL_ALL, msg, True) + + self.suspendEvents() + + factory = _settingsManager.getSetting('speechServerFactory') + if factory: + self._setSpeechSystemsChoice(factory) + + server = _settingsManager.getSetting('speechServerInfo') + if server: + self._setSpeechServersChoice(server) + + self._cleanupSpeechServers() + self.restoreSettings() + + GObject.timeout_add(1000, self.resumeEvents) + + msg = "PREFERENCES DIALOG: Window closure complete" + debug.println(debug.LEVEL_ALL, msg, True) + + def windowDestroyed(self, widget): + """Signal handler for the "destroyed" signal for the cthulhuSetupWindow + GtkWindow widget. Reset cthulhu_state.cthulhuOS to None, so that the + GUI can be rebuilt from the GtkBuilder file the next time the user + wants to display the configuration GUI. + + Arguments: + - widget: the component that generated the signal. + """ + + msg = "PREFERENCES DIALOG: Window is being destroyed" + debug.println(debug.LEVEL_ALL, msg, True) + + self.keyBindView.set_model(None) + self.getTextAttributesView.set_model(None) + self.pronunciationView.set_model(None) + self.keyBindView.set_headers_visible(False) + self.getTextAttributesView.set_headers_visible(False) + self.pronunciationView.set_headers_visible(False) + self.keyBindView.hide() + self.getTextAttributesView.hide() + self.pronunciationView.hide() + cthulhu_state.cthulhuOS = None + + msg = "PREFERENCES DIALOG: Window destruction complete" + debug.println(debug.LEVEL_ALL, msg, True) + + def resumeEvents(self): + msg = "PREFERENCES DIALOG: Re-registering floody events." + debug.println(debug.LEVEL_ALL, msg, True) + + manager = event_manager.getManager() + manager.registerListener("object:state-changed:showing") + manager.registerListener("object:children-changed:remove") + manager.registerListener("object:selection-changed") + manager.registerListener("object:property-change:accessible-name") + + def suspendEvents(self): + msg = "PREFERENCES DIALOG: Deregistering floody events." + debug.println(debug.LEVEL_ALL, msg, True) + + manager = event_manager.getManager() + manager.deregisterListener("object:state-changed:showing") + manager.deregisterListener("object:children-changed:remove") + manager.deregisterListener("object:selection-changed") + manager.deregisterListener("object:property-change:accessible-name") + + def showProfileGUI(self, widget): + """Show profile Dialog to add a new one""" + cthulhu_gui_profile.setApp(self.app) + cthulhu_gui_profile.showProfileUI(self) + + def saveProfile(self, profileToSaveLabel): + """Creates a new profile based on the name profileToSaveLabel and + updates the Preferences dialog combo boxes accordingly.""" + + if not profileToSaveLabel: + return + profileToSave = profileToSaveLabel.replace(' ', '_').lower() + profile = [profileToSaveLabel, profileToSave] + + def saveActiveProfile(newProfile = True): + if newProfile: + activeProfileIter = self.profilesComboModel.append(profile) + self.profilesCombo.set_active_iter(activeProfileIter) + + self.prefsDict['profile'] = profile + self.prefsDict['activeProfile'] = profile + self.saveBasicSettings() + self.writeUserPreferences() + + availableProfiles = [p[1] for p in self.__getAvailableProfiles()] + if isinstance(profileToSave, str) \ + and profileToSave != '' \ + and profileToSave not in availableProfiles \ + and profileToSave != self._defaultProfile[1]: + saveActiveProfile() + else: + if profileToSave is not None: + message = guilabels.PROFILE_CONFLICT_MESSAGE % \ + f"{GLib.markup_escape_text(profileToSaveLabel)}" + dialog = Gtk.MessageDialog(None, + Gtk.DialogFlags.MODAL, + type=Gtk.MessageType.INFO, + buttons=Gtk.ButtonsType.YES_NO) + dialog.set_markup(f"{guilabels.PROFILE_CONFLICT_LABEL}") + dialog.format_secondary_markup(message) + dialog.set_title(guilabels.PROFILE_CONFLICT_TITLE) + response = dialog.run() + if response == Gtk.ResponseType.YES: + dialog.destroy() + saveActiveProfile(False) + else: + dialog.destroy() + + + def removeProfileButtonClicked(self, widget): + """Remove profile button clicked handler + + If we removed the last profile, a default one will automatically get + added back by the settings manager. + """ + + oldProfile = self.getComboBoxList(self.profilesCombo) + + message = guilabels.PROFILE_REMOVE_MESSAGE % \ + f"{GLib.markup_escape_text(oldProfile[0])}" + dialog = Gtk.MessageDialog(self.window, Gtk.DialogFlags.MODAL, + type=Gtk.MessageType.INFO, + buttons=Gtk.ButtonsType.YES_NO) + dialog.set_markup(f"{guilabels.PROFILE_REMOVE_LABEL}") + dialog.format_secondary_markup(message) + if dialog.run() == Gtk.ResponseType.YES: + # If we remove the currently used starting profile, fallback on + # the first listed profile, or the default one if there's + # nothing better + newStartingProfile = self.prefsDict.get('startingProfile') + if not newStartingProfile or newStartingProfile == oldProfile: + newStartingProfile = self._defaultProfile + for row in self.profilesComboModel: + rowProfile = row[:] + if rowProfile != oldProfile: + newStartingProfile = rowProfile + break + # Update the current profile to the active profile unless we're + # removing that one, in which case we use the new starting + # profile + newProfile = self.prefsDict.get('activeProfile') + if not newProfile or newProfile == oldProfile: + newProfile = newStartingProfile + + _settingsManager.removeProfile(oldProfile[1]) + self.loadProfile(newProfile) + + # Make sure nothing is referencing the removed profile anymore + startingProfile = self.prefsDict.get('startingProfile') + if not startingProfile or startingProfile == oldProfile: + self.prefsDict['startingProfile'] = newStartingProfile + _settingsManager.setStartingProfile(newStartingProfile) + self.writeUserPreferences() + + dialog.destroy() + + def loadProfileButtonClicked(self, widget): + """Load profile button clicked handler""" + + if self._isInitialSetup: + return + + dialog = Gtk.MessageDialog(None, + Gtk.DialogFlags.MODAL, + type=Gtk.MessageType.INFO, + buttons=Gtk.ButtonsType.YES_NO) + + dialog.set_markup(f"{guilabels.PROFILE_LOAD_LABEL}") + dialog.format_secondary_markup(guilabels.PROFILE_LOAD_MESSAGE) + response = dialog.run() + if response == Gtk.ResponseType.YES: + dialog.destroy() + self.loadSelectedProfile() + else: + dialog.destroy() + + def loadSelectedProfile(self): + """Load selected profile""" + + activeProfile = self.getComboBoxList(self.profilesCombo) + self.loadProfile(activeProfile) + + def loadProfile(self, profile): + """Load profile""" + + self.saveBasicSettings() + + self.prefsDict['activeProfile'] = profile + _settingsManager.setProfile(profile[1]) + self.prefsDict = _settingsManager.getGeneralSettings(profile[1]) + + self.app.getDynamicApiManager().getAPI('LoadUserSettings')(skipReloadMessage=True) + + self._initGUIState() + + braille.checkBrailleSetting() + + self._initSpeechState() + + self._populateKeyBindings() + + self.__initProfileCombo() + diff --git a/src/cthulhu/plugins/ClassicPreferences/cthulhu_gui_profile.py b/src/cthulhu/plugins/ClassicPreferences/cthulhu_gui_profile.py new file mode 100644 index 0000000..72c34d1 --- /dev/null +++ b/src/cthulhu/plugins/ClassicPreferences/cthulhu_gui_profile.py @@ -0,0 +1,163 @@ +# Cthulhu +# +# Copyright 2010 Consorcio Fernando de los Rios. +# Author: Javier Hernandez Antunez +# Author: Alejandro Leiva +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., Franklin Street, Fifth Floor, +# Boston MA 02110-1301 USA. + +"""Displays the Save Profile As dialog.""" + +__id__ = "$Id$" +__version__ = "$Revision$" +__date__ = "$Date$" +__copyright__ = "Copyright (c) 2010 Consorcio Fernando de los Rios." +__license__ = "LGPL" + +import locale +import sys +from gi.repository import Gtk + +cthulhu_state = None +guilabels = None + +OS = None +newProfile = None + +app = None + +class CthulhuProfileGUI(Gtk.Dialog): + + def __init__(self, app): + """Initialize the Cthulhu profile configuration GUI.""" + self.app = app + global guilabels + global cthulhu_state + + guilabels = self.app.getDynamicApiManager().getAPI('GuiLabels') + cthulhu_state = self.app.getDynamicApiManager().getAPI('CthulhuState') + + Gtk.Dialog.__init__(self) + self.set_title(guilabels.PROFILE_SAVE_AS_TITLE) + self.set_has_resize_grip(False) + + self.add_button('gtk-cancel', Gtk.ResponseType.CANCEL) + self.add_button('gtk-save', Gtk.ResponseType.ACCEPT) + + grid = Gtk.Grid() + grid.set_property('margin', 12) + grid.set_row_spacing(10) + grid.set_column_spacing(10) + + # Right now the content area is a GtkBox. We'll need to update + # this once GtkBox is fully deprecated. + contentArea = self.get_content_area() + contentArea.pack_start(grid, True, True, 0) + + self.profileEntry = Gtk.Entry() + self.profileEntry.set_property('hexpand', True) + self.profileEntry.set_activates_default(True) + grid.attach(self.profileEntry, 1, 0, 1, 1) + + label = Gtk.Label(guilabels.PROFILE_NAME_LABEL) + label.set_use_underline(True) + label.set_mnemonic_widget(self.profileEntry) + grid.attach(label, 0, 0, 1, 1) + + defaultButton = self.get_widget_for_response(Gtk.ResponseType.ACCEPT) + defaultButton.set_property('can-default', True) + defaultButton.set_property('has-default', True) + + self.connect('response', self.onResponse) + self.connect('destroy', self.onDestroy) + + self.searchString = None + self.profileString = None + self.prefsDialog = None + self.translationContext = None + + def init(self): + self.profileString = '' + + def showGUI(self, prefsDialog): + """Show the Save Profile As dialog.""" + + self.show_all() + self.prefsDialog = prefsDialog + self.profileEntry.set_text(self.profileString) + ts = 0 + try: + ts = cthulhu_state.lastInputEvent.timestamp + except: + pass + if ts == 0: + ts = Gtk.get_current_event_time() + self.present_with_time(ts) + + def onResponse(self, widget, response): + """Signal handler for the responses emitted by the dialog.""" + + if response in [Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT]: + self.hide() + return + + if response == Gtk.ResponseType.ACCEPT: + global newProfile + + newProfile = self.profileEntry.get_text() + if newProfile: + self.destroy() + if self.prefsDialog: + self.prefsDialog.saveProfile(newProfile) + + def onDestroy(self, widget): + """Signal handler for the 'destroy' signal of the dialog.""" + + global OS + + OS = None + +def setTranslationContext(newTranslationContext): + global _, translationContext + translationContext = newTranslationContext + _ = newTranslationContext.gettext + +def setApp(newApp): + global app + app = newApp + +def showProfileUI(prefsDialog=None): + global OS + global newProfile + + newProfile = None + + if not OS: + OS = CthulhuProfileGUI(app) + OS.init() + + OS.showGUI(prefsDialog) + +def main(): + locale.setlocale(locale.LC_ALL, '') + + showProfileUI() + + Gtk.main() + sys.exit(0) + +if __name__ == "__main__": + main()