9 Commits

Author SHA1 Message Date
Storm Dragon c4890f31af Fix 2026.03.02 release versioning 2026-03-02 17:56:45 -05:00
Storm Dragon c44245189a Merge branch 'testing' 2026-03-02 15:42:15 -05:00
Storm Dragon c7b8e4a30d Clean stale autostart source references from translations 2026-03-02 15:42:04 -05:00
Storm Dragon 20fe1a7e30 Remove autogenerated autostart desktop template 2026-03-02 15:38:29 -05:00
Storm Dragon 25931758f7 Merge branch 'testing'
# Conflicts:
#	distro-packages/Arch-Linux/PKGBUILD
#	meson.build
#	src/cthulhu/cthulhuVersion.py
2026-03-02 15:37:50 -05:00
Storm Dragon daf57ef22c Removed the autogenerated autostart desktop file. Moved the desktop file to contrib so it's still available if people want it. 2026-02-25 23:20:05 -05:00
Storm Dragon 1d2d727fa2 Requested feature added. It is now possible to customize speech used for echo settings including speech module, voice, rate, volume, pitch. 2026-02-22 01:44:19 -05:00
Storm Dragon 11bd7107d2 Attempt to stop plugins from spontaneously re-enabling themselves. 2026-02-19 02:40:46 -05:00
Storm Dragon 95d33047fa Fix prefs state churn and sound-only role speech
Some settings were being reset when tab changed and a key was pressed, e.g. control.

Radio buttons were still announced even with sound only set.

Controls for AI assistant tab were vanishing after being set.
2026-02-18 07:03:12 -05:00
101 changed files with 1128 additions and 273 deletions
-8
View File
@@ -8,13 +8,11 @@ set -e # Exit on error
# Colors for output (only if stdout is a terminal) # Colors for output (only if stdout is a terminal)
if [[ -t 1 ]]; then if [[ -t 1 ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m' GREEN='\033[0;32m'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
BLUE='\033[0;34m' BLUE='\033[0;34m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
else else
RED=''
GREEN='' GREEN=''
YELLOW='' YELLOW=''
BLUE='' BLUE=''
@@ -56,12 +54,6 @@ if [[ $REPLY =~ ^[Yy]$ ]]; then
# Remove data files # Remove data files
# Remove desktop files
if [[ -f "$HOME/.local/share/applications/cthulhu-autostart.desktop" ]]; then
rm -f "$HOME/.local/share/applications/cthulhu-autostart.desktop"
echo " Removed: ~/.local/share/applications/cthulhu-autostart.desktop"
fi
# Remove icons # Remove icons
for size in 16x16 22x22 24x24 32x32 48x48 256x256 scalable symbolic; do for size in 16x16 22x22 24x24 32x32 48x48 256x256 scalable symbolic; do
icon_path="$HOME/.local/share/icons/hicolor/$size/apps" icon_path="$HOME/.local/share/icons/hicolor/$size/apps"
-11
View File
@@ -1,11 +0,0 @@
[Desktop Entry]
Type=Application
Name=Cthulhu Screen Reader
Exec=cthulhu
NoDisplay=true
# Desktop-neutral autostart - no GNOME-specific conditions
# Users can enable/disable via their desktop environment's accessibility settings
# or by adding/removing this file from ~/.config/autostart/
X-GNOME-AutoRestart=true
Categories=Accessibility;
Keywords=screen;reader;accessibility;speech;braille;
+2 -7
View File
@@ -1,7 +1,7 @@
# Maintainer: Storm Dragon <storm_dragon@stormux.org> # Maintainer: Storm Dragon <storm_dragon@stormux.org>
pkgname=cthulhu pkgname=cthulhu
pkgver=2026.02.18 pkgver=2026.03.02
pkgrel=1 pkgrel=1
pkgdesc="Desktop-agnostic screen reader with plugin system, forked from Orca" pkgdesc="Desktop-agnostic screen reader with plugin system, forked from Orca"
url="https://git.stormux.org/storm/cthulhu" url="https://git.stormux.org/storm/cthulhu"
@@ -84,7 +84,7 @@ makedepends=(
) )
install=cthulhu.install install=cthulhu.install
source=( source=(
"git+https://git.stormux.org/storm/cthulhu.git" "git+https://git.stormux.org/storm/cthulhu.git#tag=${pkgver}"
"cthulhu.install" "cthulhu.install"
) )
b2sums=( b2sums=(
@@ -92,11 +92,6 @@ b2sums=(
'SKIP' 'SKIP'
) )
pkgver() {
cd cthulhu
grep "^version = " src/cthulhu/cthulhuVersion.py | sed 's/version = "\(.*\)"/\1/'
}
build() { build() {
cd cthulhu cd cthulhu
arch-meson _build arch-meson _build
+1 -11
View File
@@ -1,5 +1,5 @@
project('cthulhu', project('cthulhu',
version: '2026.02.18-master', version: '2026.03.02-master',
meson_version: '>= 1.0.0', meson_version: '>= 1.0.0',
) )
@@ -93,16 +93,6 @@ else
summary += {'sound support': 'no (missing gstreamer)'} summary += {'sound support': 'no (missing gstreamer)'}
endif endif
# Integration with session startup
i18n.merge_file(
input: 'cthulhu-autostart.desktop.in',
output: '@BASENAME@',
type: 'desktop',
po_dir: meson.project_source_root() / 'po',
install: true,
install_dir: get_option('sysconfdir') / 'xdg' / 'autostart',
)
# Update icon cache manually (desktop-neutral) - optional, ignore failures # Update icon cache manually (desktop-neutral) - optional, ignore failures
gtk_update_icon_cache = find_program('gtk4-update-icon-cache', required: false) gtk_update_icon_cache = find_program('gtk4-update-icon-cache', required: false)
if gtk_update_icon_cache.found() if gtk_update_icon_cache.found()
-1
View File
@@ -1,6 +1,5 @@
# List of source files containing translatable strings. # List of source files containing translatable strings.
# Please keep this file sorted alphabetically. # Please keep this file sorted alphabetically.
cthulhu-autostart.desktop.in
src/cthulhu/braille_rolenames.py src/cthulhu/braille_rolenames.py
src/cthulhu/brltablenames.py src/cthulhu/brltablenames.py
src/cthulhu/chnames.py src/cthulhu/chnames.py
-2
View File
@@ -13,11 +13,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-DamnedLies-Scope: partial\n" "X-DamnedLies-Scope: partial\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "" msgstr ""
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.6.10\n" "X-Generator: Poedit 1.6.10\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lector de pantalla Cthulhu" msgstr "Lector de pantalla Cthulhu"
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "lector de pantalla; fabla; braille;" msgstr "lector de pantalla; fabla; braille;"
-2
View File
@@ -24,11 +24,9 @@ msgstr ""
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" "&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
"X-Poedit-Language: Arabic\n" "X-Poedit-Language: Arabic\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "قارئ الشاشة Cthulhu" msgstr "قارئ الشاشة Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "شاشة;قارئ;إمكانية الوصول;كلام;برايل;" msgstr "شاشة;قارئ;إمكانية الوصول;كلام;برايل;"
-2
View File
@@ -19,11 +19,9 @@ msgstr ""
"X-Poedit-Language: Asturian\n" "X-Poedit-Language: Asturian\n"
"X-Poedit-SourceCharset: utf-8\n" "X-Poedit-SourceCharset: utf-8\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Llector de pantalla d'Cthulhu" msgstr "Llector de pantalla d'Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -17,11 +17,9 @@ msgstr ""
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Чытач з экрана Cthulhu" msgstr "Чытач з экрана Cthulhu"
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "чытач з экрана;экран;голас;маўленне;Брайль;" msgstr "чытач з экрана;экран;голас;маўленне;Брайль;"
-2
View File
@@ -23,11 +23,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu — екранен четец" msgstr "Cthulhu — екранен четец"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "екран;четец;достъпност;говор;брайл;" msgstr "екран;четец;достъпност;говор;брайл;"
-2
View File
@@ -21,12 +21,10 @@ msgstr ""
"X-Generator: KBabel 1.9.1\n" "X-Generator: KBabel 1.9.1\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "অর্কা স্ক্রীন রিডার / বিবর্ধন" msgstr "অর্কা স্ক্রীন রিডার / বিবর্ধন"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,12 +18,10 @@ msgstr ""
"X-Generator: KBabel 1.9.1\n" "X-Generator: KBabel 1.9.1\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu'র বৈশিষ্ট্য" msgstr "Cthulhu'র বৈশিষ্ট্য"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -15,11 +15,9 @@ msgstr ""
"X-Launchpad-Export-Date: 2015-02-05 06:46+0000\n" "X-Launchpad-Export-Date: 2015-02-05 06:46+0000\n"
"X-Generator: Poedit 1.7.4\n" "X-Generator: Poedit 1.7.4\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu čitač ekrana" msgstr "Cthulhu čitač ekrana"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "čitač ekrana;govor;brailleovo pismo;" msgstr "čitač ekrana;govor;brailleovo pismo;"
-2
View File
@@ -27,11 +27,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lector de pantalla Cthulhu" msgstr "Lector de pantalla Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "pantalla;lector;accessibilitat;veu;braille;" msgstr "pantalla;lector;accessibilitat;veu;braille;"
-2
View File
@@ -27,11 +27,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Poedit 1.8.11\n" "X-Generator: Poedit 1.8.11\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lector de pantalla Cthulhu" msgstr "Lector de pantalla Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "lector de pantalla;pronunciació;braille;" msgstr "lector de pantalla;pronunciació;braille;"
-2
View File
@@ -19,12 +19,10 @@ msgstr ""
"X-Launchpad-Export-Date: 2020-05-04 21:32+0000\n" "X-Launchpad-Export-Date: 2020-05-04 21:32+0000\n"
"X-Generator: Poedit 2.3\n" "X-Generator: Poedit 2.3\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "خوێنەری پەردەی ئۆرکا" msgstr "خوێنەری پەردەی ئۆرکا"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -24,11 +24,9 @@ msgstr ""
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Čtečka obrazovky Cthulhu" msgstr "Čtečka obrazovky Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "obrazovka;čtečka;přístupnost;řeč;braille;" msgstr "obrazovka;čtečka;přístupnost;řeč;braille;"
-2
View File
@@ -17,12 +17,10 @@ msgstr ""
"Plural-Forms: nplurals=4; plural= (n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != " "Plural-Forms: nplurals=4; plural= (n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != "
"11) ? 2 : 3;\n" "11) ? 2 : 3;\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Hoffterau Cthulhu" msgstr "Hoffterau Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -32,11 +32,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.0.1\n" "X-Generator: Poedit 3.0.1\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Skærmlæseren Cthulhu" msgstr "Skærmlæseren Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "skærm;læser;tilgængelighed;tale;braille;" msgstr "skærm;læser;tilgængelighed;tale;braille;"
-2
View File
@@ -37,11 +37,9 @@ msgstr ""
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu-Bildschirmleser" msgstr "Cthulhu-Bildschirmleser"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "bildschirm;leser;barrierefreiheit;sprache;braille;" msgstr "bildschirm;leser;barrierefreiheit;sprache;braille;"
-2
View File
@@ -20,12 +20,10 @@ msgstr ""
"X-Poedit-SourceCharset: utf-8\n" "X-Poedit-SourceCharset: utf-8\n"
"Plural-Forms: nplurals=2; plural=(n!=1)\n" "Plural-Forms: nplurals=2; plural=(n!=1)\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "ཨོར་ཀ་གསལ་གཞི་ལྷག་བྱེད་/ཆེ་ཤེལ་" msgstr "ཨོར་ཀ་གསལ་གཞི་ལྷག་བྱེད་/ཆེ་ཤེལ་"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -28,11 +28,9 @@ msgstr ""
"X-Generator: Poedit 2.3\n" "X-Generator: Poedit 2.3\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Αναγνώστης οθόνης Cthulhu" msgstr "Αναγνώστης οθόνης Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "οθόνη;αναγνώστης;προσβασιμότητα;ομιλία;μπράιγ;" msgstr "οθόνη;αναγνώστης;προσβασιμότητα;ομιλία;μπράιγ;"
-2
View File
@@ -17,11 +17,9 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "" msgstr ""
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -21,11 +21,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Gtranslator 45.3\n" "X-Generator: Gtranslator 45.3\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu Screen Reader" msgstr "Cthulhu Screen Reader"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "screen reader;speech;braille;" msgstr "screen reader;speech;braille;"
-2
View File
@@ -21,11 +21,9 @@ msgstr ""
"X-Generator: Poedit 3.2.2\n" "X-Generator: Poedit 3.2.2\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Ekranlegilo Orko" msgstr "Ekranlegilo Orko"
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "ekranlegilo;parolo;brajlo;" msgstr "ekranlegilo;parolo;brajlo;"
-2
View File
@@ -28,11 +28,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1)\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n"
"X-Generator: Gtranslator 45.alpha0\n" "X-Generator: Gtranslator 45.alpha0\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lector de pantalla Cthulhu" msgstr "Lector de pantalla Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "lector;pantalla;accesibilidad;voz;braille;" msgstr "lector;pantalla;accesibilidad;voz;braille;"
-2
View File
@@ -20,12 +20,10 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu ekraanilugeja ja luup" msgstr "Cthulhu ekraanilugeja ja luup"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu pantaila-irakurlea" msgstr "Cthulhu pantaila-irakurlea"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "pantaila;irakurlea;irisgarritasuna;mintzamena;braillea;" msgstr "pantaila;irakurlea;irisgarritasuna;mintzamena;braillea;"
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.4\n" "X-Generator: Poedit 3.4\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "صفحه‌خوان اورکا" msgstr "صفحه‌خوان اورکا"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -27,11 +27,9 @@ msgstr ""
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
"X-Poedit-Bookmarks: 814,-1,-1,-1,-1,-1,-1,-1,-1,-1\n" "X-Poedit-Bookmarks: 814,-1,-1,-1,-1,-1,-1,-1,-1,-1\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu-näytönlukija" msgstr "Cthulhu-näytönlukija"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "näyttö;lukija;saavutettavuus;puhe;pistekirjoitus;" msgstr "näyttö;lukija;saavutettavuus;puhe;pistekirjoitus;"
-2
View File
@@ -31,11 +31,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lecteur d’écran Cthulhu" msgstr "Lecteur d’écran Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "lecteur;écran;accessibilité;parole;braille;" msgstr "lecteur;écran;accessibilité;parole;braille;"
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.0.3\n" "X-Generator: Poedit 2.0.3\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "" msgstr ""
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,12 +18,10 @@ msgstr ""
"Plural-Forms: nplurals=5; plural=n==1 ? 0 : (n%10==1 || n%10==2) ? 1 : " "Plural-Forms: nplurals=5; plural=n==1 ? 0 : (n%10==1 || n%10==2) ? 1 : "
"(n%10>=3 && n%10<= 6) ? 2 : ((n%10>=7 && n%10<=9) || n==10) ? 3 : 4;\n" "(n%10>=3 && n%10<= 6) ? 2 : ((n%10>=7 && n%10<=9) || n==10) ? 3 : 4;\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Léitheoir Scáileáin agus Formhéadaitheoir Cthulhu" msgstr "Léitheoir Scáileáin agus Formhéadaitheoir Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -34,11 +34,9 @@ msgstr ""
"X-DL-Domain: po\n" "X-DL-Domain: po\n"
"X-DL-State: Translating\n" "X-DL-State: Translating\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lector da pantalla Cthulhu" msgstr "Lector da pantalla Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "pantalla;lector;accesibilidade;fala;braille;" msgstr "pantalla;lector;accesibilidade;fala;braille;"
-2
View File
@@ -33,12 +33,10 @@ msgstr ""
"\n" "\n"
"\n" "\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu સ્ક્રીન વાંચક અને વિસ્તૃતકારક" msgstr "Cthulhu સ્ક્રીન વાંચક અને વિસ્તૃતકારક"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -14,11 +14,9 @@ msgstr ""
"2 : 3);\n" "2 : 3);\n"
"X-Generator: Poedit 3.4.1\n" "X-Generator: Poedit 3.4.1\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "מקריא המסך Cthulhu" msgstr "מקריא המסך Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "מסך;קורא;נגישות;דיבור;ברייל;" msgstr "מסך;קורא;נגישות;דיבור;ברייל;"
-2
View File
@@ -22,11 +22,9 @@ msgstr ""
"\n" "\n"
"X-Generator: Lokalize 1.5\n" "X-Generator: Lokalize 1.5\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu स्क्रीन वाचक" msgstr "Cthulhu स्क्रीन वाचक"
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "स्क्रीन रीडर;भाषण;ब्रेल;" msgstr "स्क्रीन रीडर;भाषण;ब्रेल;"
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"X-Launchpad-Export-Date: 2016-09-15 12:40+0000\n" "X-Launchpad-Export-Date: 2016-09-15 12:40+0000\n"
"X-Generator: Poedit 1.6.10\n" "X-Generator: Poedit 1.6.10\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu čitač zaslona" msgstr "Cthulhu čitač zaslona"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "čitač zaslona;govor;brajica;" msgstr "čitač zaslona;govor;brajica;"
-2
View File
@@ -22,11 +22,9 @@ msgstr ""
"X-Generator: Poedit 2.3\n" "X-Generator: Poedit 2.3\n"
# Megjegyzés: Mivel mindenhol régóta Gáborral egyeztetve az Cthulhu nevet magyarosan Orkaként írjuk a fordításban, így a fordítást megváltoztattam Orka képernyőolvasóra az Cthulhu képernyőolvasó helyett. # Megjegyzés: Mivel mindenhol régóta Gáborral egyeztetve az Cthulhu nevet magyarosan Orkaként írjuk a fordításban, így a fordítást megváltoztattam Orka képernyőolvasóra az Cthulhu képernyőolvasó helyett.
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Orka képernyőolvasó" msgstr "Orka képernyőolvasó"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "képernyő;olvasó;akadálymentesítés;beszéd;braille;" msgstr "képernyő;olvasó;akadálymentesítés;beszéd;braille;"
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Pembaca Layar Cthulhu" msgstr "Pembaca Layar Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "layar;pembaca;aksesibilitas;ucapan;braille;" msgstr "layar;pembaca;aksesibilitas;ucapan;braille;"
-2
View File
@@ -17,11 +17,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Lokalize 1.5\n" "X-Generator: Lokalize 1.5\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu skjálestur" msgstr "Cthulhu skjálestur"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -39,11 +39,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1)\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n"
"X-Generator: Gtranslator 42.0\n" "X-Generator: Gtranslator 42.0\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lettore schermo Cthulhu" msgstr "Lettore schermo Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "lettore;schermo;accessibilità;voce;braille;" msgstr "lettore;schermo;accessibilità;voce;braille;"
-2
View File
@@ -25,11 +25,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu スクリーンリーダー" msgstr "Cthulhu スクリーンリーダー"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "スクリーン;リーダー;アクセシビリティ;音声;点字;" msgstr "スクリーン;リーダー;アクセシビリティ;音声;点字;"
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu - ეკრანის მკითხველი" msgstr "Cthulhu - ეკრანის მკითხველი"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "ეკრანი;მკითხველი;ხელმისაწვდომობა;მეტყველება;ბრაილი;" msgstr "ეკრანი;მკითხველი;ხელმისაწვდომობა;მეტყველება;ბრაილი;"
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.0\n" "X-Generator: Poedit 3.0\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "" msgstr ""
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu экраннан оқитын қолданбасы" msgstr "Cthulhu экраннан оқитын қолданбасы"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,12 +18,10 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: KBabel 1.11.4\n" "X-Generator: KBabel 1.11.4\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "ಆದ್ಯತೆಗಳು" msgstr "ಆದ್ಯತೆಗಳು"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -15,11 +15,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu 화면 낭독기" msgstr "Cthulhu 화면 낭독기"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "화면;리더;접근성;음성;점자;" msgstr "화면;리더;접근성;음성;점자;"
-2
View File
@@ -25,11 +25,9 @@ msgstr ""
"(n%100<10 || n%100>=20) ? 1 : 2);\n" "(n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu ekrano skaityklė" msgstr "Cthulhu ekrano skaityklė"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "ekrano skaityklė;kalba;brailio raštas;" msgstr "ekrano skaityklė;kalba;brailio raštas;"
-2
View File
@@ -23,11 +23,9 @@ msgstr ""
"2);\n" "2);\n"
"X-Generator: Lokalize 21.12.3\n" "X-Generator: Lokalize 21.12.3\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu ekrāna lasītājs" msgstr "Cthulhu ekrāna lasītājs"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "ekrāna lasītājs;runa;Brails;" msgstr "ekrāna lasītājs;runa;Brails;"
-2
View File
@@ -19,12 +19,10 @@ msgstr ""
"\n" "\n"
"\n" "\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu स्क्रीन पाठक / आवर्द्धक" msgstr "Cthulhu स्क्रीन पाठक / आवर्द्धक"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural= n==1 || n%10==1 ? 0 : 1\n" "Plural-Forms: nplurals=2; plural= n==1 || n%10==1 ? 0 : 1\n"
"X-Generator: KBabel 1.11.4\n" "X-Generator: KBabel 1.11.4\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu читач на екранот" msgstr "Cthulhu читач на екранот"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -22,12 +22,10 @@ msgstr ""
"X-Generator: Poedit 2.0.1\n" "X-Generator: Poedit 2.0.1\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "സ്ക്രീന്‍ വായന അവസാനിപ്പിക്കാം." msgstr "സ്ക്രീന്‍ വായന അവസാനിപ്പിക്കാം."
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,12 +18,10 @@ msgstr ""
"X-Generator: KBabel 1.9.1\n" "X-Generator: KBabel 1.9.1\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "वर्धक भिंग" msgstr "वर्धक भिंग"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 2.0.6\n" "X-Generator: Poedit 2.0.6\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Pembaca skrin Cthulhu" msgstr "Pembaca skrin Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "skrin;pembaca;kebolehcapaian;pertuturan;braille;" msgstr "skrin;pembaca;kebolehcapaian;pertuturan;braille;"
-2
View File
@@ -19,11 +19,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu skjermleser" msgstr "Cthulhu skjermleser"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "skjerm;leser;tilgjengelighet;tale;blindeskrift;" msgstr "skjerm;leser;tilgjengelighet;tale;blindeskrift;"
-2
View File
@@ -13,11 +13,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.0.1\n" "X-Generator: Poedit 3.0.1\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "ओर्का दृष्टि वाचक" msgstr "ओर्का दृष्टि वाचक"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "दृष्टि वाचक; वाचन;ब्रेल;" msgstr "दृष्टि वाचक; वाचन;ब्रेल;"
-2
View File
@@ -31,11 +31,9 @@ msgstr ""
"X-Generator: Poedit 3.2.2\n" "X-Generator: Poedit 3.2.2\n"
# vergrootglas/loep # vergrootglas/loep
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu schermlezer" msgstr "Cthulhu schermlezer"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "scherm;lezer;toegankelijkheid;spraak;braille;" msgstr "scherm;lezer;toegankelijkheid;spraak;braille;"
-2
View File
@@ -18,12 +18,10 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu skjermlesar og forstørringsglas" msgstr "Cthulhu skjermlesar og forstørringsglas"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -23,11 +23,9 @@ msgstr ""
"X-Launchpad-Export-Date: 2015-05-21 18:05+0000\n" "X-Launchpad-Export-Date: 2015-05-21 18:05+0000\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Lector d'ecran Cthulhu" msgstr "Lector d'ecran Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
"lector d'ecran;oral;braille;votz;sintèsi vocala;sintesi vocala;lector " "lector d'ecran;oral;braille;votz;sintèsi vocala;sintesi vocala;lector "
-2
View File
@@ -22,12 +22,10 @@ msgstr ""
"\n" "\n"
"\n" "\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "ଓର୍କା ସ୍କ୍ରିନ ପାଠକ /ଛୋଟକୁ ବଡ ଦେଖଉଥିବା ଉପକରଣ " msgstr "ଓର୍କା ସ୍କ୍ରିନ ପାଠକ /ଛୋଟକୁ ବଡ ଦେଖଉଥିବା ଉପକରଣ "
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -24,11 +24,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-Bookmarks: 589,-1,-1,-1,-1,-1,-1,-1,-1,-1\n" "X-Poedit-Bookmarks: 589,-1,-1,-1,-1,-1,-1,-1,-1,-1\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "ਓਰਕਾ ਸਕਰੀਨ ਰੀਡਰ" msgstr "ਓਰਕਾ ਸਕਰੀਨ ਰੀਡਰ"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -24,11 +24,9 @@ msgstr ""
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2);\n" "|| n%100>=20) ? 1 : 2);\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Czytnik ekranowy Cthulhu" msgstr "Czytnik ekranowy Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "czytnik;ekran;dostępność;mowa;braille;" msgstr "czytnik;ekran;dostępność;mowa;braille;"
-2
View File
@@ -28,11 +28,9 @@ msgstr ""
"X-Source-Language: C\n" "X-Source-Language: C\n"
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Leitor de ecrã Cthulhu" msgstr "Leitor de ecrã Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "leitor;ecrã;acessibilidade;voz;braille;" msgstr "leitor;ecrã;acessibilidade;voz;braille;"
-2
View File
@@ -49,11 +49,9 @@ msgstr ""
"X-DL-Domain: po\n" "X-DL-Domain: po\n"
"X-DL-State: Translating\n" "X-DL-State: Translating\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Leitor de tela Cthulhu" msgstr "Leitor de tela Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "leitor;tela;acessibilidade;voz;braille;" msgstr "leitor;tela;acessibilidade;voz;braille;"
-2
View File
@@ -24,11 +24,9 @@ msgstr ""
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
"X-Poedit-SourceCharset: UTF-8\n" "X-Poedit-SourceCharset: UTF-8\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cititorul de ecran Cthulhu" msgstr "Cititorul de ecran Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "ecran;cititor;accesibilitate;vorbire;braille;" msgstr "ecran;cititor;accesibilitate;vorbire;braille;"
-2
View File
@@ -22,11 +22,9 @@ msgstr ""
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Экранный диктор Cthulhu" msgstr "Экранный диктор Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "экран;чтец;доступность;речь;брайль;" msgstr "экран;чтец;доступность;речь;брайль;"
-2
View File
@@ -24,11 +24,9 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "" msgstr ""
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: KBabel 1.11.4\n" "X-Generator: KBabel 1.11.4\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "" msgstr ""
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -19,11 +19,9 @@ msgstr ""
"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
"X-Generator: Gtranslator 2.91.7\n" "X-Generator: Gtranslator 2.91.7\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Ukončí čítačku obrazovky" msgstr "Ukončí čítačku obrazovky"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -22,11 +22,9 @@ msgstr ""
"X-Poedit-SourceCharset: utf-8\n" "X-Poedit-SourceCharset: utf-8\n"
"X-Generator: Poedit 3.0.1\n" "X-Generator: Poedit 3.0.1\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Zaslonski bralnik Orka" msgstr "Zaslonski bralnik Orka"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "govor;zaslonski bralnik;Braillova pisava;brajica;povečevalnik" msgstr "govor;zaslonski bralnik;Braillova pisava;brajica;povečevalnik"
-2
View File
@@ -17,12 +17,10 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cthulhu-autostart.desktop.in:3
#, fuzzy #, fuzzy
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Ekrani Zmadhues (Lupë)" msgstr "Ekrani Zmadhues (Lupë)"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -21,11 +21,9 @@ msgstr ""
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
"X-Generator: Poedit 3.1.1\n" "X-Generator: Poedit 3.1.1\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Читач екрана Орка" msgstr "Читач екрана Орка"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "читач екрана;говор;брајева азбука;" msgstr "читач екрана;говор;брајева азбука;"
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"X-Project-Style: gnome\n" "X-Project-Style: gnome\n"
"X-Generator: Poedit 2.0.3\n" "X-Generator: Poedit 2.0.3\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Čitač ekrana Orka" msgstr "Čitač ekrana Orka"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "čitač ekrana;govor;brajeva azbuka;" msgstr "čitač ekrana;govor;brajeva azbuka;"
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu skärmläsare" msgstr "Cthulhu skärmläsare"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "skärm;läsare;tillgänglighet;tal;punktskrift;" msgstr "skärm;läsare;tillgänglighet;tal;punktskrift;"
-2
View File
@@ -24,11 +24,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n!=1);\\n\n" "Plural-Forms: nplurals=2; plural=(n!=1);\\n\n"
"\n" "\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "ஆர்கா திரை படிப்பி " msgstr "ஆர்கா திரை படிப்பி "
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "திரைபடிப்பான்;பேசுதல்;ப்ரெய்ல்;" msgstr "திரைபடிப்பான்;பேசுதல்;ப்ரெய்ல்;"
-2
View File
@@ -23,11 +23,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n!=1);\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n"
"X-Generator: KBabel 1.11.4\n" "X-Generator: KBabel 1.11.4\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "ఓర్కా తెరచదువరి" msgstr "ఓర్కా తెరచదువరి"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "" msgstr ""
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.6.3\n" "X-Generator: Poedit 1.6.3\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Хонандаи экрани Cthulhu" msgstr "Хонандаи экрани Cthulhu"
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "хонандаи экран;нутқ;брайл;" msgstr "хонандаи экран;нутқ;брайл;"
-2
View File
@@ -19,11 +19,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "โปรแกรมอ่านหน้าจอ Cthulhu" msgstr "โปรแกรมอ่านหน้าจอ Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "หน้าจอ;อ่าน;การเข้าถึง;เสียงพูด;อักษรเบรลล์;" msgstr "หน้าจอ;อ่าน;การเข้าถึง;เสียงพูด;อักษรเบรลล์;"
-2
View File
@@ -27,11 +27,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.4\n" "X-Generator: Poedit 3.4\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu Ekran Okuyucu" msgstr "Cthulhu Ekran Okuyucu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "ekran;okuyucu;erişilebilirlik;konuşma;braille;" msgstr "ekran;okuyucu;erişilebilirlik;konuşma;braille;"
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu ئېكران ئوقۇغۇ" msgstr "Cthulhu ئېكران ئوقۇغۇ"
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "screen reader;speech;braille;ئېكران ئوقۇغۇچ;تاۋۇش;ئەمالار ئېلىپبەسى;" msgstr "screen reader;speech;braille;ئېكران ئوقۇغۇچ;تاۋۇش;ئەمالار ئېلىپبەسى;"
-2
View File
@@ -26,11 +26,9 @@ msgstr ""
"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Lokalize 20.12.0\n" "X-Generator: Lokalize 20.12.0\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Інструмент читання з екрана «Cthulhu»" msgstr "Інструмент читання з екрана «Cthulhu»"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "екран;читач;доступність;мовлення;брайль;" msgstr "екран;читач;доступність;мовлення;брайль;"
-2
View File
@@ -18,11 +18,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: LocFactoryEditor 1.8\n" "X-Generator: LocFactoryEditor 1.8\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Trình đọc màn hình Cthulhu" msgstr "Trình đọc màn hình Cthulhu"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "màn hình;đọc;trợ năng;giọng nói;chữ nổi;" msgstr "màn hình;đọc;trợ năng;giọng nói;chữ nổi;"
-2
View File
@@ -29,11 +29,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.3.2\n" "X-Generator: Poedit 3.3.2\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu 屏幕阅读器" msgstr "Cthulhu 屏幕阅读器"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "屏幕;阅读器;无障碍;语音;盲文;" msgstr "屏幕;阅读器;无障碍;语音;盲文;"
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 1.6.5\n" "X-Generator: Poedit 1.6.5\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu 螢幕閱讀器" msgstr "Cthulhu 螢幕閱讀器"
#: cthulhu-autostart.desktop.in:11
#, fuzzy #, fuzzy
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "screen reader;speech;braille;螢幕閱讀器;語音;點字;" msgstr "screen reader;speech;braille;螢幕閱讀器;語音;點字;"
-2
View File
@@ -20,11 +20,9 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 1.8.12\n" "X-Generator: Poedit 1.8.12\n"
#: cthulhu-autostart.desktop.in:3
msgid "Cthulhu Screen Reader" msgid "Cthulhu Screen Reader"
msgstr "Cthulhu 螢幕閱讀器" msgstr "Cthulhu 螢幕閱讀器"
#: cthulhu-autostart.desktop.in:11
msgid "screen;reader;accessibility;speech;braille;" msgid "screen;reader;accessibility;speech;braille;"
msgstr "螢幕;閱讀器;無障礙;語音;點字;" msgstr "螢幕;閱讀器;無障礙;語音;點字;"
+48 -2
View File
@@ -85,12 +85,16 @@ class Backend:
with open(fileName, 'w', encoding='utf-8') as settingsFile: with open(fileName, 'w', encoding='utf-8') as settingsFile:
settingsFile.write(dumps(prefsDoc)) settingsFile.write(dumps(prefsDoc))
def _updateTable(self, targetTable, newValues): def _updateTable(self, targetTable, newValues, preserveMissingKeys=None):
if not isinstance(newValues, dict): if not isinstance(newValues, dict):
return return
preserveMissingKeys = set(preserveMissingKeys or [])
for key in list(targetTable.keys()): for key in list(targetTable.keys()):
if key not in newValues: if key not in newValues:
if key in preserveMissingKeys:
continue
del targetTable[key] del targetTable[key]
continue continue
newValue = newValues[key] newValue = newValues[key]
@@ -173,7 +177,12 @@ class Backend:
profiles[profile] = {} profiles[profile] = {}
profileTable = profiles[profile] profileTable = profiles[profile]
self._updateTable(profileTable, general) # Keep plugin persistence keys when callers provide partial updates.
self._updateTable(
profileTable,
general,
preserveMissingKeys={"activePlugins", "pluginSources"},
)
self._writeDocument(self.settingsFile, prefsDoc) self._writeDocument(self.settingsFile, prefsDoc)
def _getSettings(self): def _getSettings(self):
@@ -213,12 +222,33 @@ class Backend:
return settingsDict return settingsDict
def _hasLikelyCustomEchoVoice(self, echoVoice, defaultVoice):
"""Return True when echoVoice appears intentionally customized."""
if not echoVoice:
return False
echoEstablished = bool(echoVoice.get('established', False))
if not defaultVoice:
return echoEstablished or bool(echoVoice.get(acss.ACSS.FAMILY))
for key in [acss.ACSS.RATE, acss.ACSS.AVERAGE_PITCH, acss.ACSS.GAIN, acss.ACSS.FAMILY]:
echoValue = echoVoice.get(key)
defaultValue = defaultVoice.get(key)
if echoValue is not None and echoValue != defaultValue:
return True
return echoEstablished and bool(echoVoice.get(acss.ACSS.FAMILY))
def getGeneral(self, profile=None): def getGeneral(self, profile=None):
""" Get general settings from default settings and """ Get general settings from default settings and
override with profile values. """ override with profile values. """
self._getSettings() self._getSettings()
generalSettings = self.general.copy() generalSettings = self.general.copy()
generalSettings = self._migrateSettings(generalSettings) generalSettings = self._migrateSettings(generalSettings)
# Plugin state is profile-scoped; ignore legacy/global values.
generalSettings.pop('activePlugins', None)
generalSettings.pop('pluginSources', None)
defaultProfile = generalSettings.get('startingProfile', defaultProfile = generalSettings.get('startingProfile',
['Default', 'default']) ['Default', 'default'])
if profile is None: if profile is None:
@@ -229,8 +259,24 @@ class Backend:
if key == 'voices': if key == 'voices':
for voiceType, voiceDef in value.items(): for voiceType, voiceDef in value.items():
value[voiceType] = acss.ACSS(voiceDef) value[voiceType] = acss.ACSS(voiceDef)
if key == 'echoVoice' and isinstance(value, dict):
value = acss.ACSS(value)
if key not in ['startingProfile', 'activeProfile']: if key not in ['startingProfile', 'activeProfile']:
generalSettings[key] = value generalSettings[key] = value
# Backward compatibility: recover custom echo behavior when legacy
# profiles have echoVoice but are missing custom-echo toggle keys.
if 'useCustomEchoVoice' not in profileSettings:
voices = generalSettings.get('voices') or {}
defaultVoice = voices.get(settings.DEFAULT_VOICE)
echoVoice = generalSettings.get('echoVoice')
if self._hasLikelyCustomEchoVoice(echoVoice, defaultVoice):
generalSettings['useCustomEchoVoice'] = True
if 'useCustomEchoForKey' not in profileSettings:
generalSettings['useCustomEchoForKey'] = True
if 'useCustomEchoForCharacter' not in profileSettings:
generalSettings['useCustomEchoForCharacter'] = True
try: try:
generalSettings['activeProfile'] = profileSettings['profile'] generalSettings['activeProfile'] = profileSettings['profile']
except KeyError: except KeyError:
+310 -3
View File
@@ -136,6 +136,24 @@
<property name="step_increment">0.10000000149</property> <property name="step_increment">0.10000000149</property>
<property name="page_increment">1</property> <property name="page_increment">1</property>
</object> </object>
<object class="GtkAdjustment" id="echoPitchAdjustment">
<property name="upper">10</property>
<property name="value">5</property>
<property name="step_increment">0.10000000149</property>
<property name="page_increment">1</property>
</object>
<object class="GtkAdjustment" id="echoRateAdjustment">
<property name="upper">100</property>
<property name="value">50</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="echoVolumeAdjustment">
<property name="upper">10</property>
<property name="value">10</property>
<property name="step_increment">0.10000000149</property>
<property name="page_increment">1</property>
</object>
<object class="GtkDialog" id="cthulhuSetupWindow"> <object class="GtkDialog" id="cthulhuSetupWindow">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="title" translatable="yes">Cthulhu Preferences</property> <property name="title" translatable="yes">Cthulhu Preferences</property>
@@ -3037,6 +3055,295 @@
<property name="top_attach">1</property> <property name="top_attach">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkFrame" id="echoVoiceFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkAlignment" id="echoVoiceAlignment">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="left_padding">12</property>
<child>
<object class="GtkGrid" id="echoVoiceGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">10</property>
<property name="column_spacing">8</property>
<child>
<object class="GtkCheckButton" id="useCustomEchoVoiceCheckButton">
<property name="label" translatable="yes">Use c_ustom echo voice settings</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="useCustomEchoVoiceToggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="useCustomEchoSpeechServerCheckButton">
<property name="label" translatable="yes">Use custom _speech-dispatcher module for echo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="useCustomEchoSpeechServerToggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="echoSpeechServersLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">1</property>
<property name="label" translatable="yes">Echo _module:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">echoSpeechServers</property>
<accessibility>
<relation type="label-for" target="echoSpeechServers"/>
</accessibility>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="echoSpeechServers">
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="changed" handler="echoSpeechServersChanged" swapped="no"/>
<accessibility>
<relation type="labelled-by" target="echoSpeechServersLabel"/>
</accessibility>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="echoSpeechFamiliesLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">1</property>
<property name="label" translatable="yes">Echo _voice:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">echoSpeechFamilies</property>
<accessibility>
<relation type="label-for" target="echoSpeechFamilies"/>
</accessibility>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="echoSpeechFamilies">
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="changed" handler="echoSpeechFamiliesChanged" swapped="no"/>
<accessibility>
<relation type="labelled-by" target="echoSpeechFamiliesLabel"/>
</accessibility>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="echoRateLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">1</property>
<property name="label" translatable="yes">Echo ra_te:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">echoRateScale</property>
<accessibility>
<relation type="label-for" target="echoRateScale"/>
</accessibility>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkScale" id="echoRateScale">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">echoRateAdjustment</property>
<property name="round_digits">0</property>
<property name="digits">0</property>
<property name="value_pos">right</property>
<signal name="value-changed" handler="echoRateValueChanged" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="echoPitchLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">1</property>
<property name="label" translatable="yes">Echo pi_tch:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">echoPitchScale</property>
<accessibility>
<relation type="label-for" target="echoPitchScale"/>
</accessibility>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkScale" id="echoPitchScale">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">echoPitchAdjustment</property>
<property name="round_digits">1</property>
<property name="value_pos">right</property>
<signal name="value-changed" handler="echoPitchValueChanged" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="echoVolumeLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">1</property>
<property name="label" translatable="yes">Echo vo_lume:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">echoVolumeScale</property>
<accessibility>
<relation type="label-for" target="echoVolumeScale"/>
</accessibility>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
</packing>
</child>
<child>
<object class="GtkScale" id="echoVolumeScale">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">echoVolumeAdjustment</property>
<property name="round_digits">1</property>
<property name="value_pos">right</property>
<signal name="value-changed" handler="echoVolumeValueChanged" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">6</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="useCustomEchoForKeyCheckButton">
<property name="label" translatable="yes">Use custom voice for _key echo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="checkButtonToggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">7</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="useCustomEchoForCharacterCheckButton">
<property name="label" translatable="yes">Use custom voice for _character echo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="checkButtonToggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">8</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="useCustomEchoForWordCheckButton">
<property name="label" translatable="yes">Use custom voice for _word echo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="checkButtonToggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">9</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="useCustomEchoForSentenceCheckButton">
<property name="label" translatable="yes">Use custom voice for _sentence echo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="checkButtonToggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">10</property>
<property name="width">2</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="echoVoiceLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Echo Voice</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child> <child>
<object class="GtkCheckButton" id="enableEchoByCharacterCheckButton"> <object class="GtkCheckButton" id="enableEchoByCharacterCheckButton">
<property name="label" translatable="yes" comments="Translators: When this option is enabled, inserted text of length 1 is spoken.">Enable echo by cha_racter</property> <property name="label" translatable="yes" comments="Translators: When this option is enabled, inserted text of length 1 is spoken.">Enable echo by cha_racter</property>
@@ -3049,7 +3356,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">2</property> <property name="top_attach">3</property>
</packing> </packing>
</child> </child>
<child> <child>
@@ -3064,7 +3371,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">3</property> <property name="top_attach">4</property>
</packing> </packing>
</child> </child>
<child> <child>
@@ -3079,7 +3386,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">4</property> <property name="top_attach">5</property>
</packing> </packing>
</child> </child>
</object> </object>
+1 -1
View File
@@ -23,5 +23,5 @@
# Forked from Orca screen reader. # Forked from Orca screen reader.
# Cthulhu project: https://git.stormux.org/storm/cthulhu # Cthulhu project: https://git.stormux.org/storm/cthulhu
version = "2026.02.18" version = "2026.03.02"
codeName = "master" codeName = "master"
+449 -12
View File
@@ -38,6 +38,9 @@ gi.require_version("Gtk", "3.0")
from gi.repository import Atspi from gi.repository import Atspi
import os import os
import json
import subprocess
import sys
import threading import threading
from gi.repository import Gdk from gi.repository import Gdk
from gi.repository import GLib from gi.repository import GLib
@@ -173,6 +176,16 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self.speechSystemsChoice = None self.speechSystemsChoice = None
self.speechSystemsChoices = None self.speechSystemsChoices = None
self.speechSystemsModel = None self.speechSystemsModel = None
self.echoVoice = None
self.echoSpeechFamiliesChoice = None
self.echoSpeechFamiliesChoices = None
self.echoSpeechFamiliesModel = None
self.echoSpeechServersChoice = None
self.echoSpeechServersChoices = None
self.echoSpeechServersModel = None
self.initializingEchoSpeech = False
self._echoVoiceFetchToken = 0
self._updatingEchoSpeechFamilies = False
self.systemVoice = None self.systemVoice = None
self.uppercaseVoice = None self.uppercaseVoice = None
self.window = None self.window = None
@@ -386,7 +399,12 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self._initComboBox(self.get_widget("speechLanguages")) self._initComboBox(self.get_widget("speechLanguages"))
self.speechFamiliesModel = \ self.speechFamiliesModel = \
self._initComboBox(self.get_widget("speechFamilies")) self._initComboBox(self.get_widget("speechFamilies"))
self.echoSpeechServersModel = \
self._initComboBox(self.get_widget("echoSpeechServers"))
self.echoSpeechFamiliesModel = \
self._initComboBox(self.get_widget("echoSpeechFamilies"))
self._initSpeechState() self._initSpeechState()
self._initEchoSpeechState()
# TODO - JD: Will this ever be the case?? # TODO - JD: Will this ever be the case??
self._isInitialSetup = \ self._isInitialSetup = \
@@ -1671,6 +1689,261 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self._setupSpeechSystems(factories) self._setupSpeechSystems(factories)
self.initializingSpeech = False self.initializingSpeech = False
def _getSpeechDispatcherFactory(self):
"""Returns the Speech Dispatcher factory if available."""
factories = cthulhu.cthulhuApp.settingsManager.getSpeechServerFactories()
for factory in factories:
try:
if factory.SpeechServer.getFactoryName() == guilabels.SPEECH_DISPATCHER:
return factory
except Exception:
debug.printException(debug.LEVEL_FINEST)
return None
def _setEchoVoiceFamily(self, family):
"""Sets echo voice family from the selected server family object."""
if not family:
return
name = family.get(speechserver.VoiceFamily.NAME)
language = family.get(speechserver.VoiceFamily.LANG)
dialect = family.get(speechserver.VoiceFamily.DIALECT)
variant = family.get(speechserver.VoiceFamily.VARIANT)
self.echoVoice[acss.ACSS.FAMILY] = {
speechserver.VoiceFamily.NAME: name,
speechserver.VoiceFamily.LANG: language,
speechserver.VoiceFamily.DIALECT: dialect,
speechserver.VoiceFamily.VARIANT: variant,
}
self.echoVoice['established'] = True
def _populateEchoSpeechFamilies(self, families):
"""Populate the echo family combobox from the provided families list."""
combobox = self.get_widget("echoSpeechFamilies")
combobox.set_model(None)
self.echoSpeechFamiliesModel.clear()
self.echoSpeechFamiliesChoices = list(families or [])
self.echoSpeechFamiliesChoice = None
selectedIndex = 0
selectedMatchFound = False
selectedFamily = self.echoVoice.get(acss.ACSS.FAMILY)
selectedName = selectedLanguage = selectedDialect = selectedVariant = None
if selectedFamily:
selectedName = selectedFamily.get(speechserver.VoiceFamily.NAME)
selectedLanguage = selectedFamily.get(speechserver.VoiceFamily.LANG)
selectedDialect = selectedFamily.get(speechserver.VoiceFamily.DIALECT)
selectedVariant = selectedFamily.get(speechserver.VoiceFamily.VARIANT)
for i, family in enumerate(self.echoSpeechFamiliesChoices):
name = family.get(speechserver.VoiceFamily.NAME) or ""
language = family.get(speechserver.VoiceFamily.LANG) or ""
dialect = family.get(speechserver.VoiceFamily.DIALECT) or ""
variant = family.get(speechserver.VoiceFamily.VARIANT)
display = name
locale = language
if dialect:
locale = f"{language}-{dialect}" if language else dialect
if locale:
display = f"{name} ({locale})"
self.echoSpeechFamiliesModel.append((i, display))
if selectedName == name and selectedLanguage == language \
and selectedDialect == dialect and selectedVariant == variant:
selectedIndex = i
selectedMatchFound = True
combobox.set_model(self.echoSpeechFamiliesModel)
if self.echoSpeechFamiliesChoices:
self._updatingEchoSpeechFamilies = True
try:
combobox.set_active(selectedIndex)
finally:
self._updatingEchoSpeechFamilies = False
self.echoSpeechFamiliesChoice = self.echoSpeechFamiliesChoices[selectedIndex]
def _fetchEchoSpeechFamiliesWorker(self, serverInfo, requestToken):
"""Fetch module-specific voices in a subprocess and apply asynchronously."""
queryScript = """
import json
import sys
from cthulhu import speechdispatcherfactory
from cthulhu import speechserver
serverInfo = json.loads(sys.argv[1])
if isinstance(serverInfo, list):
serverInfo = tuple(serverInfo)
server = speechdispatcherfactory.SpeechServer.getSpeechServer(serverInfo)
families = server.getVoiceFamilies() if server else []
result = []
for family in families:
result.append({
"name": family.get(speechserver.VoiceFamily.NAME),
"lang": family.get(speechserver.VoiceFamily.LANG),
"dialect": family.get(speechserver.VoiceFamily.DIALECT),
"variant": family.get(speechserver.VoiceFamily.VARIANT),
})
print(json.dumps(result))
"""
familyDefs = None
try:
completed = subprocess.run(
[sys.executable, "-c", queryScript, json.dumps(serverInfo)],
capture_output=True,
text=True,
timeout=4.0,
check=False,
)
if completed.returncode == 0 and completed.stdout.strip():
familyDefs = json.loads(completed.stdout.strip())
else:
tokens = [
"PREFERENCES DIALOG: Echo voice fetch failed for",
serverInfo,
"rc=",
completed.returncode,
]
debug.printTokens(debug.LEVEL_WARNING, tokens, True)
except subprocess.TimeoutExpired:
tokens = ["PREFERENCES DIALOG: Echo voice fetch timed out for", serverInfo]
debug.printTokens(debug.LEVEL_WARNING, tokens, True)
except Exception:
debug.printException(debug.LEVEL_WARNING)
GLib.idle_add(self._applyFetchedEchoSpeechFamilies, requestToken, familyDefs)
def _applyFetchedEchoSpeechFamilies(self, requestToken, familyDefs):
"""Apply fetched echo families if this is still the active request."""
if requestToken != self._echoVoiceFetchToken:
return False
if not isinstance(familyDefs, list):
return False
families = []
for familyDef in familyDefs:
if not isinstance(familyDef, dict):
continue
families.append(
speechserver.VoiceFamily({
speechserver.VoiceFamily.NAME: familyDef.get("name"),
speechserver.VoiceFamily.LANG: familyDef.get("lang"),
speechserver.VoiceFamily.DIALECT: familyDef.get("dialect"),
speechserver.VoiceFamily.VARIANT: familyDef.get("variant"),
})
)
if not families:
return False
self._populateEchoSpeechFamilies(families)
return False
def _setupEchoSpeechFamilies(self):
"""Populate echo voice family list for the current echo server."""
self._echoVoiceFetchToken += 1
requestToken = self._echoVoiceFetchToken
useCustomModule = self.get_widget("useCustomEchoSpeechServerCheckButton").get_active()
fallbackFamilies = list(self.speechFamiliesChoices or [])
self._populateEchoSpeechFamilies(fallbackFamilies)
if useCustomModule and self.echoSpeechServersChoice:
serverInfo = self.echoSpeechServersChoice.getInfo()
thread = threading.Thread(
target=self._fetchEchoSpeechFamiliesWorker,
args=(serverInfo, requestToken),
daemon=True,
)
thread.start()
def _setEchoSpeechServersChoice(self, serverInfo):
"""Set the active echo module based on stored server info."""
if not self.echoSpeechServersChoices:
self.echoSpeechServersChoice = None
self._setupEchoSpeechFamilies()
return
valueSet = False
for i, server in enumerate(self.echoSpeechServersChoices):
info = server.getInfo()
if serverInfo and info == serverInfo:
self.get_widget("echoSpeechServers").set_active(i)
self.echoSpeechServersChoice = server
valueSet = True
break
if not valueSet:
self.get_widget("echoSpeechServers").set_active(0)
self.echoSpeechServersChoice = self.echoSpeechServersChoices[0]
self._setupEchoSpeechFamilies()
def _setupEchoSpeechServers(self):
"""Populate available speech-dispatcher modules for echo."""
combobox = self.get_widget("echoSpeechServers")
combobox.set_model(None)
self.echoSpeechServersModel.clear()
self.echoSpeechServersChoices = []
self.echoSpeechServersChoice = None
if not self.prefsDict.get('enableSpeech', True):
combobox.set_model(self.echoSpeechServersModel)
self._setupEchoSpeechFamilies()
return
factory = self._getSpeechDispatcherFactory()
if factory is None:
combobox.set_model(self.echoSpeechServersModel)
self._setupEchoSpeechFamilies()
return
try:
self.echoSpeechServersChoices = factory.SpeechServer.getSpeechServers()
except Exception:
debug.printException(debug.LEVEL_WARNING)
self.echoSpeechServersChoices = []
for i, server in enumerate(self.echoSpeechServersChoices):
name = server.getInfo()[0]
self.echoSpeechServersModel.append((i, name))
combobox.set_model(self.echoSpeechServersModel)
self._setEchoSpeechServersChoice(self.prefsDict.get("echoSpeechServerInfo"))
def _initEchoSpeechState(self):
"""Initialize echo voice controls and choices."""
self.echoVoice = acss.ACSS(
self.prefsDict.get("echoVoice", settings.echoVoice) or {})
baseVoice = self.defaultVoice or acss.ACSS(
self.prefsDict.get("voices", {}).get(settings.DEFAULT_VOICE, {}))
rate = self.echoVoice.get(acss.ACSS.RATE, baseVoice.get(acss.ACSS.RATE, 50.0))
pitch = self.echoVoice.get(acss.ACSS.AVERAGE_PITCH, baseVoice.get(acss.ACSS.AVERAGE_PITCH, 5.0))
volume = self.echoVoice.get(acss.ACSS.GAIN, baseVoice.get(acss.ACSS.GAIN, 10.0))
self.get_widget("echoRateScale").set_value(rate)
self.get_widget("echoPitchScale").set_value(pitch)
self.get_widget("echoVolumeScale").set_value(volume)
self.initializingEchoSpeech = True
self._setupEchoSpeechServers()
self.initializingEchoSpeech = False
self._setEchoVoiceItems()
def _setSpokenTextAttributes(self, view, setAttributes, def _setSpokenTextAttributes(self, view, setAttributes,
state, moveToTop=False): state, moveToTop=False):
"""Given a set of spoken text attributes, update the model used by the """Given a set of spoken text attributes, update the model used by the
@@ -2404,6 +2677,19 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
prefs["enableEchoByWord"]) prefs["enableEchoByWord"])
self.get_widget("enableEchoBySentenceCheckButton").set_active( \ self.get_widget("enableEchoBySentenceCheckButton").set_active( \
prefs["enableEchoBySentence"]) prefs["enableEchoBySentence"])
self.get_widget("useCustomEchoVoiceCheckButton").set_active(
prefs.get("useCustomEchoVoice", settings.useCustomEchoVoice))
self.get_widget("useCustomEchoSpeechServerCheckButton").set_active(
prefs.get("useCustomEchoSpeechServer", settings.useCustomEchoSpeechServer))
self.get_widget("useCustomEchoForKeyCheckButton").set_active(
prefs.get("useCustomEchoForKey", settings.useCustomEchoForKey))
self.get_widget("useCustomEchoForCharacterCheckButton").set_active(
prefs.get("useCustomEchoForCharacter", settings.useCustomEchoForCharacter))
self.get_widget("useCustomEchoForWordCheckButton").set_active(
prefs.get("useCustomEchoForWord", settings.useCustomEchoForWord))
self.get_widget("useCustomEchoForSentenceCheckButton").set_active(
prefs.get("useCustomEchoForSentence", settings.useCustomEchoForSentence))
self._setEchoVoiceItems()
# Text attributes pane. # Text attributes pane.
# #
@@ -2563,22 +2849,20 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self._updateAIControlsState(enabled) self._updateAIControlsState(enabled)
def _updateAIControlsState(self, enabled): def _updateAIControlsState(self, enabled):
"""Enable or disable AI controls based on AI enabled state.""" """Refresh AI controls while keeping configuration fields editable."""
self.aiProviderCombo.set_sensitive(enabled) _ = enabled # kept for signal/call compatibility
self.aiApiKeyEntry.set_sensitive(enabled) # Keep settings editable even when AI assistant is disabled so users
self.aiOllamaModelEntry.set_sensitive(enabled) # can configure providers/keys before enabling it.
self.aiOllamaEndpointEntry.set_sensitive(enabled) self.aiProviderCombo.set_sensitive(True)
self.aiConfirmationCheckButton.set_sensitive(enabled) self.aiConfirmationCheckButton.set_sensitive(True)
self.aiScreenshotQualityCombo.set_sensitive(enabled) self.aiScreenshotQualityCombo.set_sensitive(True)
try: try:
self.get_widget("aiGetClaudeKeyButton").set_sensitive(enabled) self.get_widget("aiGetClaudeKeyButton").set_sensitive(True)
except: except:
pass # Button might not exist in older UI files pass # Button might not exist in older UI files
# Update provider-specific controls if AI is enabled current_provider = self.prefsDict.get("aiProvider", settings.aiProvider)
if enabled: self._updateProviderControls(current_provider)
current_provider = self.prefsDict.get("aiProvider", settings.aiProvider)
self._updateProviderControls(current_provider)
def _initIndentationState(self): def _initIndentationState(self):
"""Initialize Indentation widgets with current settings.""" """Initialize Indentation widgets with current settings."""
@@ -2908,6 +3192,45 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self.get_widget("enableNavigationKeysCheckButton").set_sensitive(enable) self.get_widget("enableNavigationKeysCheckButton").set_sensitive(enable)
self.get_widget("enableDiacriticalKeysCheckButton").set_sensitive( \ self.get_widget("enableDiacriticalKeysCheckButton").set_sensitive( \
enable) enable)
self._setEchoVoiceItems()
def _setEchoVoiceItems(self):
"""[In]sensitize echo voice controls based on current state."""
useCustomVoice = self.get_widget("useCustomEchoVoiceCheckButton").get_active()
useCustomModule = self.get_widget("useCustomEchoSpeechServerCheckButton").get_active()
keyEchoEnabled = self.get_widget("keyEchoCheckButton").get_active()
charEchoEnabled = self.get_widget("enableEchoByCharacterCheckButton").get_active()
wordEchoEnabled = self.get_widget("enableEchoByWordCheckButton").get_active()
sentenceEchoEnabled = self.get_widget("enableEchoBySentenceCheckButton").get_active()
speechEnabled = self.get_widget("speechSupportCheckButton").get_active()
speechSystemIsDispatcher = False
if self.speechSystemsChoice:
try:
speechSystemIsDispatcher = \
self.speechSystemsChoice.SpeechServer.getFactoryName() == guilabels.SPEECH_DISPATCHER
except Exception:
speechSystemIsDispatcher = False
voiceControlsEnabled = useCustomVoice and speechEnabled
moduleOverrideAvailable = voiceControlsEnabled and speechSystemIsDispatcher
moduleControlsEnabled = moduleOverrideAvailable and useCustomModule
self.get_widget("useCustomEchoSpeechServerCheckButton").set_sensitive(moduleOverrideAvailable)
self.get_widget("echoSpeechServers").set_sensitive(moduleControlsEnabled)
self.get_widget("echoSpeechFamilies").set_sensitive(voiceControlsEnabled)
self.get_widget("echoRateScale").set_sensitive(useCustomVoice)
self.get_widget("echoPitchScale").set_sensitive(useCustomVoice)
self.get_widget("echoVolumeScale").set_sensitive(useCustomVoice)
self.get_widget("useCustomEchoForKeyCheckButton").set_sensitive(useCustomVoice and keyEchoEnabled)
self.get_widget("useCustomEchoForCharacterCheckButton").set_sensitive(
useCustomVoice and charEchoEnabled)
self.get_widget("useCustomEchoForWordCheckButton").set_sensitive(useCustomVoice and wordEchoEnabled)
self.get_widget("useCustomEchoForSentenceCheckButton").set_sensitive(
useCustomVoice and sentenceEchoEnabled)
def _presentMessage(self, text, interrupt=False, voice=None): def _presentMessage(self, text, interrupt=False, voice=None):
"""If the text field is not None, presents the given text, optionally """If the text field is not None, presents the given text, optionally
@@ -3234,6 +3557,8 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
enable = widget.get_active() enable = widget.get_active()
self.prefsDict["enableSpeech"] = enable self.prefsDict["enableSpeech"] = enable
self.get_widget("speechOptionsGrid").set_sensitive(enable) self.get_widget("speechOptionsGrid").set_sensitive(enable)
self._setupEchoSpeechServers()
self._setEchoVoiceItems()
def onlySpeakDisplayedTextToggled(self, widget): def onlySpeakDisplayedTextToggled(self, widget):
"""Signal handler for the "toggled" signal for the GtkCheckButton """Signal handler for the "toggled" signal for the GtkCheckButton
@@ -3265,6 +3590,8 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
selectedIndex = widget.get_active() selectedIndex = widget.get_active()
self.speechSystemsChoice = self.speechSystemsChoices[selectedIndex] self.speechSystemsChoice = self.speechSystemsChoices[selectedIndex]
self._setupSpeechServers() self._setupSpeechServers()
self._setupEchoSpeechServers()
self._setEchoVoiceItems()
def speechServersChanged(self, widget): def speechServersChanged(self, widget):
"""Signal handler for the "changed" signal for the speechServers """Signal handler for the "changed" signal for the speechServers
@@ -3297,6 +3624,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
pass pass
self._setupVoices() self._setupVoices()
self._setEchoVoiceItems()
def speechLanguagesChanged(self, widget): def speechLanguagesChanged(self, widget):
"""Signal handler for the "value_changed" signal for the languages """Signal handler for the "value_changed" signal for the languages
@@ -3435,6 +3763,75 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
voices.get(settings.DEFAULT_VOICE, {})[acss.ACSS.GAIN] = volume voices.get(settings.DEFAULT_VOICE, {})[acss.ACSS.GAIN] = volume
cthulhu.cthulhuApp.settingsManager.setSetting('voices', voices) cthulhu.cthulhuApp.settingsManager.setSetting('voices', voices)
def useCustomEchoVoiceToggled(self, widget):
"""Signal handler for enabling/disabling custom echo voice settings."""
self.prefsDict["useCustomEchoVoice"] = widget.get_active()
self._setEchoVoiceItems()
self._setupEchoSpeechFamilies()
def useCustomEchoSpeechServerToggled(self, widget):
"""Signal handler for enabling/disabling custom echo module override."""
self.prefsDict["useCustomEchoSpeechServer"] = widget.get_active()
self._setEchoVoiceItems()
self._setupEchoSpeechFamilies()
def echoSpeechServersChanged(self, widget):
"""Signal handler for selecting the echo speech-dispatcher module."""
if self.initializingEchoSpeech:
return
selectedIndex = widget.get_active()
if selectedIndex < 0:
self.echoSpeechServersChoice = None
self.prefsDict["echoSpeechServerInfo"] = None
self._setupEchoSpeechFamilies()
return
self.echoSpeechServersChoice = self.echoSpeechServersChoices[selectedIndex]
self.prefsDict["echoSpeechServerInfo"] = self.echoSpeechServersChoice.getInfo()
self._setupEchoSpeechFamilies()
def echoSpeechFamiliesChanged(self, widget):
"""Signal handler for selecting the echo voice family."""
if self.initializingEchoSpeech or self._updatingEchoSpeechFamilies:
return
selectedIndex = widget.get_active()
if selectedIndex < 0:
self.echoSpeechFamiliesChoice = None
return
self.echoSpeechFamiliesChoice = self.echoSpeechFamiliesChoices[selectedIndex]
self._setEchoVoiceFamily(self.echoSpeechFamiliesChoice)
def echoRateValueChanged(self, widget):
"""Signal handler for changing custom echo rate."""
if self.echoVoice is None:
self.echoVoice = acss.ACSS({})
self.echoVoice[acss.ACSS.RATE] = widget.get_value()
self.echoVoice['established'] = True
def echoPitchValueChanged(self, widget):
"""Signal handler for changing custom echo pitch."""
if self.echoVoice is None:
self.echoVoice = acss.ACSS({})
self.echoVoice[acss.ACSS.AVERAGE_PITCH] = widget.get_value()
self.echoVoice['established'] = True
def echoVolumeValueChanged(self, widget):
"""Signal handler for changing custom echo volume."""
if self.echoVoice is None:
self.echoVoice = acss.ACSS({})
self.echoVoice[acss.ACSS.GAIN] = widget.get_value()
self.echoVoice['established'] = True
def checkButtonToggled(self, widget): def checkButtonToggled(self, widget):
"""Signal handler for "toggled" signal for basic GtkCheckButton """Signal handler for "toggled" signal for basic GtkCheckButton
widgets. The user has altered the state of the checkbox. widgets. The user has altered the state of the checkbox.
@@ -3452,6 +3849,15 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
# strip "CheckButton" from the end. # strip "CheckButton" from the end.
settingName = settingName[:-11] settingName = settingName[:-11]
self.prefsDict[settingName] = widget.get_active() self.prefsDict[settingName] = widget.get_active()
if settingName in [
"enableEchoByCharacter",
"enableEchoByWord",
"enableEchoBySentence",
"useCustomEchoForKey",
"useCustomEchoForCharacter",
"useCustomEchoForWord",
"useCustomEchoForSentence"]:
self._setEchoVoiceItems()
def keyEchoChecked(self, widget): def keyEchoChecked(self, widget):
"""Signal handler for the "toggled" signal for the """Signal handler for the "toggled" signal for the
@@ -4254,6 +4660,35 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
settings.SYSTEM_VOICE: acss.ACSS(self.systemVoice), settings.SYSTEM_VOICE: acss.ACSS(self.systemVoice),
} }
self.prefsDict["useCustomEchoVoice"] = \
self.get_widget("useCustomEchoVoiceCheckButton").get_active()
self.prefsDict["useCustomEchoSpeechServer"] = \
self.get_widget("useCustomEchoSpeechServerCheckButton").get_active()
self.prefsDict["useCustomEchoForKey"] = \
self.get_widget("useCustomEchoForKeyCheckButton").get_active()
self.prefsDict["useCustomEchoForCharacter"] = \
self.get_widget("useCustomEchoForCharacterCheckButton").get_active()
self.prefsDict["useCustomEchoForWord"] = \
self.get_widget("useCustomEchoForWordCheckButton").get_active()
self.prefsDict["useCustomEchoForSentence"] = \
self.get_widget("useCustomEchoForSentenceCheckButton").get_active()
if self.echoVoice is None:
self.echoVoice = acss.ACSS({})
# Persist slider values directly so saving does not depend on
# value-changed signal timing.
self.echoVoice[acss.ACSS.RATE] = self.get_widget("echoRateScale").get_value()
self.echoVoice[acss.ACSS.AVERAGE_PITCH] = self.get_widget("echoPitchScale").get_value()
self.echoVoice[acss.ACSS.GAIN] = self.get_widget("echoVolumeScale").get_value()
self.echoVoice['established'] = True
self.prefsDict["echoVoice"] = acss.ACSS(self.echoVoice)
if self.echoSpeechServersChoice:
self.prefsDict["echoSpeechServerInfo"] = self.echoSpeechServersChoice.getInfo()
else:
self.prefsDict["echoSpeechServerInfo"] = None
def applyButtonClicked(self, widget): def applyButtonClicked(self, widget):
"""Signal handler for the "clicked" signal for the applyButton """Signal handler for the "clicked" signal for the applyButton
GtkButton widget. The user has clicked the Apply button. GtkButton widget. The user has clicked the Apply button.
@@ -4286,6 +4721,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
self._refresh_dynamic_plugin_tabs() self._refresh_dynamic_plugin_tabs()
braille.checkBrailleSetting() braille.checkBrailleSetting()
self._initSpeechState() self._initSpeechState()
self._initEchoSpeechState()
self._populateKeyBindings() self._populateKeyBindings()
self.__initProfileCombo() self.__initProfileCombo()
@@ -4548,6 +4984,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
braille.checkBrailleSetting() braille.checkBrailleSetting()
self._initSpeechState() self._initSpeechState()
self._initEchoSpeechState()
self._populateKeyBindings() self._populateKeyBindings()
+8 -7
View File
@@ -325,7 +325,6 @@ class AIAssistant(Plugin):
if not self._prefs_widgets: if not self._prefs_widgets:
return return
enabled = self._prefs_widgets["enable_check"].get_active()
provider_values = self._prefs_widgets.get("provider_values", []) provider_values = self._prefs_widgets.get("provider_values", [])
provider_index = self._prefs_widgets["provider_combo"].get_active() provider_index = self._prefs_widgets["provider_combo"].get_active()
provider = provider_values[provider_index] if 0 <= provider_index < len(provider_values) else settings.aiProvider provider = provider_values[provider_index] if 0 <= provider_index < len(provider_values) else settings.aiProvider
@@ -333,12 +332,14 @@ class AIAssistant(Plugin):
is_gemini = provider == settings.AI_PROVIDER_GEMINI is_gemini = provider == settings.AI_PROVIDER_GEMINI
is_ollama = provider == settings.AI_PROVIDER_OLLAMA is_ollama = provider == settings.AI_PROVIDER_OLLAMA
self._prefs_widgets["provider_combo"].set_sensitive(enabled) # Keep preferences editable even when the feature is disabled so users
self._prefs_widgets["api_key_entry"].set_sensitive(enabled and is_gemini) # can prepare configuration before turning AI assistant on.
self._prefs_widgets["ollama_model_entry"].set_sensitive(enabled and is_ollama) self._prefs_widgets["provider_combo"].set_sensitive(True)
self._prefs_widgets["ollama_endpoint_entry"].set_sensitive(enabled and is_ollama) self._prefs_widgets["api_key_entry"].set_sensitive(is_gemini)
self._prefs_widgets["confirmation_check"].set_sensitive(enabled) self._prefs_widgets["ollama_model_entry"].set_sensitive(is_ollama)
self._prefs_widgets["quality_combo"].set_sensitive(enabled) self._prefs_widgets["ollama_endpoint_entry"].set_sensitive(is_ollama)
self._prefs_widgets["confirmation_check"].set_sensitive(True)
self._prefs_widgets["quality_combo"].set_sensitive(True)
def refresh_settings(self): def refresh_settings(self):
"""Refresh plugin settings and reinitialize provider. Called when settings change.""" """Refresh plugin settings and reinitialize provider. Called when settings change."""
+3 -19
View File
@@ -21,10 +21,8 @@ from gi.repository import Gtk, Gdk, Pango
from cthulhu.plugin import Plugin, cthulhu_hookimpl from cthulhu.plugin import Plugin, cthulhu_hookimpl
from cthulhu import cthulhu from cthulhu import cthulhu
from cthulhu import debug from cthulhu import debug
from cthulhu import settings_manager
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager
class PluginManager(Plugin): class PluginManager(Plugin):
@@ -473,25 +471,11 @@ class PluginManager(Plugin):
current_general['activePlugins'] = active_plugins current_general['activePlugins'] = active_plugins
cthulhu.cthulhuApp.settingsManager.profile = profile_name cthulhu.cthulhuApp.settingsManager.profile = profile_name
cthulhu.cthulhuApp.settingsManager._setProfileGeneral(current_general) cthulhu.cthulhuApp.settingsManager.saveProfileSettings(current_general)
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Settings saved via settings manager (profile {profile_name})", True)
pronunciations = cthulhu.cthulhuApp.settingsManager.getPronunciations(profile_name) or {}
keybindings = cthulhu.cthulhuApp.settingsManager.getKeybindings(profile_name) or {}
backend = cthulhu.cthulhuApp.settingsManager._backend
if backend:
backend.saveProfileSettings(
profile_name,
cthulhu.cthulhuApp.settingsManager.profileGeneral,
pronunciations,
keybindings
)
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Settings saved to backend (profile {profile_name})", True)
else:
debug.printMessage(debug.LEVEL_INFO, "PluginManager: No backend available for saving", True)
except Exception as save_error: except Exception as save_error:
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Error saving via backend: {save_error}", True) debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Error saving plugin state: {save_error}", True)
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Updated active plugins: {active_plugins}", True) debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Updated active plugins: {active_plugins}", True)
+3
View File
@@ -5122,6 +5122,9 @@ class Utilities:
if not event.type.startswith("object:text-changed:insert"): if not event.type.startswith("object:text-changed:insert"):
return False return False
if not cthulhu.cthulhuApp.settingsManager.getSetting("enableKeyEcho"):
return False
if AXUtilities.is_focusable(event.source) \ if AXUtilities.is_focusable(event.source) \
and not AXUtilities.is_focused(event.source) \ and not AXUtilities.is_focused(event.source) \
and event.source != cthulhu_state.locusOfFocus: and event.source != cthulhu_state.locusOfFocus:
+75 -4
View File
@@ -44,6 +44,7 @@ from gi.repository import Gdk
import re import re
import time import time
import cthulhu.acss as acss
import cthulhu.braille as braille import cthulhu.braille as braille
import cthulhu.cmdnames as cmdnames import cthulhu.cmdnames as cmdnames
import cthulhu.dbus_service as dbus_service import cthulhu.dbus_service as dbus_service
@@ -1818,6 +1819,9 @@ class Script(script.Script):
if len(string) != 1: if len(string) != 1:
return return
if not cthulhu.cthulhuApp.settingsManager.getSetting('enableKeyEcho'):
return
if cthulhu.cthulhuApp.settingsManager.getSetting('enableEchoBySentence') \ if cthulhu.cthulhuApp.settingsManager.getSetting('enableEchoBySentence') \
and self.echoPreviousSentence(event.source): and self.echoPreviousSentence(event.source):
return return
@@ -1967,6 +1971,15 @@ class Script(script.Script):
debug.printMessage(debug.LEVEL_INFO, msg, True) debug.printMessage(debug.LEVEL_INFO, msg, True)
return return
# Some toolkits emit transient window:deactivate/window:activate pairs
# while the same window remains active. Treat those as noise so we do
# not clear state and force a full script/settings reactivation.
AXObject.clear_cache(event.source)
if AXUtilities.is_active(event.source):
msg = "DEFAULT: Ignoring event. Source window still active."
debug.printMessage(debug.LEVEL_INFO, msg, True)
return
if self.flatReviewPresenter.is_active(): if self.flatReviewPresenter.is_active():
self.flatReviewPresenter.quit() self.flatReviewPresenter.quit()
@@ -2219,9 +2232,12 @@ class Script(script.Script):
sentence = self.utilities.substring(obj, sentenceStartOffset + 1, sentence = self.utilities.substring(obj, sentenceStartOffset + 1,
sentenceEndOffset + 1) sentenceEndOffset + 1)
voice = self.speechGenerator.voice(obj=obj, string=sentence)
sentence = self.utilities.adjustForRepeats(sentence) sentence = self.utilities.adjustForRepeats(sentence)
self.speakMessage(sentence, voice) if self._shouldUseCustomEchoVoice("sentence"):
speech.speakEchoText(sentence, self._getCustomEchoVoice())
else:
voice = self.speechGenerator.voice(obj=obj, string=sentence)
self.speakMessage(sentence, voice)
return True return True
def echoPreviousWord(self, obj, offset=None): def echoPreviousWord(self, obj, offset=None):
@@ -2288,9 +2304,12 @@ class Script(script.Script):
word = self.utilities.\ word = self.utilities.\
substring(obj, wordStartOffset + 1, wordEndOffset + 1) substring(obj, wordStartOffset + 1, wordEndOffset + 1)
voice = self.speechGenerator.voice(obj=obj, string=word)
word = self.utilities.adjustForRepeats(word) word = self.utilities.adjustForRepeats(word)
self.speakMessage(word, voice) if self._shouldUseCustomEchoVoice("word"):
speech.speakEchoText(word, self._getCustomEchoVoice())
else:
voice = self.speechGenerator.voice(obj=obj, string=word)
self.speakMessage(word, voice)
return True return True
def sayCharacter(self, obj): def sayCharacter(self, obj):
@@ -2892,6 +2911,9 @@ class Script(script.Script):
if not event.shouldEcho or event.isCthulhuModified(): if not event.shouldEcho or event.isCthulhuModified():
return False return False
if not cthulhu.cthulhuApp.settingsManager.getSetting('enableKeyEcho'):
return False
role = AXObject.get_role(cthulhu_state.locusOfFocus) role = AXObject.get_role(cthulhu_state.locusOfFocus)
if role in [Atspi.Role.DIALOG, Atspi.Role.FRAME, Atspi.Role.WINDOW]: if role in [Atspi.Role.DIALOG, Atspi.Role.FRAME, Atspi.Role.WINDOW]:
focusedObject = AXUtilities.get_focused_object(cthulhu_state.activeWindow) focusedObject = AXUtilities.get_focused_object(cthulhu_state.activeWindow)
@@ -3332,6 +3354,47 @@ class Script(script.Script):
# # # #
######################################################################## ########################################################################
def _shouldUseCustomEchoVoice(self, echoType):
"""Returns True if custom echo settings should be used for the given type."""
settingForType = {
"key": "useCustomEchoForKey",
"character": "useCustomEchoForCharacter",
"word": "useCustomEchoForWord",
"sentence": "useCustomEchoForSentence",
}
settingName = settingForType.get(echoType)
if not settingName:
return False
settingsManager = cthulhu.cthulhuApp.settingsManager
if not settingsManager.getSetting("enableKeyEcho"):
return False
if not settingsManager.getSetting("useCustomEchoVoice"):
return False
return settingsManager.getSetting(settingName)
def _getCustomEchoVoice(self):
"""Returns the effective ACSS voice for custom echo output."""
settingsManager = cthulhu.cthulhuApp.settingsManager
voices = settingsManager.getSetting("voices") or {}
defaultVoice = acss.ACSS(voices.get(settings.DEFAULT_VOICE, {}))
echoVoice = settingsManager.getSetting("echoVoice")
if not echoVoice:
return defaultVoice
try:
defaultVoice.update(acss.ACSS(echoVoice))
except Exception:
debug.printException(debug.LEVEL_INFO)
return defaultVoice
def speakKeyEvent(self, event): def speakKeyEvent(self, event):
"""Method to speak a keyboard event. Scripts should use this method """Method to speak a keyboard event. Scripts should use this method
rather than calling speech.speakKeyEvent directly.""" rather than calling speech.speakKeyEvent directly."""
@@ -3340,6 +3403,10 @@ class Script(script.Script):
if event.is_printable_key(): if event.is_printable_key():
string = event.event_string string = event.event_string
if self._shouldUseCustomEchoVoice("key"):
speech.speakEchoKeyEvent(event, self._getCustomEchoVoice())
return
voice = self.speechGenerator.voice(string=string) voice = self.speechGenerator.voice(string=string)
speech.speakKeyEvent(event, voice) speech.speakKeyEvent(event, voice)
@@ -3347,6 +3414,10 @@ class Script(script.Script):
"""Method to speak a single character. Scripts should use this """Method to speak a single character. Scripts should use this
method rather than calling speech.speakCharacter directly.""" method rather than calling speech.speakCharacter directly."""
if self._shouldUseCustomEchoVoice("character"):
speech.speakEchoCharacter(character, self._getCustomEchoVoice())
return
voice = self.speechGenerator.voice(string=character) voice = self.speechGenerator.voice(string=character)
speech.speakCharacter(character, voice) speech.speakCharacter(character, voice)
+3 -1
View File
@@ -572,7 +572,9 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
and soundEnabled: and soundEnabled:
roleSoundIcon = sound_theme_manager.getManager().getRoleSoundIcon(role) roleSoundIcon = sound_theme_manager.getManager().getRoleSoundIcon(role)
if roleSoundPresentation == settings.ROLE_SOUND_PRESENTATION_SOUND_ONLY \ if roleSoundPresentation == settings.ROLE_SOUND_PRESENTATION_SOUND_ONLY \
and soundEnabled and roleSoundIcon: and soundEnabled:
# Stateful controls present their role via state sounds; suppress
# spoken role names here even if no dedicated role icon exists.
if AXUtilities.is_check_box(obj) \ if AXUtilities.is_check_box(obj) \
or AXUtilities.is_check_menu_item(obj) \ or AXUtilities.is_check_menu_item(obj) \
or AXUtilities.is_radio_button(obj) \ or AXUtilities.is_radio_button(obj) \
+16
View File
@@ -53,6 +53,14 @@ userCustomizableSettings = [
"enableEchoByWord", "enableEchoByWord",
"enableEchoBySentence", "enableEchoBySentence",
"enableKeyEcho", "enableKeyEcho",
"useCustomEchoVoice",
"useCustomEchoSpeechServer",
"echoSpeechServerInfo",
"echoVoice",
"useCustomEchoForKey",
"useCustomEchoForCharacter",
"useCustomEchoForWord",
"useCustomEchoForSentence",
"gameMode", "gameMode",
"nvda2cthulhuTranslateEnabled", "nvda2cthulhuTranslateEnabled",
"enableAlphabeticKeys", "enableAlphabeticKeys",
@@ -352,6 +360,14 @@ enableDiacriticalKeys = False
enableEchoByCharacter = False enableEchoByCharacter = False
enableEchoByWord = False enableEchoByWord = False
enableEchoBySentence = False enableEchoBySentence = False
useCustomEchoVoice = False
useCustomEchoSpeechServer = False
echoSpeechServerInfo = None
echoVoice = ACSS({})
useCustomEchoForKey = True
useCustomEchoForCharacter = True
useCustomEchoForWord = False
useCustomEchoForSentence = False
presentLockingKeys = None presentLockingKeys = None
# Mouse review # Mouse review
+76 -9
View File
@@ -314,6 +314,13 @@ class SettingsManager(object):
converted_voices[voice_type] = voice_def converted_voices[voice_type] = voice_def
general["voices"] = converted_voices general["voices"] = converted_voices
echo_voice = general.get("echoVoice")
if isinstance(echo_voice, dict):
try:
general["echoVoice"] = ACSS(echo_voice)
except Exception:
general["echoVoice"] = echo_voice
return general return general
def getDefaultSetting(self, settingName: str) -> Any: def getDefaultSetting(self, settingName: str) -> Any:
@@ -379,6 +386,18 @@ class SettingsManager(object):
def getSetting(self, settingName: str) -> Any: def getSetting(self, settingName: str) -> Any:
return getattr(settings, settingName, None) return getattr(settings, settingName, None)
def _getListSetting(self, settingName: str) -> List[str]:
value = self.getSetting(settingName)
if isinstance(value, (list, tuple)):
return [item for item in value if isinstance(item, str)]
return []
def _ensurePluginPersistenceSettings(self, general: Dict[str, Any]) -> None:
if 'activePlugins' not in general:
general['activePlugins'] = self._getListSetting('activePlugins')
if 'pluginSources' not in general:
general['pluginSources'] = self._getListSetting('pluginSources')
def getVoiceLocale(self, voice: str = 'default') -> str: def getVoiceLocale(self, voice: str = 'default') -> str:
voices = self.getSetting('voices') voices = self.getSetting('voices')
v = ACSS(voices.get(voice, {})) v = ACSS(voices.get(voice, {}))
@@ -620,12 +639,24 @@ class SettingsManager(object):
debug.printMessage(debug.LEVEL_INFO, msg, True) debug.printMessage(debug.LEVEL_INFO, msg, True)
self.profileGeneral = {} self.profileGeneral = {}
alwaysPersistKeys = {
'useCustomEchoVoice',
'useCustomEchoSpeechServer',
'useCustomEchoForKey',
'useCustomEchoForCharacter',
'useCustomEchoForWord',
'useCustomEchoForSentence',
}
for key, value in general.items(): for key, value in general.items():
if key in ['startingProfile', 'activeProfile']: if key in ['startingProfile', 'activeProfile']:
continue continue
elif key == 'profile': elif key == 'profile':
self.profileGeneral[key] = value self.profileGeneral[key] = value
elif key in ['activePlugins', 'pluginSources']:
self.profileGeneral[key] = copy.deepcopy(value)
elif key in alwaysPersistKeys:
self.profileGeneral[key] = copy.deepcopy(value)
elif value != self.defaultGeneral.get(key): elif value != self.defaultGeneral.get(key):
self.profileGeneral[key] = value self.profileGeneral[key] = value
elif self.general.get(key) != value: elif self.general.get(key) != value:
@@ -634,6 +665,46 @@ class SettingsManager(object):
msg = 'SETTINGS MANAGER: General settings for profile set' msg = 'SETTINGS MANAGER: General settings for profile set'
debug.printMessage(debug.LEVEL_INFO, msg, True) debug.printMessage(debug.LEVEL_INFO, msg, True)
def saveProfileSettings(
self,
general: Dict[str, Any],
pronunciations: Optional[Dict[str, Any]] = None,
keybindings: Optional[Dict[str, Any]] = None,
) -> None:
profileName: Optional[str] = self.profile
profileSetting = general.get('profile')
if isinstance(profileSetting, (list, tuple)) and len(profileSetting) > 1:
profileName = profileSetting[1]
elif not profileName:
defaultProfile = settings.profile
if isinstance(defaultProfile, (list, tuple)) and len(defaultProfile) > 1:
profileName = defaultProfile[1]
else:
profileName = 'default'
self.profile = profileName
generalCopy = dict(general)
self._ensurePluginPersistenceSettings(generalCopy)
self._setProfileGeneral(generalCopy)
if self.profile is None:
return
profileName = self.profile
if pronunciations is None:
pronunciations = self.getPronunciations(profileName) or {}
if keybindings is None:
keybindings = self.getKeybindings(profileName) or {}
self._setProfilePronunciations(pronunciations)
self._setProfileKeybindings(keybindings)
if self._backend:
self._backend.saveProfileSettings(self.profile,
self.profileGeneral,
self.profilePronunciations,
self.profileKeybindings)
def _setProfilePronunciations(self, pronunciations: Dict[str, Any]) -> None: def _setProfilePronunciations(self, pronunciations: Dict[str, Any]) -> None:
"""Set the changed pronunciations settings from the defaults' ones """Set the changed pronunciations settings from the defaults' ones
as the profile's.""" as the profile's."""
@@ -664,9 +735,12 @@ class SettingsManager(object):
if not self._backend: if not self._backend:
return return
profileScopedKeys = {'activePlugins', 'pluginSources'}
appGeneral = {} appGeneral = {}
profileGeneral = self.getGeneralSettings(self.profile) if self.profile else {} profileGeneral = self.getGeneralSettings(self.profile) if self.profile else {}
for key, value in general.items(): for key, value in general.items():
if key in profileScopedKeys:
continue
if value != profileGeneral.get(key): if value != profileGeneral.get(key):
appGeneral[key] = value appGeneral[key] = value
@@ -705,24 +779,17 @@ class SettingsManager(object):
currentProfile = _profile[1] currentProfile = _profile[1]
self.profile = currentProfile self.profile = currentProfile
self._ensurePluginPersistenceSettings(general)
# Elements that need to stay updated in main configuration. # Elements that need to stay updated in main configuration.
self.defaultGeneral['startingProfile'] = general.get('startingProfile', self.defaultGeneral['startingProfile'] = general.get('startingProfile',
_profile) _profile)
self._setProfileGeneral(general) self.saveProfileSettings(general, pronunciations, keybindings)
self._setProfilePronunciations(pronunciations)
self._setProfileKeybindings(keybindings)
tokens = ["SETTINGS MANAGER: Saving for backend", self._backend] tokens = ["SETTINGS MANAGER: Saving for backend", self._backend]
debug.printTokens(debug.LEVEL_INFO, tokens, True) debug.printTokens(debug.LEVEL_INFO, tokens, True)
if self._backend and self.profile:
self._backend.saveProfileSettings(self.profile,
self.profileGeneral,
self.profilePronunciations,
self.profileKeybindings)
tokens = ["SETTINGS MANAGER: Settings for", script, "(app:", script.app, ") saved"] tokens = ["SETTINGS MANAGER: Settings for", script, "(app:", script.app, ") saved"]
debug.printTokens(debug.LEVEL_INFO, tokens, True) debug.printTokens(debug.LEVEL_INFO, tokens, True)
return self._enableAccessibility() return self._enableAccessibility()
+129 -11
View File
@@ -69,6 +69,7 @@ def _ensureLogger() -> None:
# The speech server to use for all speech operations. # The speech server to use for all speech operations.
# #
_speechserver: Optional[SpeechServer] = None _speechserver: Optional[SpeechServer] = None
_echoSpeechserver: Optional[SpeechServer] = None
# The last time something was spoken. # The last time something was spoken.
_timestamp: float = 0.0 _timestamp: float = 0.0
@@ -76,12 +77,15 @@ _timestamp: float = 0.0
# Optional callback for live monitoring of spoken text. # Optional callback for live monitoring of spoken text.
_monitorWriteTextCallback: Optional[Callable[[str], None]] = None _monitorWriteTextCallback: Optional[Callable[[str], None]] = None
def _initSpeechServer(moduleName: Optional[str], speechServerInfo: Optional[Any]) -> None: def _isSpeechDispatcherFactory(moduleName: Optional[str]) -> bool:
if not moduleName:
return False
return moduleName.split(".")[-1] == "speechdispatcherfactory"
global _speechserver def _initSpeechServer(moduleName: Optional[str], speechServerInfo: Optional[Any]) -> SpeechServer:
if not moduleName: if not moduleName:
return raise Exception("ERROR: No speech server module name provided")
factory = None factory = None
try: try:
@@ -94,35 +98,58 @@ def _initSpeechServer(moduleName: Optional[str], speechServerInfo: Optional[Any]
# Now, get the speech server we care about. # Now, get the speech server we care about.
# #
speechServerInfo = settings.speechServerInfo speech_server = None
if factory: if factory:
if speechServerInfo: if speechServerInfo:
_speechserver = factory.SpeechServer.getSpeechServer(speechServerInfo) # type: ignore speech_server = factory.SpeechServer.getSpeechServer(speechServerInfo) # type: ignore
if not _speechserver: if not speech_server:
_speechserver = factory.SpeechServer.getSpeechServer() # type: ignore speech_server = factory.SpeechServer.getSpeechServer() # type: ignore
if speechServerInfo: if speechServerInfo:
tokens = ["SPEECH: Invalid speechServerInfo:", speechServerInfo] tokens = ["SPEECH: Invalid speechServerInfo:", speechServerInfo]
debug.printTokens(debug.LEVEL_INFO, tokens, True) debug.printTokens(debug.LEVEL_INFO, tokens, True)
if not _speechserver: if not speech_server:
raise Exception(f"ERROR: No speech server for factory: {moduleName}") raise Exception(f"ERROR: No speech server for factory: {moduleName}")
return speech_server
def _refreshEchoSpeechServer() -> None:
global _echoSpeechserver
_echoSpeechserver = None
if not settings.enableSpeech or not settings.useCustomEchoSpeechServer:
return
if not _isSpeechDispatcherFactory(settings.speechServerFactory):
return
try:
_echoSpeechserver = _initSpeechServer(
settings.speechServerFactory,
settings.echoSpeechServerInfo
)
except Exception:
debug.printException(debug.LEVEL_INFO)
_echoSpeechserver = None
def init() -> None: def init() -> None:
global _speechserver
debug.printMessage(debug.LEVEL_INFO, 'SPEECH: Initializing', True) debug.printMessage(debug.LEVEL_INFO, 'SPEECH: Initializing', True)
if _speechserver: if _speechserver:
debug.printMessage(debug.LEVEL_INFO, 'SPEECH: Already initialized', True) debug.printMessage(debug.LEVEL_INFO, 'SPEECH: Already initialized', True)
_refreshEchoSpeechServer()
return return
chosenModuleName = settings.speechServerFactory chosenModuleName = settings.speechServerFactory
try: try:
_initSpeechServer(chosenModuleName, settings.speechServerInfo) _speechserver = _initSpeechServer(chosenModuleName, settings.speechServerInfo)
except Exception: except Exception:
moduleNames = settings.speechFactoryModules moduleNames = settings.speechFactoryModules
for moduleName in moduleNames: for moduleName in moduleNames:
if moduleName != settings.speechServerFactory: if moduleName != settings.speechServerFactory:
try: try:
_initSpeechServer(moduleName, None) _speechserver = _initSpeechServer(moduleName, None)
if _speechserver: if _speechserver:
chosenModuleName = moduleName chosenModuleName = moduleName
break break
@@ -144,6 +171,7 @@ def init() -> None:
msg = 'SPEECH: Not available' msg = 'SPEECH: Not available'
debug.printMessage(debug.LEVEL_INFO, msg, True) debug.printMessage(debug.LEVEL_INFO, msg, True)
_refreshEchoSpeechServer()
debug.printMessage(debug.LEVEL_INFO, 'SPEECH: Initialized', True) debug.printMessage(debug.LEVEL_INFO, 'SPEECH: Initialized', True)
def checkSpeechSetting() -> None: def checkSpeechSetting() -> None:
@@ -157,6 +185,9 @@ def checkSpeechSetting() -> None:
if not _speechserver: if not _speechserver:
init() init()
return
_refreshEchoSpeechServer()
def getSpeechServer() -> Optional[SpeechServer]: def getSpeechServer() -> Optional[SpeechServer]:
"""Returns the speech server instance.""" """Returns the speech server instance."""
@@ -170,6 +201,7 @@ def setSpeechServer(speechServer: SpeechServer) -> None:
""" """
global _speechserver global _speechserver
_speechserver = speechServer _speechserver = speechServer
_refreshEchoSpeechServer()
def set_monitor_callbacks(writeText: Optional[Callable[[str], None]] = None) -> None: def set_monitor_callbacks(writeText: Optional[Callable[[str], None]] = None) -> None:
"""Sets runtime callbacks for live speech monitoring.""" """Sets runtime callbacks for live speech monitoring."""
@@ -418,6 +450,35 @@ def speakKeyEvent(event: Any, acss: Optional[Any] = None) -> None:
if _speechserver: if _speechserver:
_speechserver.speakKeyEvent(event, acss) # type: ignore _speechserver.speakKeyEvent(event, acss) # type: ignore
def speakEchoKeyEvent(event: Any, acss: Optional[Any] = None) -> None:
"""Speaks a key event for key/typing echo using echo speech settings."""
_ensureLogger()
if settings.silenceSpeech:
return
global _timestamp
if _timestamp:
msg = f"SPEECH: Last spoke {time.time() - _timestamp:.4f} seconds ago"
debug.printMessage(debug.LEVEL_INFO, msg, True)
_timestamp = time.time()
keyname = event.getKeyName()
lockingStateString = event.getLockingStateString()
acss = __resolveACSS(acss)
msg = f"{keyname} {lockingStateString}"
logLine = f"SPEECH ECHO OUTPUT: '{msg.strip()}' {acss}"
debug.printMessage(debug.LEVEL_INFO, logLine, True)
if log:
log.info(logLine)
_write_to_monitor(msg.strip())
server = _echoSpeechserver or _speechserver
if server:
server.speakKeyEvent(event, acss) # type: ignore
def speakCharacter(character: str, acss: Optional[Any] = None) -> None: def speakCharacter(character: str, acss: Optional[Any] = None) -> None:
"""Speaks a single character immediately. """Speaks a single character immediately.
@@ -452,6 +513,58 @@ def speakCharacter(character: str, acss: Optional[Any] = None) -> None:
if _speechserver: if _speechserver:
_speechserver.speakCharacter(character, acss=acss) # type: ignore _speechserver.speakCharacter(character, acss=acss) # type: ignore
def speakEchoCharacter(character: str, acss: Optional[Any] = None) -> None:
"""Speaks a single character for key/typing echo using echo speech settings."""
_ensureLogger()
if settings.silenceSpeech:
return
global _timestamp
if _timestamp:
msg = f"SPEECH: Last spoke {time.time() - _timestamp:.4f} seconds ago"
debug.printMessage(debug.LEVEL_INFO, msg, True)
_timestamp = time.time()
acss = __resolveACSS(acss)
tokens = [f"SPEECH ECHO OUTPUT: '{character}'", acss]
debug.printTokens(debug.LEVEL_INFO, tokens, True)
if log:
log.info(f"SPEECH ECHO OUTPUT: '{character}'")
_write_to_monitor(character)
server = _echoSpeechserver or _speechserver
if server:
server.speakCharacter(character, acss=acss) # type: ignore
def speakEchoText(text: str, acss: Optional[Any] = None, interrupt: bool = True) -> None:
"""Speaks text for typing echo using echo speech settings."""
_ensureLogger()
if settings.silenceSpeech:
return
global _timestamp
if _timestamp:
msg = f"SPEECH: Last spoke {time.time() - _timestamp:.4f} seconds ago"
debug.printMessage(debug.LEVEL_INFO, msg, True)
_timestamp = time.time()
acss = __resolveACSS(acss)
logLine = f"SPEECH ECHO OUTPUT: '{text}' {acss}"
debug.printMessage(debug.LEVEL_INFO, logLine, True)
if log:
log.info(logLine)
_write_to_monitor(text)
server = _echoSpeechserver or _speechserver
if server:
server.speak(text, acss, interrupt) # type: ignore
def getInfo() -> Optional[Any]: def getInfo() -> Optional[Any]:
info = None info = None
if _speechserver: if _speechserver:
@@ -462,13 +575,18 @@ def getInfo() -> Optional[Any]:
def stop() -> None: def stop() -> None:
if _speechserver: if _speechserver:
_speechserver.stop() # type: ignore _speechserver.stop() # type: ignore
if _echoSpeechserver and _echoSpeechserver != _speechserver:
_echoSpeechserver.stop() # type: ignore
def shutdown() -> None: def shutdown() -> None:
debug.printMessage(debug.LEVEL_INFO, 'SPEECH: Shutting down', True) debug.printMessage(debug.LEVEL_INFO, 'SPEECH: Shutting down', True)
global _speechserver global _speechserver, _echoSpeechserver
if _speechserver: if _speechserver:
_speechserver.shutdownActiveServers() # type: ignore _speechserver.shutdownActiveServers() # type: ignore
_speechserver = None _speechserver = None
elif _echoSpeechserver:
_echoSpeechserver.shutdownActiveServers() # type: ignore
_echoSpeechserver = None
def reset(text: Optional[str] = None, acss: Optional[Any] = None) -> None: def reset(text: Optional[str] = None, acss: Optional[Any] = None) -> None:
if _speechserver: if _speechserver:

Some files were not shown because too many files have changed in this diff Show More