Remove files that should be ignored based on .gitignore
This commit is contained in:
-655
@@ -1,655 +0,0 @@
|
||||
# Makefile.in generated by automake 1.17 from Makefile.am.
|
||||
# src/Makefile. Generated from Makefile.in by configure.
|
||||
|
||||
# Copyright (C) 1994-2024 Free Software Foundation, Inc.
|
||||
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
|
||||
|
||||
am__is_gnu_make = { \
|
||||
if test -z '$(MAKELEVEL)'; then \
|
||||
false; \
|
||||
elif test -n '$(MAKE_HOST)'; then \
|
||||
true; \
|
||||
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
|
||||
true; \
|
||||
else \
|
||||
false; \
|
||||
fi; \
|
||||
}
|
||||
am__make_running_with_option = \
|
||||
case $${target_option-} in \
|
||||
?) ;; \
|
||||
*) echo "am__make_running_with_option: internal error: invalid" \
|
||||
"target option '$${target_option-}' specified" >&2; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
has_opt=no; \
|
||||
sane_makeflags=$$MAKEFLAGS; \
|
||||
if $(am__is_gnu_make); then \
|
||||
sane_makeflags=$$MFLAGS; \
|
||||
else \
|
||||
case $$MAKEFLAGS in \
|
||||
*\\[\ \ ]*) \
|
||||
bs=\\; \
|
||||
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
|
||||
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
|
||||
esac; \
|
||||
fi; \
|
||||
skip_next=no; \
|
||||
strip_trailopt () \
|
||||
{ \
|
||||
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
|
||||
}; \
|
||||
for flg in $$sane_makeflags; do \
|
||||
test $$skip_next = yes && { skip_next=no; continue; }; \
|
||||
case $$flg in \
|
||||
*=*|--*) continue;; \
|
||||
-*I) strip_trailopt 'I'; skip_next=yes;; \
|
||||
-*I?*) strip_trailopt 'I';; \
|
||||
-*O) strip_trailopt 'O'; skip_next=yes;; \
|
||||
-*O?*) strip_trailopt 'O';; \
|
||||
-*l) strip_trailopt 'l'; skip_next=yes;; \
|
||||
-*l?*) strip_trailopt 'l';; \
|
||||
-[dEDm]) skip_next=yes;; \
|
||||
-[JT]) skip_next=yes;; \
|
||||
esac; \
|
||||
case $$flg in \
|
||||
*$$target_option*) has_opt=yes; break;; \
|
||||
esac; \
|
||||
done; \
|
||||
test $$has_opt = yes
|
||||
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
|
||||
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
|
||||
am__rm_f = rm -f $(am__rm_f_notfound)
|
||||
am__rm_rf = rm -rf $(am__rm_f_notfound)
|
||||
pkgdatadir = $(datadir)/cthulhu
|
||||
pkgincludedir = $(includedir)/cthulhu
|
||||
pkglibdir = $(libdir)/cthulhu
|
||||
pkglibexecdir = $(libexecdir)/cthulhu
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
INSTALL_HEADER = $(INSTALL_DATA)
|
||||
transform = $(program_transform_name)
|
||||
NORMAL_INSTALL = :
|
||||
PRE_INSTALL = :
|
||||
POST_INSTALL = :
|
||||
NORMAL_UNINSTALL = :
|
||||
PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = x86_64-pc-linux-gnu
|
||||
host_triplet = x86_64-pc-linux-gnu
|
||||
subdir = src
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/m4/build-to-host.m4 \
|
||||
$(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/host-cpu-c-abi.m4 \
|
||||
$(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \
|
||||
$(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
|
||||
$(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/nls.m4 \
|
||||
$(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \
|
||||
$(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/yelp.m4 \
|
||||
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
|
||||
mkinstalldirs = $(install_sh) -d
|
||||
CONFIG_CLEAN_FILES =
|
||||
CONFIG_CLEAN_VPATH_FILES =
|
||||
AM_V_P = $(am__v_P_$(V))
|
||||
am__v_P_ = $(am__v_P_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_P_0 = false
|
||||
am__v_P_1 = :
|
||||
AM_V_GEN = $(am__v_GEN_$(V))
|
||||
am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_GEN_0 = @echo " GEN " $@;
|
||||
am__v_GEN_1 =
|
||||
AM_V_at = $(am__v_at_$(V))
|
||||
am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_at_0 = @
|
||||
am__v_at_1 =
|
||||
SOURCES =
|
||||
DIST_SOURCES =
|
||||
RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
|
||||
ctags-recursive dvi-recursive html-recursive info-recursive \
|
||||
install-data-recursive install-dvi-recursive \
|
||||
install-exec-recursive install-html-recursive \
|
||||
install-info-recursive install-pdf-recursive \
|
||||
install-ps-recursive install-recursive installcheck-recursive \
|
||||
installdirs-recursive pdf-recursive ps-recursive \
|
||||
tags-recursive uninstall-recursive
|
||||
am__can_run_installinfo = \
|
||||
case $$AM_UPDATE_INFO_DIR in \
|
||||
n|no|NO) false;; \
|
||||
*) (install-info --version) >/dev/null 2>&1;; \
|
||||
esac
|
||||
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
|
||||
distclean-recursive maintainer-clean-recursive
|
||||
am__recursive_targets = \
|
||||
$(RECURSIVE_TARGETS) \
|
||||
$(RECURSIVE_CLEAN_TARGETS) \
|
||||
$(am__extra_recursive_targets)
|
||||
AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
|
||||
distdir distdir-am
|
||||
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
|
||||
# Read a list of newline-separated strings from the standard input,
|
||||
# and print each of them once, without duplicates. Input order is
|
||||
# *not* preserved.
|
||||
am__uniquify_input = $(AWK) '\
|
||||
BEGIN { nonempty = 0; } \
|
||||
{ items[$$0] = 1; nonempty = 1; } \
|
||||
END { if (nonempty) { for (i in items) print i; }; } \
|
||||
'
|
||||
# Make sure the list of sources is unique. This is necessary because,
|
||||
# e.g., the same source file might be shared among _SOURCES variables
|
||||
# for different programs/libraries.
|
||||
am__define_uniq_tagged_files = \
|
||||
list='$(am__tagged_files)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | $(am__uniquify_input)`
|
||||
DIST_SUBDIRS = $(SUBDIRS)
|
||||
am__DIST_COMMON = $(srcdir)/Makefile.in
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
am__relativize = \
|
||||
dir0=`pwd`; \
|
||||
sed_first='s,^\([^/]*\)/.*$$,\1,'; \
|
||||
sed_rest='s,^[^/]*/*,,'; \
|
||||
sed_last='s,^.*/\([^/]*\)$$,\1,'; \
|
||||
sed_butlast='s,/*[^/]*$$,,'; \
|
||||
while test -n "$$dir1"; do \
|
||||
first=`echo "$$dir1" | sed -e "$$sed_first"`; \
|
||||
if test "$$first" != "."; then \
|
||||
if test "$$first" = ".."; then \
|
||||
dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
|
||||
dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
|
||||
else \
|
||||
first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
|
||||
if test "$$first2" = "$$first"; then \
|
||||
dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
|
||||
else \
|
||||
dir2="../$$dir2"; \
|
||||
fi; \
|
||||
dir0="$$dir0"/"$$first"; \
|
||||
fi; \
|
||||
fi; \
|
||||
dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
|
||||
done; \
|
||||
reldir="$$dir2"
|
||||
ACLOCAL = ${SHELL} '/home/storm/cthulhutmp/cthulhu/missing' aclocal-1.17
|
||||
AMTAR = $${TAR-tar}
|
||||
AM_DEFAULT_VERBOSITY = 1
|
||||
ATKBRIDGE_CFLAGS = -I/usr/include/at-spi2-atk/2.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/at-spi-2.0 -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/sysprof-6 -pthread -I/usr/include/atk-1.0 -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include
|
||||
ATKBRIDGE_LIBS = -latk-bridge-2.0
|
||||
ATSPI2_CFLAGS = -I/usr/include/at-spi-2.0 -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/sysprof-6 -pthread
|
||||
ATSPI2_LIBS = -latspi -ldbus-1 -lglib-2.0
|
||||
AUTOCONF = ${SHELL} '/home/storm/cthulhutmp/cthulhu/missing' autoconf
|
||||
AUTOHEADER = ${SHELL} '/home/storm/cthulhutmp/cthulhu/missing' autoheader
|
||||
AUTOMAKE = ${SHELL} '/home/storm/cthulhutmp/cthulhu/missing' automake-1.17
|
||||
AWK = gawk
|
||||
CC = gcc
|
||||
CCDEPMODE = depmode=none
|
||||
CFLAGS = -g -O2
|
||||
CPP = gcc -E
|
||||
CPPFLAGS =
|
||||
CSCOPE = cscope
|
||||
CTAGS = ctags
|
||||
CYGPATH_W = echo
|
||||
DEFS = -DPACKAGE_NAME=\"cthulhu\" -DPACKAGE_TARNAME=\"cthulhu\" -DPACKAGE_VERSION=\"45.3\" -DPACKAGE_STRING=\"cthulhu\ 45.3\" -DPACKAGE_BUGREPORT=\"https://gitlab.gnome.org/GNOME/cthulhu/-/issues/\" -DPACKAGE_URL=\"\" -DPACKAGE=\"cthulhu\" -DVERSION=\"45.3\" -DENABLE_NLS=1 -DHAVE_GETTEXT=1 -DHAVE_DCGETTEXT=1 -DGETTEXT_PACKAGE=\"cthulhu\"
|
||||
DEPDIR = .deps
|
||||
ECHO_C =
|
||||
ECHO_N = -n
|
||||
ECHO_T =
|
||||
ETAGS = etags
|
||||
EXEEXT =
|
||||
GETTEXT_MACRO_VERSION = 0.20
|
||||
GETTEXT_PACKAGE = cthulhu
|
||||
GMSGFMT = /usr/bin/msgfmt
|
||||
GMSGFMT_015 = /usr/bin/msgfmt
|
||||
GSTREAMER_CFLAGS = -I/usr/include/gstreamer-1.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/sysprof-6 -pthread
|
||||
GSTREAMER_LIBS = -lgstreamer-1.0 -lgobject-2.0 -lglib-2.0
|
||||
HELP_DIR = ${datadir}/help
|
||||
INSTALL = /usr/bin/install -c
|
||||
INSTALL_DATA = ${INSTALL} -m 644
|
||||
INSTALL_PROGRAM = ${INSTALL}
|
||||
INSTALL_SCRIPT = ${INSTALL}
|
||||
INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
|
||||
INTLLIBS =
|
||||
INTL_MACOSX_LIBS =
|
||||
ITSTOOL = itstool
|
||||
LDFLAGS =
|
||||
LIBICONV = -liconv
|
||||
LIBINTL =
|
||||
LIBOBJS =
|
||||
LIBS =
|
||||
LN_S = ln -s
|
||||
LOUIS_TABLE_DIR = /usr/share/liblouis/tables
|
||||
LTLIBICONV = -liconv
|
||||
LTLIBINTL =
|
||||
LTLIBOBJS =
|
||||
MAINT =
|
||||
MAKEINFO = ${SHELL} '/home/storm/cthulhutmp/cthulhu/missing' makeinfo
|
||||
MKDIR_P = /usr/bin/mkdir -p
|
||||
MSGFMT = /usr/bin/msgfmt
|
||||
MSGMERGE = /usr/bin/msgmerge
|
||||
MSGMERGE_FOR_MSGFMT_OPTION = --for-msgfmt
|
||||
OBJEXT = o
|
||||
PACKAGE = cthulhu
|
||||
PACKAGE_BUGREPORT = https://gitlab.gnome.org/GNOME/cthulhu/-/issues/
|
||||
PACKAGE_NAME = cthulhu
|
||||
PACKAGE_STRING = cthulhu 45.3
|
||||
PACKAGE_TARNAME = cthulhu
|
||||
PACKAGE_URL =
|
||||
PACKAGE_VERSION = 45.3
|
||||
PATH_SEPARATOR = :
|
||||
PKG_CONFIG = /usr/bin/pkg-config
|
||||
PKG_CONFIG_LIBDIR =
|
||||
PKG_CONFIG_PATH =
|
||||
PLATFORM_PATH = :/usr/bin:/usr/sbin:/bin
|
||||
POSUB = po
|
||||
PYGOBJECT_CFLAGS = -I/usr/include/pygobject-3.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/sysprof-6 -pthread
|
||||
PYGOBJECT_LIBS = -lgobject-2.0 -lglib-2.0
|
||||
PYTHON = /home/storm/.pyenv/shims/python
|
||||
PYTHON_EXEC_PREFIX = ${exec_prefix}
|
||||
PYTHON_PLATFORM = linux
|
||||
PYTHON_PREFIX = ${prefix}
|
||||
PYTHON_VERSION = 3.12
|
||||
REVISION = fb25228b6
|
||||
SED = /usr/bin/sed
|
||||
SET_MAKE =
|
||||
SHELL = /bin/sh
|
||||
STRIP =
|
||||
USE_NLS = yes
|
||||
VERSION = 45.3
|
||||
XGETTEXT = /usr/bin/xgettext
|
||||
XGETTEXT_015 = /usr/bin/xgettext
|
||||
XGETTEXT_EXTRA_OPTIONS =
|
||||
XMLLINT = xmllint
|
||||
YELP_LC_DIST = true
|
||||
YELP_LC_MEDIA_LINKS = true
|
||||
abs_builddir = /home/storm/cthulhutmp/cthulhu/src
|
||||
abs_srcdir = /home/storm/cthulhutmp/cthulhu/src
|
||||
abs_top_builddir = /home/storm/cthulhutmp/cthulhu
|
||||
abs_top_srcdir = /home/storm/cthulhutmp/cthulhu
|
||||
ac_ct_CC = gcc
|
||||
am__include = include
|
||||
am__leading_dot = .
|
||||
am__quote =
|
||||
am__rm_f_notfound =
|
||||
am__tar = tar --format=ustar -chf - "$$tardir"
|
||||
am__untar = tar -xf -
|
||||
am__xargs_n = xargs -n
|
||||
bindir = ${exec_prefix}/bin
|
||||
build = x86_64-pc-linux-gnu
|
||||
build_alias =
|
||||
build_cpu = x86_64
|
||||
build_os = linux-gnu
|
||||
build_vendor = pc
|
||||
builddir = .
|
||||
datadir = ${datarootdir}
|
||||
datarootdir = ${prefix}/share
|
||||
docdir = ${datarootdir}/doc/${PACKAGE_TARNAME}
|
||||
dvidir = ${docdir}
|
||||
exec_prefix = ${prefix}
|
||||
host = x86_64-pc-linux-gnu
|
||||
host_alias =
|
||||
host_cpu = x86_64
|
||||
host_os = linux-gnu
|
||||
host_vendor = pc
|
||||
htmldir = ${docdir}
|
||||
includedir = ${prefix}/include
|
||||
infodir = ${datarootdir}/info
|
||||
install_sh = ${SHELL} /home/storm/cthulhutmp/cthulhu/install-sh
|
||||
libdir = ${exec_prefix}/lib
|
||||
libexecdir = ${exec_prefix}/libexec
|
||||
localedir = ${datarootdir}/locale
|
||||
localedir_c = "/usr/local/share/locale"
|
||||
localedir_c_make = \"$(localedir)\"
|
||||
localstatedir = /var
|
||||
mandir = ${datarootdir}/man
|
||||
mkdir_p = $(MKDIR_P)
|
||||
oldincludedir = /usr/include
|
||||
pdfdir = ${docdir}
|
||||
pkgpyexecdir = ${pyexecdir}/cthulhu
|
||||
pkgpythondir = ${pythondir}/cthulhu
|
||||
prefix = /usr/local
|
||||
program_transform_name = s,x,x,
|
||||
psdir = ${docdir}
|
||||
pyexecdir = ${PYTHON_EXEC_PREFIX}/lib/python3.12/site-packages
|
||||
pythondir = ${PYTHON_PREFIX}/lib/python3.12/site-packages
|
||||
runstatedir = ${localstatedir}/run
|
||||
sbindir = ${exec_prefix}/sbin
|
||||
sharedstatedir = ${prefix}/com
|
||||
srcdir = .
|
||||
sysconfdir = /etc
|
||||
target_alias =
|
||||
top_build_prefix = ../
|
||||
top_builddir = ..
|
||||
top_srcdir = ..
|
||||
SUBDIRS = cthulhu
|
||||
all: all-recursive
|
||||
|
||||
.SUFFIXES:
|
||||
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
|
||||
@for dep in $?; do \
|
||||
case '$(am__configure_deps)' in \
|
||||
*$$dep*) \
|
||||
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
|
||||
&& { if test -f $@; then exit 0; else break; fi; }; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
done; \
|
||||
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
|
||||
$(am__cd) $(top_srcdir) && \
|
||||
$(AUTOMAKE) --gnu src/Makefile
|
||||
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
|
||||
@case '$?' in \
|
||||
*config.status*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
|
||||
*) \
|
||||
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
|
||||
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
|
||||
esac;
|
||||
|
||||
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
$(top_srcdir)/configure: $(am__configure_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(am__aclocal_m4_deps):
|
||||
|
||||
# This directory's subdirectories are mostly independent; you can cd
|
||||
# into them and run 'make' without going through this Makefile.
|
||||
# To change the values of 'make' variables: instead of editing Makefiles,
|
||||
# (1) if the variable is set in 'config.status', edit 'config.status'
|
||||
# (which will cause the Makefiles to be regenerated when you run 'make');
|
||||
# (2) otherwise, pass the desired values on the 'make' command line.
|
||||
$(am__recursive_targets):
|
||||
@fail=; \
|
||||
if $(am__make_keepgoing); then \
|
||||
failcom='fail=yes'; \
|
||||
else \
|
||||
failcom='exit 1'; \
|
||||
fi; \
|
||||
dot_seen=no; \
|
||||
target=`echo $@ | sed s/-recursive//`; \
|
||||
case "$@" in \
|
||||
distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
|
||||
*) list='$(SUBDIRS)' ;; \
|
||||
esac; \
|
||||
for subdir in $$list; do \
|
||||
echo "Making $$target in $$subdir"; \
|
||||
if test "$$subdir" = "."; then \
|
||||
dot_seen=yes; \
|
||||
local_target="$$target-am"; \
|
||||
else \
|
||||
local_target="$$target"; \
|
||||
fi; \
|
||||
($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
|
||||
|| eval $$failcom; \
|
||||
done; \
|
||||
if test "$$dot_seen" = "no"; then \
|
||||
$(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
|
||||
fi; test -z "$$fail"
|
||||
|
||||
ID: $(am__tagged_files)
|
||||
$(am__define_uniq_tagged_files); mkid -fID $$unique
|
||||
tags: tags-recursive
|
||||
TAGS: tags
|
||||
|
||||
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
|
||||
set x; \
|
||||
here=`pwd`; \
|
||||
if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
|
||||
include_option=--etags-include; \
|
||||
empty_fix=.; \
|
||||
else \
|
||||
include_option=--include; \
|
||||
empty_fix=; \
|
||||
fi; \
|
||||
list='$(SUBDIRS)'; for subdir in $$list; do \
|
||||
if test "$$subdir" = .; then :; else \
|
||||
test ! -f $$subdir/TAGS || \
|
||||
set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
|
||||
fi; \
|
||||
done; \
|
||||
$(am__define_uniq_tagged_files); \
|
||||
shift; \
|
||||
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
|
||||
test -n "$$unique" || unique=$$empty_fix; \
|
||||
if test $$# -gt 0; then \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
"$$@" $$unique; \
|
||||
else \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
$$unique; \
|
||||
fi; \
|
||||
fi
|
||||
ctags: ctags-recursive
|
||||
|
||||
CTAGS: ctags
|
||||
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
|
||||
$(am__define_uniq_tagged_files); \
|
||||
test -z "$(CTAGS_ARGS)$$unique" \
|
||||
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
|
||||
$$unique
|
||||
|
||||
GTAGS:
|
||||
here=`$(am__cd) $(top_builddir) && pwd` \
|
||||
&& $(am__cd) $(top_srcdir) \
|
||||
&& gtags -i $(GTAGS_ARGS) "$$here"
|
||||
cscopelist: cscopelist-recursive
|
||||
|
||||
cscopelist-am: $(am__tagged_files)
|
||||
list='$(am__tagged_files)'; \
|
||||
case "$(srcdir)" in \
|
||||
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
|
||||
*) sdir=$(subdir)/$(srcdir) ;; \
|
||||
esac; \
|
||||
for i in $$list; do \
|
||||
if test -f "$$i"; then \
|
||||
echo "$(subdir)/$$i"; \
|
||||
else \
|
||||
echo "$$sdir/$$i"; \
|
||||
fi; \
|
||||
done >> $(top_builddir)/cscope.files
|
||||
|
||||
distclean-tags:
|
||||
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
|
||||
distdir: $(BUILT_SOURCES)
|
||||
$(MAKE) $(AM_MAKEFLAGS) distdir-am
|
||||
|
||||
distdir-am: $(DISTFILES)
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
list='$(DISTFILES)'; \
|
||||
dist_files=`for file in $$list; do echo $$file; done | \
|
||||
sed -e "s|^$$srcdirstrip/||;t" \
|
||||
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
|
||||
case $$dist_files in \
|
||||
*/*) $(MKDIR_P) `echo "$$dist_files" | \
|
||||
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
|
||||
sort -u` ;; \
|
||||
esac; \
|
||||
for file in $$dist_files; do \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test -d "$(distdir)/$$file"; then \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
|
||||
else \
|
||||
test -f "$(distdir)/$$file" \
|
||||
|| cp -p $$d/$$file "$(distdir)/$$file" \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
|
||||
if test "$$subdir" = .; then :; else \
|
||||
$(am__make_dryrun) \
|
||||
|| test -d "$(distdir)/$$subdir" \
|
||||
|| $(MKDIR_P) "$(distdir)/$$subdir" \
|
||||
|| exit 1; \
|
||||
dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
|
||||
$(am__relativize); \
|
||||
new_distdir=$$reldir; \
|
||||
dir1=$$subdir; dir2="$(top_distdir)"; \
|
||||
$(am__relativize); \
|
||||
new_top_distdir=$$reldir; \
|
||||
echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
|
||||
echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
|
||||
($(am__cd) $$subdir && \
|
||||
$(MAKE) $(AM_MAKEFLAGS) \
|
||||
top_distdir="$$new_top_distdir" \
|
||||
distdir="$$new_distdir" \
|
||||
am__remove_distdir=: \
|
||||
am__skip_length_check=: \
|
||||
am__skip_mode_fix=: \
|
||||
distdir) \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
check-am: all-am
|
||||
check: check-recursive
|
||||
all-am: Makefile
|
||||
installdirs: installdirs-recursive
|
||||
installdirs-am:
|
||||
install: install-recursive
|
||||
install-exec: install-exec-recursive
|
||||
install-data: install-data-recursive
|
||||
uninstall: uninstall-recursive
|
||||
|
||||
install-am: all-am
|
||||
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
|
||||
|
||||
installcheck: installcheck-recursive
|
||||
install-strip:
|
||||
if test -z '$(STRIP)'; then \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
install; \
|
||||
else \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
|
||||
fi
|
||||
mostlyclean-generic:
|
||||
|
||||
clean-generic:
|
||||
|
||||
distclean-generic:
|
||||
-$(am__rm_f) $(CONFIG_CLEAN_FILES)
|
||||
-test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
|
||||
|
||||
maintainer-clean-generic:
|
||||
@echo "This command is intended for maintainers to use"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
clean: clean-recursive
|
||||
|
||||
clean-am: clean-generic mostlyclean-am
|
||||
|
||||
distclean: distclean-recursive
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-generic distclean-tags
|
||||
|
||||
dvi: dvi-recursive
|
||||
|
||||
dvi-am:
|
||||
|
||||
html: html-recursive
|
||||
|
||||
html-am:
|
||||
|
||||
info: info-recursive
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am:
|
||||
|
||||
install-dvi: install-dvi-recursive
|
||||
|
||||
install-dvi-am:
|
||||
|
||||
install-exec-am:
|
||||
|
||||
install-html: install-html-recursive
|
||||
|
||||
install-html-am:
|
||||
|
||||
install-info: install-info-recursive
|
||||
|
||||
install-info-am:
|
||||
|
||||
install-man:
|
||||
|
||||
install-pdf: install-pdf-recursive
|
||||
|
||||
install-pdf-am:
|
||||
|
||||
install-ps: install-ps-recursive
|
||||
|
||||
install-ps-am:
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-recursive
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
mostlyclean: mostlyclean-recursive
|
||||
|
||||
mostlyclean-am: mostlyclean-generic
|
||||
|
||||
pdf: pdf-recursive
|
||||
|
||||
pdf-am:
|
||||
|
||||
ps: ps-recursive
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am:
|
||||
|
||||
.MAKE: $(am__recursive_targets) install-am install-strip
|
||||
|
||||
.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
|
||||
check-am clean clean-generic cscopelist-am ctags ctags-am \
|
||||
distclean distclean-generic distclean-tags distdir dvi dvi-am \
|
||||
html html-am info info-am install install-am install-data \
|
||||
install-data-am install-dvi install-dvi-am install-exec \
|
||||
install-exec-am install-html install-html-am install-info \
|
||||
install-info-am install-man install-pdf install-pdf-am \
|
||||
install-ps install-ps-am install-strip installcheck \
|
||||
installcheck-am installdirs installdirs-am maintainer-clean \
|
||||
maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
|
||||
pdf-am ps ps-am tags tags-am uninstall uninstall-am
|
||||
|
||||
.PRECIOUS: Makefile
|
||||
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
||||
|
||||
# Tell GNU make to disable its built-in pattern rules.
|
||||
%:: %,v
|
||||
%:: RCS/%,v
|
||||
%:: RCS/%
|
||||
%:: s.%
|
||||
%:: SCCS/s.%
|
||||
@@ -1 +0,0 @@
|
||||
SUBDIRS = cthulhu
|
||||
-655
@@ -1,655 +0,0 @@
|
||||
# Makefile.in generated by automake 1.17 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994-2024 Free Software Foundation, Inc.
|
||||
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
@SET_MAKE@
|
||||
VPATH = @srcdir@
|
||||
am__is_gnu_make = { \
|
||||
if test -z '$(MAKELEVEL)'; then \
|
||||
false; \
|
||||
elif test -n '$(MAKE_HOST)'; then \
|
||||
true; \
|
||||
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
|
||||
true; \
|
||||
else \
|
||||
false; \
|
||||
fi; \
|
||||
}
|
||||
am__make_running_with_option = \
|
||||
case $${target_option-} in \
|
||||
?) ;; \
|
||||
*) echo "am__make_running_with_option: internal error: invalid" \
|
||||
"target option '$${target_option-}' specified" >&2; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
has_opt=no; \
|
||||
sane_makeflags=$$MAKEFLAGS; \
|
||||
if $(am__is_gnu_make); then \
|
||||
sane_makeflags=$$MFLAGS; \
|
||||
else \
|
||||
case $$MAKEFLAGS in \
|
||||
*\\[\ \ ]*) \
|
||||
bs=\\; \
|
||||
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
|
||||
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
|
||||
esac; \
|
||||
fi; \
|
||||
skip_next=no; \
|
||||
strip_trailopt () \
|
||||
{ \
|
||||
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
|
||||
}; \
|
||||
for flg in $$sane_makeflags; do \
|
||||
test $$skip_next = yes && { skip_next=no; continue; }; \
|
||||
case $$flg in \
|
||||
*=*|--*) continue;; \
|
||||
-*I) strip_trailopt 'I'; skip_next=yes;; \
|
||||
-*I?*) strip_trailopt 'I';; \
|
||||
-*O) strip_trailopt 'O'; skip_next=yes;; \
|
||||
-*O?*) strip_trailopt 'O';; \
|
||||
-*l) strip_trailopt 'l'; skip_next=yes;; \
|
||||
-*l?*) strip_trailopt 'l';; \
|
||||
-[dEDm]) skip_next=yes;; \
|
||||
-[JT]) skip_next=yes;; \
|
||||
esac; \
|
||||
case $$flg in \
|
||||
*$$target_option*) has_opt=yes; break;; \
|
||||
esac; \
|
||||
done; \
|
||||
test $$has_opt = yes
|
||||
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
|
||||
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
|
||||
am__rm_f = rm -f $(am__rm_f_notfound)
|
||||
am__rm_rf = rm -rf $(am__rm_f_notfound)
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkglibexecdir = $(libexecdir)/@PACKAGE@
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
INSTALL_HEADER = $(INSTALL_DATA)
|
||||
transform = $(program_transform_name)
|
||||
NORMAL_INSTALL = :
|
||||
PRE_INSTALL = :
|
||||
POST_INSTALL = :
|
||||
NORMAL_UNINSTALL = :
|
||||
PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
subdir = src
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/m4/build-to-host.m4 \
|
||||
$(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/host-cpu-c-abi.m4 \
|
||||
$(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \
|
||||
$(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
|
||||
$(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/nls.m4 \
|
||||
$(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \
|
||||
$(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/yelp.m4 \
|
||||
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
|
||||
mkinstalldirs = $(install_sh) -d
|
||||
CONFIG_CLEAN_FILES =
|
||||
CONFIG_CLEAN_VPATH_FILES =
|
||||
AM_V_P = $(am__v_P_@AM_V@)
|
||||
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
|
||||
am__v_P_0 = false
|
||||
am__v_P_1 = :
|
||||
AM_V_GEN = $(am__v_GEN_@AM_V@)
|
||||
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
|
||||
am__v_GEN_0 = @echo " GEN " $@;
|
||||
am__v_GEN_1 =
|
||||
AM_V_at = $(am__v_at_@AM_V@)
|
||||
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
|
||||
am__v_at_0 = @
|
||||
am__v_at_1 =
|
||||
SOURCES =
|
||||
DIST_SOURCES =
|
||||
RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
|
||||
ctags-recursive dvi-recursive html-recursive info-recursive \
|
||||
install-data-recursive install-dvi-recursive \
|
||||
install-exec-recursive install-html-recursive \
|
||||
install-info-recursive install-pdf-recursive \
|
||||
install-ps-recursive install-recursive installcheck-recursive \
|
||||
installdirs-recursive pdf-recursive ps-recursive \
|
||||
tags-recursive uninstall-recursive
|
||||
am__can_run_installinfo = \
|
||||
case $$AM_UPDATE_INFO_DIR in \
|
||||
n|no|NO) false;; \
|
||||
*) (install-info --version) >/dev/null 2>&1;; \
|
||||
esac
|
||||
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
|
||||
distclean-recursive maintainer-clean-recursive
|
||||
am__recursive_targets = \
|
||||
$(RECURSIVE_TARGETS) \
|
||||
$(RECURSIVE_CLEAN_TARGETS) \
|
||||
$(am__extra_recursive_targets)
|
||||
AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
|
||||
distdir distdir-am
|
||||
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
|
||||
# Read a list of newline-separated strings from the standard input,
|
||||
# and print each of them once, without duplicates. Input order is
|
||||
# *not* preserved.
|
||||
am__uniquify_input = $(AWK) '\
|
||||
BEGIN { nonempty = 0; } \
|
||||
{ items[$$0] = 1; nonempty = 1; } \
|
||||
END { if (nonempty) { for (i in items) print i; }; } \
|
||||
'
|
||||
# Make sure the list of sources is unique. This is necessary because,
|
||||
# e.g., the same source file might be shared among _SOURCES variables
|
||||
# for different programs/libraries.
|
||||
am__define_uniq_tagged_files = \
|
||||
list='$(am__tagged_files)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | $(am__uniquify_input)`
|
||||
DIST_SUBDIRS = $(SUBDIRS)
|
||||
am__DIST_COMMON = $(srcdir)/Makefile.in
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
am__relativize = \
|
||||
dir0=`pwd`; \
|
||||
sed_first='s,^\([^/]*\)/.*$$,\1,'; \
|
||||
sed_rest='s,^[^/]*/*,,'; \
|
||||
sed_last='s,^.*/\([^/]*\)$$,\1,'; \
|
||||
sed_butlast='s,/*[^/]*$$,,'; \
|
||||
while test -n "$$dir1"; do \
|
||||
first=`echo "$$dir1" | sed -e "$$sed_first"`; \
|
||||
if test "$$first" != "."; then \
|
||||
if test "$$first" = ".."; then \
|
||||
dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
|
||||
dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
|
||||
else \
|
||||
first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
|
||||
if test "$$first2" = "$$first"; then \
|
||||
dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
|
||||
else \
|
||||
dir2="../$$dir2"; \
|
||||
fi; \
|
||||
dir0="$$dir0"/"$$first"; \
|
||||
fi; \
|
||||
fi; \
|
||||
dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
|
||||
done; \
|
||||
reldir="$$dir2"
|
||||
ACLOCAL = @ACLOCAL@
|
||||
AMTAR = @AMTAR@
|
||||
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
|
||||
ATKBRIDGE_CFLAGS = @ATKBRIDGE_CFLAGS@
|
||||
ATKBRIDGE_LIBS = @ATKBRIDGE_LIBS@
|
||||
ATSPI2_CFLAGS = @ATSPI2_CFLAGS@
|
||||
ATSPI2_LIBS = @ATSPI2_LIBS@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
AWK = @AWK@
|
||||
CC = @CC@
|
||||
CCDEPMODE = @CCDEPMODE@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPP = @CPP@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
CSCOPE = @CSCOPE@
|
||||
CTAGS = @CTAGS@
|
||||
CYGPATH_W = @CYGPATH_W@
|
||||
DEFS = @DEFS@
|
||||
DEPDIR = @DEPDIR@
|
||||
ECHO_C = @ECHO_C@
|
||||
ECHO_N = @ECHO_N@
|
||||
ECHO_T = @ECHO_T@
|
||||
ETAGS = @ETAGS@
|
||||
EXEEXT = @EXEEXT@
|
||||
GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
|
||||
GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
|
||||
GMSGFMT = @GMSGFMT@
|
||||
GMSGFMT_015 = @GMSGFMT_015@
|
||||
GSTREAMER_CFLAGS = @GSTREAMER_CFLAGS@
|
||||
GSTREAMER_LIBS = @GSTREAMER_LIBS@
|
||||
HELP_DIR = @HELP_DIR@
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
INTLLIBS = @INTLLIBS@
|
||||
INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
|
||||
ITSTOOL = @ITSTOOL@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBICONV = @LIBICONV@
|
||||
LIBINTL = @LIBINTL@
|
||||
LIBOBJS = @LIBOBJS@
|
||||
LIBS = @LIBS@
|
||||
LN_S = @LN_S@
|
||||
LOUIS_TABLE_DIR = @LOUIS_TABLE_DIR@
|
||||
LTLIBICONV = @LTLIBICONV@
|
||||
LTLIBINTL = @LTLIBINTL@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
MAINT = @MAINT@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
MKDIR_P = @MKDIR_P@
|
||||
MSGFMT = @MSGFMT@
|
||||
MSGMERGE = @MSGMERGE@
|
||||
MSGMERGE_FOR_MSGFMT_OPTION = @MSGMERGE_FOR_MSGFMT_OPTION@
|
||||
OBJEXT = @OBJEXT@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
PACKAGE_STRING = @PACKAGE_STRING@
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
PACKAGE_URL = @PACKAGE_URL@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
PKG_CONFIG = @PKG_CONFIG@
|
||||
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
|
||||
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
|
||||
PLATFORM_PATH = @PLATFORM_PATH@
|
||||
POSUB = @POSUB@
|
||||
PYGOBJECT_CFLAGS = @PYGOBJECT_CFLAGS@
|
||||
PYGOBJECT_LIBS = @PYGOBJECT_LIBS@
|
||||
PYTHON = @PYTHON@
|
||||
PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
|
||||
PYTHON_PLATFORM = @PYTHON_PLATFORM@
|
||||
PYTHON_PREFIX = @PYTHON_PREFIX@
|
||||
PYTHON_VERSION = @PYTHON_VERSION@
|
||||
REVISION = @REVISION@
|
||||
SED = @SED@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
STRIP = @STRIP@
|
||||
USE_NLS = @USE_NLS@
|
||||
VERSION = @VERSION@
|
||||
XGETTEXT = @XGETTEXT@
|
||||
XGETTEXT_015 = @XGETTEXT_015@
|
||||
XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
|
||||
XMLLINT = @XMLLINT@
|
||||
YELP_LC_DIST = @YELP_LC_DIST@
|
||||
YELP_LC_MEDIA_LINKS = @YELP_LC_MEDIA_LINKS@
|
||||
abs_builddir = @abs_builddir@
|
||||
abs_srcdir = @abs_srcdir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
am__include = @am__include@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
am__rm_f_notfound = @am__rm_f_notfound@
|
||||
am__tar = @am__tar@
|
||||
am__untar = @am__untar@
|
||||
am__xargs_n = @am__xargs_n@
|
||||
bindir = @bindir@
|
||||
build = @build@
|
||||
build_alias = @build_alias@
|
||||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
builddir = @builddir@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = @docdir@
|
||||
dvidir = @dvidir@
|
||||
exec_prefix = @exec_prefix@
|
||||
host = @host@
|
||||
host_alias = @host_alias@
|
||||
host_cpu = @host_cpu@
|
||||
host_os = @host_os@
|
||||
host_vendor = @host_vendor@
|
||||
htmldir = @htmldir@
|
||||
includedir = @includedir@
|
||||
infodir = @infodir@
|
||||
install_sh = @install_sh@
|
||||
libdir = @libdir@
|
||||
libexecdir = @libexecdir@
|
||||
localedir = @localedir@
|
||||
localedir_c = @localedir_c@
|
||||
localedir_c_make = @localedir_c_make@
|
||||
localstatedir = @localstatedir@
|
||||
mandir = @mandir@
|
||||
mkdir_p = @mkdir_p@
|
||||
oldincludedir = @oldincludedir@
|
||||
pdfdir = @pdfdir@
|
||||
pkgpyexecdir = @pkgpyexecdir@
|
||||
pkgpythondir = @pkgpythondir@
|
||||
prefix = @prefix@
|
||||
program_transform_name = @program_transform_name@
|
||||
psdir = @psdir@
|
||||
pyexecdir = @pyexecdir@
|
||||
pythondir = @pythondir@
|
||||
runstatedir = @runstatedir@
|
||||
sbindir = @sbindir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
srcdir = @srcdir@
|
||||
sysconfdir = @sysconfdir@
|
||||
target_alias = @target_alias@
|
||||
top_build_prefix = @top_build_prefix@
|
||||
top_builddir = @top_builddir@
|
||||
top_srcdir = @top_srcdir@
|
||||
SUBDIRS = cthulhu
|
||||
all: all-recursive
|
||||
|
||||
.SUFFIXES:
|
||||
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
|
||||
@for dep in $?; do \
|
||||
case '$(am__configure_deps)' in \
|
||||
*$$dep*) \
|
||||
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
|
||||
&& { if test -f $@; then exit 0; else break; fi; }; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
done; \
|
||||
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
|
||||
$(am__cd) $(top_srcdir) && \
|
||||
$(AUTOMAKE) --gnu src/Makefile
|
||||
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
|
||||
@case '$?' in \
|
||||
*config.status*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
|
||||
*) \
|
||||
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
|
||||
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
|
||||
esac;
|
||||
|
||||
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(am__aclocal_m4_deps):
|
||||
|
||||
# This directory's subdirectories are mostly independent; you can cd
|
||||
# into them and run 'make' without going through this Makefile.
|
||||
# To change the values of 'make' variables: instead of editing Makefiles,
|
||||
# (1) if the variable is set in 'config.status', edit 'config.status'
|
||||
# (which will cause the Makefiles to be regenerated when you run 'make');
|
||||
# (2) otherwise, pass the desired values on the 'make' command line.
|
||||
$(am__recursive_targets):
|
||||
@fail=; \
|
||||
if $(am__make_keepgoing); then \
|
||||
failcom='fail=yes'; \
|
||||
else \
|
||||
failcom='exit 1'; \
|
||||
fi; \
|
||||
dot_seen=no; \
|
||||
target=`echo $@ | sed s/-recursive//`; \
|
||||
case "$@" in \
|
||||
distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
|
||||
*) list='$(SUBDIRS)' ;; \
|
||||
esac; \
|
||||
for subdir in $$list; do \
|
||||
echo "Making $$target in $$subdir"; \
|
||||
if test "$$subdir" = "."; then \
|
||||
dot_seen=yes; \
|
||||
local_target="$$target-am"; \
|
||||
else \
|
||||
local_target="$$target"; \
|
||||
fi; \
|
||||
($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
|
||||
|| eval $$failcom; \
|
||||
done; \
|
||||
if test "$$dot_seen" = "no"; then \
|
||||
$(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
|
||||
fi; test -z "$$fail"
|
||||
|
||||
ID: $(am__tagged_files)
|
||||
$(am__define_uniq_tagged_files); mkid -fID $$unique
|
||||
tags: tags-recursive
|
||||
TAGS: tags
|
||||
|
||||
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
|
||||
set x; \
|
||||
here=`pwd`; \
|
||||
if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
|
||||
include_option=--etags-include; \
|
||||
empty_fix=.; \
|
||||
else \
|
||||
include_option=--include; \
|
||||
empty_fix=; \
|
||||
fi; \
|
||||
list='$(SUBDIRS)'; for subdir in $$list; do \
|
||||
if test "$$subdir" = .; then :; else \
|
||||
test ! -f $$subdir/TAGS || \
|
||||
set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
|
||||
fi; \
|
||||
done; \
|
||||
$(am__define_uniq_tagged_files); \
|
||||
shift; \
|
||||
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
|
||||
test -n "$$unique" || unique=$$empty_fix; \
|
||||
if test $$# -gt 0; then \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
"$$@" $$unique; \
|
||||
else \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
$$unique; \
|
||||
fi; \
|
||||
fi
|
||||
ctags: ctags-recursive
|
||||
|
||||
CTAGS: ctags
|
||||
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
|
||||
$(am__define_uniq_tagged_files); \
|
||||
test -z "$(CTAGS_ARGS)$$unique" \
|
||||
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
|
||||
$$unique
|
||||
|
||||
GTAGS:
|
||||
here=`$(am__cd) $(top_builddir) && pwd` \
|
||||
&& $(am__cd) $(top_srcdir) \
|
||||
&& gtags -i $(GTAGS_ARGS) "$$here"
|
||||
cscopelist: cscopelist-recursive
|
||||
|
||||
cscopelist-am: $(am__tagged_files)
|
||||
list='$(am__tagged_files)'; \
|
||||
case "$(srcdir)" in \
|
||||
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
|
||||
*) sdir=$(subdir)/$(srcdir) ;; \
|
||||
esac; \
|
||||
for i in $$list; do \
|
||||
if test -f "$$i"; then \
|
||||
echo "$(subdir)/$$i"; \
|
||||
else \
|
||||
echo "$$sdir/$$i"; \
|
||||
fi; \
|
||||
done >> $(top_builddir)/cscope.files
|
||||
|
||||
distclean-tags:
|
||||
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
|
||||
distdir: $(BUILT_SOURCES)
|
||||
$(MAKE) $(AM_MAKEFLAGS) distdir-am
|
||||
|
||||
distdir-am: $(DISTFILES)
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
list='$(DISTFILES)'; \
|
||||
dist_files=`for file in $$list; do echo $$file; done | \
|
||||
sed -e "s|^$$srcdirstrip/||;t" \
|
||||
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
|
||||
case $$dist_files in \
|
||||
*/*) $(MKDIR_P) `echo "$$dist_files" | \
|
||||
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
|
||||
sort -u` ;; \
|
||||
esac; \
|
||||
for file in $$dist_files; do \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test -d "$(distdir)/$$file"; then \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
|
||||
else \
|
||||
test -f "$(distdir)/$$file" \
|
||||
|| cp -p $$d/$$file "$(distdir)/$$file" \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
|
||||
if test "$$subdir" = .; then :; else \
|
||||
$(am__make_dryrun) \
|
||||
|| test -d "$(distdir)/$$subdir" \
|
||||
|| $(MKDIR_P) "$(distdir)/$$subdir" \
|
||||
|| exit 1; \
|
||||
dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
|
||||
$(am__relativize); \
|
||||
new_distdir=$$reldir; \
|
||||
dir1=$$subdir; dir2="$(top_distdir)"; \
|
||||
$(am__relativize); \
|
||||
new_top_distdir=$$reldir; \
|
||||
echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
|
||||
echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
|
||||
($(am__cd) $$subdir && \
|
||||
$(MAKE) $(AM_MAKEFLAGS) \
|
||||
top_distdir="$$new_top_distdir" \
|
||||
distdir="$$new_distdir" \
|
||||
am__remove_distdir=: \
|
||||
am__skip_length_check=: \
|
||||
am__skip_mode_fix=: \
|
||||
distdir) \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
check-am: all-am
|
||||
check: check-recursive
|
||||
all-am: Makefile
|
||||
installdirs: installdirs-recursive
|
||||
installdirs-am:
|
||||
install: install-recursive
|
||||
install-exec: install-exec-recursive
|
||||
install-data: install-data-recursive
|
||||
uninstall: uninstall-recursive
|
||||
|
||||
install-am: all-am
|
||||
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
|
||||
|
||||
installcheck: installcheck-recursive
|
||||
install-strip:
|
||||
if test -z '$(STRIP)'; then \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
install; \
|
||||
else \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
|
||||
fi
|
||||
mostlyclean-generic:
|
||||
|
||||
clean-generic:
|
||||
|
||||
distclean-generic:
|
||||
-$(am__rm_f) $(CONFIG_CLEAN_FILES)
|
||||
-test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
|
||||
|
||||
maintainer-clean-generic:
|
||||
@echo "This command is intended for maintainers to use"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
clean: clean-recursive
|
||||
|
||||
clean-am: clean-generic mostlyclean-am
|
||||
|
||||
distclean: distclean-recursive
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-generic distclean-tags
|
||||
|
||||
dvi: dvi-recursive
|
||||
|
||||
dvi-am:
|
||||
|
||||
html: html-recursive
|
||||
|
||||
html-am:
|
||||
|
||||
info: info-recursive
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am:
|
||||
|
||||
install-dvi: install-dvi-recursive
|
||||
|
||||
install-dvi-am:
|
||||
|
||||
install-exec-am:
|
||||
|
||||
install-html: install-html-recursive
|
||||
|
||||
install-html-am:
|
||||
|
||||
install-info: install-info-recursive
|
||||
|
||||
install-info-am:
|
||||
|
||||
install-man:
|
||||
|
||||
install-pdf: install-pdf-recursive
|
||||
|
||||
install-pdf-am:
|
||||
|
||||
install-ps: install-ps-recursive
|
||||
|
||||
install-ps-am:
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-recursive
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
mostlyclean: mostlyclean-recursive
|
||||
|
||||
mostlyclean-am: mostlyclean-generic
|
||||
|
||||
pdf: pdf-recursive
|
||||
|
||||
pdf-am:
|
||||
|
||||
ps: ps-recursive
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am:
|
||||
|
||||
.MAKE: $(am__recursive_targets) install-am install-strip
|
||||
|
||||
.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
|
||||
check-am clean clean-generic cscopelist-am ctags ctags-am \
|
||||
distclean distclean-generic distclean-tags distdir dvi dvi-am \
|
||||
html html-am info info-am install install-am install-data \
|
||||
install-data-am install-dvi install-dvi-am install-exec \
|
||||
install-exec-am install-html install-html-am install-info \
|
||||
install-info-am install-man install-pdf install-pdf-am \
|
||||
install-ps install-ps-am install-strip installcheck \
|
||||
installcheck-am installdirs installdirs-am maintainer-clean \
|
||||
maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
|
||||
pdf-am ps ps-am tags tags-am uninstall uninstall-am
|
||||
|
||||
.PRECIOUS: Makefile
|
||||
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
||||
|
||||
# Tell GNU make to disable its built-in pattern rules.
|
||||
%:: %,v
|
||||
%:: RCS/%,v
|
||||
%:: RCS/%
|
||||
%:: s.%
|
||||
%:: SCCS/s.%
|
||||
@@ -1,9 +0,0 @@
|
||||
Makefile
|
||||
Makefile.in
|
||||
cthulhu
|
||||
cthulhu-setup
|
||||
cthulhu_bin.py
|
||||
cthulhu_i18n.py
|
||||
cthulhu_platform.py
|
||||
*.pyc
|
||||
*~
|
||||
@@ -1,111 +0,0 @@
|
||||
cthulhu_SCRIPTS = \
|
||||
cthulhu
|
||||
|
||||
cthulhudir=$(bindir)
|
||||
uidir=$(datadir)/cthulhu/ui
|
||||
gfxdir=$(datadir)/cthulhu/gfx
|
||||
|
||||
cthulhu_python_PYTHON = \
|
||||
__init__.py \
|
||||
acss.py \
|
||||
action_presenter.py \
|
||||
ax_collection.py \
|
||||
ax_event_synthesizer.py \
|
||||
ax_object.py \
|
||||
ax_selection.py \
|
||||
ax_utilities.py \
|
||||
ax_utilities_collection.py \
|
||||
ax_utilities_role.py \
|
||||
ax_utilities_state.py \
|
||||
bookmarks.py \
|
||||
braille.py \
|
||||
braille_generator.py \
|
||||
braille_rolenames.py \
|
||||
brlmon.py \
|
||||
brltablenames.py \
|
||||
caret_navigation.py \
|
||||
chat.py \
|
||||
chnames.py \
|
||||
cmdnames.py \
|
||||
colornames.py \
|
||||
common_keyboardmap.py \
|
||||
date_and_time_presenter.py \
|
||||
debug.py \
|
||||
desktop_keyboardmap.py \
|
||||
event_manager.py \
|
||||
find.py \
|
||||
flat_review.py \
|
||||
flat_review_presenter.py \
|
||||
formatting.py \
|
||||
generator.py \
|
||||
guilabels.py \
|
||||
highlighter.py \
|
||||
input_event.py \
|
||||
keybindings.py \
|
||||
keynames.py \
|
||||
label_inference.py \
|
||||
laptop_keyboardmap.py \
|
||||
learn_mode_presenter.py \
|
||||
liveregions.py \
|
||||
logger.py \
|
||||
mathsymbols.py \
|
||||
messages.py \
|
||||
mouse_review.py \
|
||||
notification_presenter.py \
|
||||
object_navigator.py \
|
||||
object_properties.py \
|
||||
cthulhu.py \
|
||||
cthulhu_gtkbuilder.py \
|
||||
cthulhu_gui_find.py \
|
||||
cthulhu_gui_navlist.py \
|
||||
cthulhu_gui_prefs.py \
|
||||
cthulhu_gui_profile.py \
|
||||
cthulhu_i18n.py \
|
||||
cthulhu_state.py \
|
||||
phonnames.py \
|
||||
cthulhu_platform.py \
|
||||
pronunciation_dict.py \
|
||||
punctuation_settings.py \
|
||||
script.py \
|
||||
script_manager.py \
|
||||
script_utilities.py \
|
||||
settings.py \
|
||||
settings_manager.py \
|
||||
sound.py \
|
||||
sound_generator.py \
|
||||
speech_and_verbosity_manager.py \
|
||||
speech.py \
|
||||
spellcheck.py \
|
||||
speechdispatcherfactory.py \
|
||||
speech_generator.py \
|
||||
speechserver.py \
|
||||
structural_navigation.py \
|
||||
text_attribute_names.py \
|
||||
tutorialgenerator.py \
|
||||
where_am_i_presenter.py
|
||||
|
||||
cthulhu_pythondir=$(pkgpythondir)
|
||||
|
||||
SUBDIRS = \
|
||||
scripts \
|
||||
backends
|
||||
|
||||
ui_DATA = \
|
||||
cthulhu-find.ui \
|
||||
cthulhu-setup.ui
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(ui_DATA) \
|
||||
$(gfx_DATA)
|
||||
|
||||
CLEANFILES = \
|
||||
cthulhu \
|
||||
cthulhu_bin.py \
|
||||
cthulhu_i18n.py \
|
||||
cthulhu_platform.py
|
||||
|
||||
DISTCLEANFILES = \
|
||||
cthulhu \
|
||||
cthulhu_bin.py \
|
||||
cthulhu_i18n.py \
|
||||
cthulhu_platform.py
|
||||
@@ -1,6 +0,0 @@
|
||||
"""Cthulhu Screen Reader"""
|
||||
|
||||
__copyright__ = "Copyright (c) 2005-2006 Sun Microsystems Inc."
|
||||
__license__ = "LGPL"
|
||||
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
# Cthulhu
|
||||
#
|
||||
# Copyright 2005-2008 Google Inc.
|
||||
# Portions Copyright 2007-2008, Sun Microsystems, Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
#
|
||||
"""ACSS --- Aural CSS.
|
||||
|
||||
Class ACSS defines a simple wrapper for holding ACSS voice
|
||||
definitions. Speech engines implement the code for converting
|
||||
ACSS definitions into engine-specific markup codes.
|
||||
|
||||
"""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__author__ = "T. V. Raman"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2005-2008 Google Inc."
|
||||
__license__ = "LGPL"
|
||||
|
||||
class ACSS(dict):
|
||||
|
||||
"""Holds ACSS representation of a voice."""
|
||||
|
||||
FAMILY = 'family'
|
||||
RATE = 'rate'
|
||||
GAIN = 'gain'
|
||||
AVERAGE_PITCH = 'average-pitch'
|
||||
PITCH_RANGE = 'pitch-range'
|
||||
STRESS = 'stress'
|
||||
RICHNESS = 'richness'
|
||||
PUNCTUATIONS = 'punctuations'
|
||||
|
||||
# A value of None means use the engine's default value.
|
||||
#
|
||||
settings = {
|
||||
FAMILY : None,
|
||||
RATE : 50,
|
||||
GAIN : 10,
|
||||
AVERAGE_PITCH : 5,
|
||||
PITCH_RANGE : 5,
|
||||
STRESS : 5,
|
||||
RICHNESS : 5,
|
||||
PUNCTUATIONS : 'all'
|
||||
}
|
||||
|
||||
def __init__(self, props=None):
|
||||
"""Create and initialize ACSS structure."""
|
||||
dict.__init__(self)
|
||||
props = props or {}
|
||||
if props:
|
||||
for k in props:
|
||||
if k == 'established' or k in ACSS.settings:
|
||||
# Do a 'deep copy' of the family. Otherwise,
|
||||
# the new ACSS shares the actual data with the
|
||||
# props passed in. This can cause unexpected
|
||||
# side effects.
|
||||
#
|
||||
if k == ACSS.FAMILY:
|
||||
self[k] = {}
|
||||
for j in props[k].keys():
|
||||
self[k][j] = props[k][j]
|
||||
else:
|
||||
self[k] = props[k]
|
||||
else:
|
||||
self['established'] = False
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, ACSS):
|
||||
return False
|
||||
if self.get(ACSS.FAMILY) != other.get(ACSS.FAMILY):
|
||||
return False
|
||||
if self.get(ACSS.RATE) != other.get(ACSS.RATE):
|
||||
return False
|
||||
if self.get(ACSS.AVERAGE_PITCH) != other.get(ACSS.AVERAGE_PITCH):
|
||||
return False
|
||||
return True
|
||||
|
||||
def __setitem__ (self, key, value):
|
||||
"""Update name when we change values."""
|
||||
dict.__setitem__(self, key, value)
|
||||
|
||||
def __delitem__(self, key):
|
||||
"""Update name if we delete a key."""
|
||||
dict.__delitem__(self, key)
|
||||
|
||||
def name(self):
|
||||
_name = 'acss-'
|
||||
names = list(self.keys())
|
||||
if names:
|
||||
names.sort()
|
||||
for k in names:
|
||||
_name += f"{k}-{self[k]}:"
|
||||
_name = _name[:-1]
|
||||
return _name
|
||||
|
||||
def getLocale(self):
|
||||
family = self.get(ACSS.FAMILY, {})
|
||||
return family.get('locale')
|
||||
|
||||
def getDialect(self):
|
||||
family = self.get(ACSS.FAMILY, {})
|
||||
return family.get('dialect')
|
||||
|
||||
def update(self, newDict):
|
||||
family = newDict.get(ACSS.FAMILY)
|
||||
if isinstance(family, dict) and family.get('name') is None:
|
||||
newDict.pop(ACSS.FAMILY)
|
||||
|
||||
return super().update(newDict)
|
||||
@@ -1,178 +0,0 @@
|
||||
# Cthulhu
|
||||
#
|
||||
# Copyright 2023 Igalia, S.L.
|
||||
# Author: Joanmarie Diggs <jdiggs@igalia.com>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""Module for performing accessible actions via a menu"""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2023 Igalia, S.L."
|
||||
__license__ = "LGPL"
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gdk", "3.0")
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gdk, Gtk
|
||||
|
||||
from . import cmdnames
|
||||
from . import debug
|
||||
from . import input_event
|
||||
from . import keybindings
|
||||
from . import messages
|
||||
from . import cthulhu
|
||||
from . import cthulhu_state
|
||||
from .ax_object import AXObject
|
||||
|
||||
class ActionPresenter:
|
||||
"""Provides menu for performing accessible actions on an object."""
|
||||
|
||||
def __init__(self):
|
||||
self._handlers = self._setup_handlers()
|
||||
self._bindings = self._setup_bindings()
|
||||
self._gui = None
|
||||
self._obj = None
|
||||
|
||||
def get_bindings(self):
|
||||
"""Returns the action-presenter keybindings."""
|
||||
|
||||
return self._bindings
|
||||
|
||||
def get_handlers(self):
|
||||
"""Returns the action-presenter handlers."""
|
||||
|
||||
return self._handlers
|
||||
|
||||
def _setup_handlers(self):
|
||||
"""Sets up and returns the action-presenter input event handlers."""
|
||||
|
||||
handlers = {}
|
||||
|
||||
handlers["show_actions_menu"] = \
|
||||
input_event.InputEventHandler(
|
||||
self.show_actions_menu,
|
||||
cmdnames.SHOW_ACTIONS_MENU)
|
||||
|
||||
return handlers
|
||||
|
||||
def _setup_bindings(self):
|
||||
"""Sets up and returns the action-presenter key bindings."""
|
||||
|
||||
bindings = keybindings.KeyBindings()
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"a",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.ORCA_SHIFT_MODIFIER_MASK,
|
||||
self._handlers.get("show_actions_menu")))
|
||||
|
||||
return bindings
|
||||
|
||||
def _perform_action(self, action):
|
||||
"""Attempts to perform the named action."""
|
||||
|
||||
result = AXObject.do_named_action(self._obj, action)
|
||||
tokens = ["ACTION PRESENTER: Performing", action, "on", self._obj, "succeeded:", result]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
self._gui = None
|
||||
|
||||
def show_actions_menu(self, script, event=None):
|
||||
"""Shows a menu with all the available accessible actions."""
|
||||
|
||||
obj = cthulhu.getActiveModeAndObjectOfInterest()[1] or cthulhu_state.locusOfFocus
|
||||
if obj is None:
|
||||
full = messages.LOCATION_NOT_FOUND_FULL
|
||||
brief = messages.LOCATION_NOT_FOUND_BRIEF
|
||||
script.presentMessage(full, brief)
|
||||
return True
|
||||
|
||||
actions = {}
|
||||
for i in range(AXObject.get_n_actions(obj)):
|
||||
name = AXObject.get_action_name(obj, i)
|
||||
description = AXObject.get_action_description(obj, i)
|
||||
tokens = [f"ACTION PRESENTER: Action {i} on", obj,
|
||||
f": '{name}' localized description: '{description}'"]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
actions[name] = description or name
|
||||
|
||||
if not actions.items():
|
||||
name = AXObject.get_name(obj) or script.speechGenerator.getLocalizedRoleName(obj)
|
||||
script.presentMessage(messages.NO_ACTIONS_FOUND_ON % name)
|
||||
return True
|
||||
|
||||
self._obj = obj
|
||||
self._gui = ActionMenu(actions, self._perform_action)
|
||||
self._gui.show_gui()
|
||||
return True
|
||||
|
||||
|
||||
class ActionMenu(Gtk.Menu):
|
||||
"""A simple Gtk.Menu containing a list of accessible actions."""
|
||||
|
||||
def __init__(self, actions, handler):
|
||||
super().__init__()
|
||||
self.connect("popped-up", self._on_popped_up)
|
||||
self.on_option_selected = handler
|
||||
for name, description in actions.items():
|
||||
menu_item = Gtk.MenuItem(label=description)
|
||||
menu_item.connect("activate", self._on_activate, name)
|
||||
self.append(menu_item)
|
||||
|
||||
def _on_activate(self, widget, option):
|
||||
"""Handler for the 'activate' menuitem signal"""
|
||||
|
||||
self.on_option_selected(option)
|
||||
|
||||
def _on_popped_up(self, *args):
|
||||
"""Handler for the 'popped-up' menu signal"""
|
||||
|
||||
msg = "ACTION PRESENTER: ActionMenu popped up"
|
||||
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||
|
||||
def show_gui(self):
|
||||
"""Shows the menu"""
|
||||
|
||||
self.show_all()
|
||||
display = Gdk.Display.get_default()
|
||||
seat = display.get_default_seat()
|
||||
device = seat.get_pointer()
|
||||
screen, x, y = device.get_position()
|
||||
|
||||
event = Gdk.Event.new(Gdk.EventType.BUTTON_PRESS)
|
||||
event.set_screen(screen)
|
||||
event.set_device(device)
|
||||
event.time = Gtk.get_current_event_time()
|
||||
event.x = x
|
||||
event.y = y
|
||||
|
||||
rect = Gdk.Rectangle()
|
||||
rect.x = x
|
||||
rect.y = y
|
||||
rect.width = 1
|
||||
rect.height = 1
|
||||
|
||||
window = Gdk.get_default_root_window()
|
||||
self.popup_at_rect(window, rect, Gdk.Gravity.NORTH_WEST, Gdk.Gravity.NORTH_WEST, event)
|
||||
|
||||
|
||||
_presenter = ActionPresenter()
|
||||
def getPresenter():
|
||||
return _presenter
|
||||
@@ -1,148 +0,0 @@
|
||||
# Utilities for obtaining objects via the collection interface.
|
||||
#
|
||||
# Copyright 2023 Igalia, S.L.
|
||||
# Author: Joanmarie Diggs <jdiggs@igalia.com>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""
|
||||
Utilities for obtaining objects via the collection interface.
|
||||
These utilities are app-type- and toolkit-agnostic. Utilities that might have
|
||||
different implementations or results depending on the type of app (e.g. terminal,
|
||||
chat, web) or toolkit (e.g. Qt, Gtk) should be in script_utilities.py file(s).
|
||||
|
||||
N.B. There are currently utilities that should never have custom implementations
|
||||
that live in script_utilities.py files. These will be moved over time.
|
||||
"""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2023 Igalia, S.L."
|
||||
__license__ = "LGPL"
|
||||
|
||||
import time
|
||||
|
||||
import gi
|
||||
gi.require_version("Atspi", "2.0")
|
||||
from gi.repository import Atspi
|
||||
|
||||
from . import debug
|
||||
from .ax_object import AXObject
|
||||
|
||||
|
||||
class AXCollection:
|
||||
"""Utilities for obtaining objects via the collection interface."""
|
||||
|
||||
# Too many arguments and too many local variables.
|
||||
# This function wraps Atspi.MatchRule.new which has all the arguments.
|
||||
# pylint: disable=R0913,R0914
|
||||
@staticmethod
|
||||
def create_match_rule(states=[],
|
||||
state_match_type=Atspi.CollectionMatchType.ALL,
|
||||
attributes=[],
|
||||
attribute_match_type=Atspi.CollectionMatchType.ANY,
|
||||
roles=[],
|
||||
role_match_type=Atspi.CollectionMatchType.ANY,
|
||||
interfaces=[],
|
||||
interface_match_type=Atspi.CollectionMatchType.ALL,
|
||||
invert=False):
|
||||
"""Creates a match rule based on the supplied criteria."""
|
||||
|
||||
state_set = Atspi.StateSet()
|
||||
if states:
|
||||
for state in states:
|
||||
state_set.add(state)
|
||||
|
||||
attributes_dict = {}
|
||||
if attributes:
|
||||
for attr in attributes:
|
||||
key, value = attr.split(":", 1)
|
||||
value = value.replace(":", r"\:")
|
||||
if key in attributes_dict:
|
||||
attributes_dict[key] = attributes_dict[key] + ":" + value
|
||||
else:
|
||||
attributes_dict[key] = value
|
||||
|
||||
try:
|
||||
rule = Atspi.MatchRule.new(state_set,
|
||||
state_match_type,
|
||||
attributes_dict,
|
||||
attribute_match_type,
|
||||
roles,
|
||||
role_match_type,
|
||||
interfaces,
|
||||
interface_match_type,
|
||||
invert)
|
||||
except Exception as error:
|
||||
tokens = ["AXCollection: Exception in create_match_rule:", error]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return None
|
||||
|
||||
return rule
|
||||
# pylint: enable=R0913,R0914
|
||||
|
||||
@staticmethod
|
||||
def get_all_matches(obj, rule, order=Atspi.CollectionSortOrder.CANONICAL):
|
||||
"""Returns a list of objects matching the specified rule."""
|
||||
|
||||
if not AXObject.supports_collection(obj):
|
||||
return []
|
||||
|
||||
if rule is None:
|
||||
return []
|
||||
|
||||
start = time.time()
|
||||
try:
|
||||
# 0 means no limit on the number of results
|
||||
# The final argument, traverse, is not supported but is expected.
|
||||
matches = Atspi.Collection.get_matches(obj, rule, order, 0, True)
|
||||
except Exception as error:
|
||||
tokens = ["AXCollection: Exception in get_all_matches:", error]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return []
|
||||
|
||||
msg = f"AXCollection: {len(matches)} match(es) found in {time.time() - start:.4f}s"
|
||||
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||
return matches
|
||||
|
||||
@staticmethod
|
||||
def get_first_match(obj, rule, order=Atspi.CollectionSortOrder.CANONICAL):
|
||||
"""Returns the first object matching the specified rule."""
|
||||
|
||||
if not AXObject.supports_collection(obj):
|
||||
return None
|
||||
|
||||
if rule is None:
|
||||
return None
|
||||
|
||||
start = time.time()
|
||||
try:
|
||||
# 1 means limit the number of results to 1
|
||||
# The final argument, traverse, is not supported but is expected.
|
||||
matches = Atspi.Collection.get_matches(obj, rule, order, 1, True)
|
||||
except Exception as error:
|
||||
tokens = ["AXCollection: Exception in get_first_match:", error]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return None
|
||||
|
||||
match = None
|
||||
if matches:
|
||||
match = matches[0]
|
||||
|
||||
msg = f"AXCollection: found {match} in {time.time() - start:.4f}s"
|
||||
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||
return match
|
||||
@@ -1,581 +0,0 @@
|
||||
# Cthulhu
|
||||
#
|
||||
# Copyright 2005-2008 Sun Microsystems Inc.
|
||||
# Copyright 2018-2023 Igalia, S.L.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""Provides support for synthesizing accessible input events."""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc." \
|
||||
"Copyright (c) 2018-2023 Igalia, S.L."
|
||||
__license__ = "LGPL"
|
||||
|
||||
import time
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Atspi", "2.0")
|
||||
from gi.repository import Atspi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk
|
||||
|
||||
from . import debug
|
||||
from .ax_object import AXObject
|
||||
from .ax_utilities import AXUtilities
|
||||
|
||||
class AXEventSynthesizer:
|
||||
"""Provides support for synthesizing accessible input events."""
|
||||
|
||||
_banner = None
|
||||
|
||||
@staticmethod
|
||||
def _get_mouse_coordinates():
|
||||
"""Returns the current mouse coordinates."""
|
||||
|
||||
root_window = Gtk.Window().get_screen().get_root_window()
|
||||
window, x_coord, y_coord, modifiers = root_window.get_pointer()
|
||||
tokens = ["AXEventSynthesizer: Mouse coordinates:", x_coord, ",", y_coord]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return x_coord, y_coord
|
||||
|
||||
@staticmethod
|
||||
def _generate_mouse_event(x_coord, y_coord, event):
|
||||
"""Synthesize a mouse event at a specific screen coordinate."""
|
||||
|
||||
old_x, old_y = AXEventSynthesizer._get_mouse_coordinates()
|
||||
tokens = ["AXEventSynthesizer: Generating", event, "mouse event at", x_coord, ",", y_coord]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
|
||||
try:
|
||||
success = Atspi.generate_mouse_event(x_coord, y_coord, event)
|
||||
except Exception as error:
|
||||
tokens = ["AXEventSynthesizer: Exception in _generate_mouse_event:", error]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
success = False
|
||||
else:
|
||||
tokens = ["AXEventSynthesizer: Atspi.generate_mouse_event returned", success]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
|
||||
# There seems to be a timeout / lack of reply from this blocking call.
|
||||
# But often the mouse event is successful. Pause briefly before checking.
|
||||
time.sleep(1)
|
||||
|
||||
new_x, new_y = AXEventSynthesizer._get_mouse_coordinates()
|
||||
if old_x == new_x and old_y == new_y and (old_x, old_y) != (x_coord, y_coord):
|
||||
msg = "AXEventSynthesizer: Mouse event possible failure. Pointer didn't move"
|
||||
debug.println(debug.LEVEL_INFO, msg, True)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _intersection(extents1, extents2):
|
||||
"""Returns the bounding box containing the intersection of the two boxes."""
|
||||
|
||||
x_coord1, y_coord1, width1, height1 = extents1
|
||||
x_coord2, y_coord2, width2, height2 = extents2
|
||||
|
||||
x_points1 = range(x_coord1, x_coord1 + width1 + 1)
|
||||
x_points2 = range(x_coord2, x_coord2 + width2 + 1)
|
||||
x_intersection = sorted(set(x_points1).intersection(set(x_points2)))
|
||||
|
||||
y_points1 = range(y_coord1, y_coord1 + height1 + 1)
|
||||
y_points2 = range(y_coord2, y_coord2 + height2 + 1)
|
||||
y_intersection = sorted(set(y_points1).intersection(set(y_points2)))
|
||||
|
||||
if not (x_intersection and y_intersection):
|
||||
return 0, 0, 0, 0
|
||||
|
||||
x_coord = x_intersection[0]
|
||||
y_coord = y_intersection[0]
|
||||
width = x_intersection[-1] - x_coord
|
||||
height = y_intersection[-1] - y_coord
|
||||
return x_coord, y_coord, width, height
|
||||
|
||||
@staticmethod
|
||||
def _extents_at_caret(obj):
|
||||
"""Returns the character extents of obj at the current caret offset."""
|
||||
|
||||
try:
|
||||
text = obj.queryText()
|
||||
extents = text.getCharacterExtents(text.caretOffset, Atspi.CoordType.SCREEN)
|
||||
except Exception:
|
||||
tokens = ["ERROR: Exception getting character extents for", obj]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return 0, 0, 0, 0
|
||||
|
||||
return extents
|
||||
|
||||
@staticmethod
|
||||
def _object_extents(obj):
|
||||
"""Returns the bounding box associated with obj."""
|
||||
|
||||
try:
|
||||
extents = obj.queryComponent().getExtents(Atspi.CoordType.SCREEN)
|
||||
except Exception:
|
||||
tokens = ["ERROR: Exception getting extents for", obj]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return 0, 0, 0, 0
|
||||
|
||||
return extents
|
||||
|
||||
@staticmethod
|
||||
def _mouse_event_on_character(obj, event):
|
||||
"""Performs the specified mouse event on the current character in obj."""
|
||||
|
||||
extents = AXEventSynthesizer._extents_at_caret(obj)
|
||||
if extents == (0, 0, 0, 0):
|
||||
return False
|
||||
|
||||
obj_extents = AXEventSynthesizer._object_extents(obj)
|
||||
intersection = AXEventSynthesizer._intersection(extents, obj_extents)
|
||||
if intersection == (0, 0, 0, 0):
|
||||
tokens = ["AXEventSynthesizer:", obj, "'s caret", extents, "not in obj", obj_extents]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return False
|
||||
|
||||
x_coord = max(extents[0], extents[0] + (extents[2] / 2) - 1)
|
||||
y_coord = extents[1] + extents[3] / 2
|
||||
return AXEventSynthesizer._generate_mouse_event(x_coord, y_coord, event)
|
||||
|
||||
@staticmethod
|
||||
def _mouse_event_on_object(obj, event):
|
||||
"""Performs the specified mouse event on obj."""
|
||||
|
||||
extents = AXEventSynthesizer._object_extents(obj)
|
||||
if extents == (0, 0, 0, 0):
|
||||
return False
|
||||
|
||||
x_coord = extents.x + extents.width/2
|
||||
y_coord = extents.y + extents.height/2
|
||||
return AXEventSynthesizer._generate_mouse_event(x_coord, y_coord, event)
|
||||
|
||||
@staticmethod
|
||||
def route_to_character(obj):
|
||||
"""Routes the pointer to the current character in obj."""
|
||||
|
||||
tokens = ["AXEventSynthesizer: Attempting to route to character in", obj]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return AXEventSynthesizer._mouse_event_on_character(obj, "abs")
|
||||
|
||||
@staticmethod
|
||||
def route_to_object(obj):
|
||||
"""Moves the mouse pointer to the center of obj."""
|
||||
|
||||
tokens = ["AXEventSynthesizer: Attempting to route to", obj]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return AXEventSynthesizer._mouse_event_on_object(obj, "abs")
|
||||
|
||||
@staticmethod
|
||||
def route_to_point(x_coord, y_coord):
|
||||
"""Routes the pointer to the specified coordinates."""
|
||||
|
||||
return AXEventSynthesizer._generate_mouse_event(x_coord, y_coord, "abs")
|
||||
|
||||
@staticmethod
|
||||
def click_character(obj, button=1):
|
||||
"""Single click on the current character in obj using the specified button."""
|
||||
|
||||
return AXEventSynthesizer._mouse_event_on_character(obj, f"b{button}c")
|
||||
|
||||
@staticmethod
|
||||
def click_object(obj, button=1):
|
||||
"""Single click on obj using the specified button."""
|
||||
|
||||
return AXEventSynthesizer._mouse_event_on_object(obj, f"b{button}c")
|
||||
|
||||
@staticmethod
|
||||
def click_point(x_coord, y_coord, button=1):
|
||||
"""Single click on the given point using the specified button."""
|
||||
|
||||
return AXEventSynthesizer._generate_mouse_event(x_coord, y_coord, f"b{button}c")
|
||||
|
||||
@staticmethod
|
||||
def double_click_character(obj, button=1):
|
||||
"""Double click on the current character in obj using the specified button."""
|
||||
|
||||
return AXEventSynthesizer._mouse_event_on_character(obj, f"b{button}d")
|
||||
|
||||
@staticmethod
|
||||
def double_click_object(obj, button=1):
|
||||
"""Double click on obj using the specified button."""
|
||||
|
||||
return AXEventSynthesizer._mouse_event_on_object(obj, f"b{button}d")
|
||||
|
||||
@staticmethod
|
||||
def double_click_point(x_coord, y_coord, button=1):
|
||||
"""Double click on the given point using the specified button."""
|
||||
|
||||
return AXEventSynthesizer._generate_mouse_event(x_coord, y_coord, f"b{button}d")
|
||||
|
||||
@staticmethod
|
||||
def press_at_character(obj, button=1):
|
||||
"""Performs a press on the current character in obj using the specified button."""
|
||||
|
||||
return AXEventSynthesizer._mouse_event_on_character(obj, f"b{button}p")
|
||||
|
||||
@staticmethod
|
||||
def press_at_object(obj, button=1):
|
||||
"""Performs a press on obj using the specified button."""
|
||||
|
||||
return AXEventSynthesizer._mouse_event_on_object(obj, f"b{button}p")
|
||||
|
||||
@staticmethod
|
||||
def press_at_point(x_coord, y_coord, button=1):
|
||||
"""Performs a press on the given point using the specified button."""
|
||||
|
||||
return AXEventSynthesizer._generate_mouse_event(x_coord, y_coord, f"b{button}p")
|
||||
|
||||
@staticmethod
|
||||
def release_at_character(obj, button=1):
|
||||
"""Performs a release on the current character in obj using the specified button."""
|
||||
|
||||
return AXEventSynthesizer._mouse_event_on_character(obj, f"b{button}r")
|
||||
|
||||
@staticmethod
|
||||
def release_at_object(obj, button=1):
|
||||
"""Performs a release on obj using the specified button."""
|
||||
|
||||
return AXEventSynthesizer._mouse_event_on_object(obj, f"b{button}r")
|
||||
|
||||
@staticmethod
|
||||
def release_at_point(x_coord, y_coord, button=1):
|
||||
"""Performs a release on the given point using the specified button."""
|
||||
|
||||
return AXEventSynthesizer._generate_mouse_event(x_coord, y_coord, f"b{button}r")
|
||||
|
||||
@staticmethod
|
||||
def _scroll_substring_to_location(obj, location, start_offset, end_offset):
|
||||
"""Attempts to scroll the given substring to the specified location."""
|
||||
|
||||
try:
|
||||
text = obj.queryText()
|
||||
if not text.characterCount:
|
||||
return False
|
||||
if start_offset is None:
|
||||
start_offset = 0
|
||||
if end_offset is None:
|
||||
end_offset = text.characterCount - 1
|
||||
result = text.scrollSubstringTo(start_offset, end_offset, location)
|
||||
except NotImplementedError:
|
||||
tokens = ["AXEventSynthesizer: Text interface not implemented for", obj]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return False
|
||||
except Exception:
|
||||
msg = (
|
||||
f"AXEventSynthesizer: Exception scrolling {obj} ({start_offset}, {end_offset}) "
|
||||
f"to {location.value_name}."
|
||||
)
|
||||
debug.println(debug.LEVEL_INFO, msg, True)
|
||||
return False
|
||||
|
||||
msg = (
|
||||
f"AXEventSynthesizer: scrolled {obj} substring ({start_offset}, {end_offset}) "
|
||||
f"to {location.value_name}: {result}"
|
||||
)
|
||||
debug.println(debug.LEVEL_INFO, msg, True)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _scroll_object_to_location(obj, location):
|
||||
"""Attempts to scroll obj to the specified location."""
|
||||
|
||||
try:
|
||||
result = obj.queryComponent().scrollTo(location)
|
||||
except NotImplementedError:
|
||||
tokens = ["AXEventSynthesizer: Component interface not implemented for", obj]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return False
|
||||
except Exception:
|
||||
tokens = ["AXEventSynthesizer: Exception scrolling",
|
||||
obj, "to", location.value_name, "."]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return False
|
||||
|
||||
tokens = ["AXEventSynthesizer: scrolled", obj, "to", location.value_name, ":", result]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _scroll_to_location(obj, location, start_offset=None, end_offset=None):
|
||||
"""Attempts to scroll to the specified location."""
|
||||
|
||||
try:
|
||||
component = obj.queryComponent()
|
||||
except Exception:
|
||||
tokens = ["AXEventSynthesizer: Exception querying component of", obj]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return
|
||||
|
||||
before = component.getExtents(Atspi.CoordType.SCREEN)
|
||||
|
||||
if not AXEventSynthesizer._scroll_substring_to_location(
|
||||
obj, location, start_offset, end_offset):
|
||||
AXEventSynthesizer._scroll_object_to_location(obj, location)
|
||||
|
||||
after = component.getExtents(Atspi.CoordType.SCREEN)
|
||||
msg = (
|
||||
f"AXEventSynthesizer: Before scroll: {before[0]}, {before[1]}. "
|
||||
f"After scroll: {after[0]}, {after[1]}."
|
||||
)
|
||||
debug.println(debug.LEVEL_INFO, msg, True)
|
||||
|
||||
@staticmethod
|
||||
def _scroll_substring_to_point(obj, x_coord, y_coord, start_offset, end_offset):
|
||||
"""Attempts to scroll the given substring to the specified location."""
|
||||
|
||||
try:
|
||||
text = obj.queryText()
|
||||
if not text.characterCount:
|
||||
return False
|
||||
if start_offset is None:
|
||||
start_offset = 0
|
||||
if end_offset is None:
|
||||
end_offset = text.characterCount - 1
|
||||
result = text.scrollSubstringToPoint(
|
||||
start_offset, end_offset, Atspi.CoordType.SCREEN, x_coord, y_coord)
|
||||
except NotImplementedError:
|
||||
tokens = ["AXEventSynthesizer: Text interface not implemented for", obj]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return False
|
||||
except Exception:
|
||||
msg = (
|
||||
f"AXEventSynthesizer: Exception scrolling {obj} ({start_offset}, {end_offset}) "
|
||||
f"to {x_coord}, {y_coord}"
|
||||
)
|
||||
debug.println(debug.LEVEL_INFO, msg, True)
|
||||
return False
|
||||
|
||||
msg = "AXEventSynthesizer: scrolled %s (%i, %i) to %i, %i: %s" % \
|
||||
(obj, start_offset, end_offset, x_coord, y_coord, result)
|
||||
debug.println(debug.LEVEL_INFO, msg, True)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _scroll_object_to_point(obj, x_coord, y_coord):
|
||||
"""Attempts to scroll obj to the specified point."""
|
||||
|
||||
try:
|
||||
result = obj.queryComponent().scrollToPoint(Atspi.CoordType.SCREEN, x_coord, y_coord)
|
||||
except NotImplementedError:
|
||||
tokens = ["AXEventSynthesizer: Component interface not implemented for", obj]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return False
|
||||
except Exception:
|
||||
tokens = ["AXEventSynthesizer: Exception scrolling", obj, "to", x_coord, ",", y_coord]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return False
|
||||
|
||||
tokens = ["AXEventSynthesizer: scrolled", obj, "to", x_coord, ",", y_coord, ":", result]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _scroll_to_point(obj, x_coord, y_coord, start_offset=None, end_offset=None):
|
||||
"""Attempts to scroll obj to the specified point."""
|
||||
|
||||
try:
|
||||
component = obj.queryComponent()
|
||||
except Exception:
|
||||
tokens = ["AXEventSynthesizer: Exception querying component of", obj]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return
|
||||
|
||||
before = component.getExtents(Atspi.CoordType.SCREEN)
|
||||
|
||||
if not AXEventSynthesizer._scroll_substring_to_point(
|
||||
obj, x_coord, y_coord, start_offset, end_offset):
|
||||
AXEventSynthesizer._scroll_object_to_point(obj, x_coord, y_coord)
|
||||
|
||||
after = component.getExtents(Atspi.CoordType.SCREEN)
|
||||
msg = (
|
||||
f"AXEventSynthesizer: Before scroll: {before[0]}, {before[1]}. "
|
||||
f"After scroll: {after[0]}, {after[1]}."
|
||||
)
|
||||
debug.println(debug.LEVEL_INFO, msg, True)
|
||||
|
||||
@staticmethod
|
||||
def scroll_into_view(obj, start_offset=None, end_offset=None):
|
||||
"""Attempts to scroll obj into view."""
|
||||
|
||||
AXEventSynthesizer._scroll_to_location(
|
||||
obj, Atspi.ScrollType.ANYWHERE, start_offset, end_offset)
|
||||
|
||||
@staticmethod
|
||||
def _containing_document(obj):
|
||||
"""Returns the document containing obj"""
|
||||
|
||||
document = AXObject.find_ancestor(obj, AXUtilities.is_document)
|
||||
while document:
|
||||
ancestor = AXObject.find_ancestor(document, AXUtilities.is_document)
|
||||
if ancestor is None or ancestor == document:
|
||||
break
|
||||
document = ancestor
|
||||
|
||||
return document
|
||||
|
||||
@staticmethod
|
||||
def _get_accessible_at_point(root, x_coord, y_coord):
|
||||
""""Returns the accessible in root at the specified point."""
|
||||
|
||||
try:
|
||||
result = root.queryComponent().getAccessibleAtPoint(
|
||||
x_coord, y_coord, Atspi.CoordType.SCREEN)
|
||||
except NotImplementedError:
|
||||
tokens = ["AXEventSynthesizer: Component interface not implemented for", root]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return None
|
||||
except Exception:
|
||||
msg = (
|
||||
f"AXEventSynthesizer: Exception getting accessible at "
|
||||
f"{x_coord}, {y_coord} for {root}"
|
||||
)
|
||||
debug.println(debug.LEVEL_INFO, msg, True)
|
||||
return None
|
||||
|
||||
tokens = ["AXEventSynthesizer: Accessible at",
|
||||
x_coord, ",", y_coord, "in", root, ":", result]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _get_obscuring_banner(obj):
|
||||
""""Returns the banner obscuring obj from view."""
|
||||
|
||||
document = AXEventSynthesizer._containing_document(obj)
|
||||
if not document:
|
||||
tokens = ["AXEventSynthesizer: No obscuring banner found for", obj, ". No document."]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return None
|
||||
|
||||
if not AXObject.supports_component(document):
|
||||
tokens = ["AXEventSynthesizer: No obscuring banner found for", obj, ". No doc iface."]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return None
|
||||
|
||||
obj_x, obj_y, obj_width, obj_height = AXEventSynthesizer._object_extents(obj)
|
||||
doc_x, doc_y, doc_width, doc_height = AXEventSynthesizer._object_extents(document)
|
||||
|
||||
left = AXEventSynthesizer._get_accessible_at_point(document, doc_x, obj_y)
|
||||
right = AXEventSynthesizer._get_accessible_at_point(document, doc_x + doc_width, obj_y)
|
||||
if not (left and right and left == right != document):
|
||||
tokens = ["AXEventSynthesizer: No obscuring banner found for", obj]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return None
|
||||
|
||||
tokens = ["AXEventSynthesizer:", obj, "believed to be obscured by banner", left]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return left
|
||||
|
||||
@staticmethod
|
||||
def _scroll_below_banner(obj, banner, start_offset, end_offset, margin=25):
|
||||
"""Attempts to scroll obj below banner."""
|
||||
|
||||
obj_x, obj_y, obj_width, obj_height = AXEventSynthesizer._object_extents(obj)
|
||||
banner_x, banner_y, banner_width, banner_height = AXEventSynthesizer._object_extents(banner)
|
||||
msg = (
|
||||
f"AXEventSynthesizer: Extents of banner: "
|
||||
f"({banner_x}, {banner_y}, {banner_width}, {banner_height})"
|
||||
)
|
||||
debug.println(debug.LEVEL_INFO, msg, True)
|
||||
AXEventSynthesizer._scroll_to_point(
|
||||
obj, obj_x, banner_y + banner_height + margin, start_offset, end_offset)
|
||||
|
||||
@staticmethod
|
||||
def scroll_to_top_edge(obj, start_offset=None, end_offset=None):
|
||||
"""Attempts to scroll obj to the top edge."""
|
||||
|
||||
if AXEventSynthesizer._banner and not AXObject.is_dead(AXEventSynthesizer._banner):
|
||||
msg = (
|
||||
f"AXEventSynthesizer: Suspected existing banner found: "
|
||||
f"{AXEventSynthesizer._banner}"
|
||||
)
|
||||
debug.println(debug.LEVEL_INFO, msg, True)
|
||||
AXEventSynthesizer._scroll_below_banner(
|
||||
obj, AXEventSynthesizer._banner, start_offset, end_offset)
|
||||
return
|
||||
|
||||
AXEventSynthesizer._scroll_to_location(
|
||||
obj, Atspi.ScrollType.TOP_EDGE, start_offset, end_offset)
|
||||
|
||||
AXEventSynthesizer._banner = AXEventSynthesizer._get_obscuring_banner(obj)
|
||||
if AXEventSynthesizer._banner:
|
||||
msg = f"AXEventSynthesizer: Re-scrolling {obj} due to banner"
|
||||
AXEventSynthesizer._scroll_below_banner(
|
||||
obj, AXEventSynthesizer._banner, start_offset, end_offset)
|
||||
debug.println(debug.LEVEL_INFO, msg, True)
|
||||
|
||||
@staticmethod
|
||||
def scroll_to_top_left(obj, start_offset=None, end_offset=None):
|
||||
"""Attempts to scroll obj to the top left."""
|
||||
|
||||
AXEventSynthesizer._scroll_to_location(
|
||||
obj, Atspi.ScrollType.TOP_LEFT, start_offset, end_offset)
|
||||
|
||||
@staticmethod
|
||||
def scroll_to_left_edge(obj, start_offset=None, end_offset=None):
|
||||
"""Attempts to scroll obj to the left edge."""
|
||||
|
||||
AXEventSynthesizer._scroll_to_location(
|
||||
obj, Atspi.ScrollType.LEFT_EDGE, start_offset, end_offset)
|
||||
|
||||
@staticmethod
|
||||
def scroll_to_bottom_edge(obj, start_offset=None, end_offset=None):
|
||||
"""Attempts to scroll obj to the bottom edge."""
|
||||
|
||||
AXEventSynthesizer._scroll_to_location(
|
||||
obj, Atspi.ScrollType.BOTTOM_EDGE, start_offset, end_offset)
|
||||
|
||||
@staticmethod
|
||||
def scroll_to_bottom_right(obj, start_offset=None, end_offset=None):
|
||||
"""Attempts to scroll obj to the bottom right."""
|
||||
|
||||
AXEventSynthesizer._scroll_to_location(
|
||||
obj, Atspi.ScrollType.BOTTOM_RIGHT, start_offset, end_offset)
|
||||
|
||||
@staticmethod
|
||||
def scroll_to_right_edge(obj, start_offset=None, end_offset=None):
|
||||
"""Attempts to scroll obj to the right edge."""
|
||||
|
||||
AXEventSynthesizer._scroll_to_location(
|
||||
obj, Atspi.ScrollType.RIGHT_EDGE, start_offset, end_offset)
|
||||
|
||||
@staticmethod
|
||||
def try_all_clickable_actions(obj):
|
||||
"""Attempts to perform a click-like action if one is available."""
|
||||
|
||||
actions = ["click", "press", "jump", "open"]
|
||||
for action in actions:
|
||||
if AXObject.do_named_action(obj, action):
|
||||
tokens = ["AXEventSynthesizer: '", action, "' on", obj, "performed successfully"]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return True
|
||||
|
||||
if debug.LEVEL_INFO < debug.debugLevel:
|
||||
return False
|
||||
|
||||
tokens = ["AXEventSynthesizer: Actions on", obj, ":", AXObject.actions_as_string(obj)]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return False
|
||||
|
||||
_synthesizer = AXEventSynthesizer()
|
||||
def getSynthesizer():
|
||||
return _synthesizer
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,124 +0,0 @@
|
||||
# Utilities for obtaining information about containers supporting selection
|
||||
#
|
||||
# Copyright 2023 Igalia, S.L.
|
||||
# Author: Joanmarie Diggs <jdiggs@igalia.com>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""
|
||||
Utilities for obtaining information about containers supporting selection.
|
||||
These utilities are app-type- and toolkit-agnostic. Utilities that might have
|
||||
different implementations or results depending on the type of app (e.g. terminal,
|
||||
chat, web) or toolkit (e.g. Qt, Gtk) should be in script_utilities.py file(s).
|
||||
|
||||
N.B. There are currently utilities that should never have custom implementations
|
||||
that live in script_utilities.py files. These will be moved over time.
|
||||
"""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2023 Igalia, S.L."
|
||||
__license__ = "LGPL"
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Atspi", "2.0")
|
||||
from gi.repository import Atspi
|
||||
|
||||
from . import debug
|
||||
from .ax_object import AXObject
|
||||
|
||||
|
||||
class AXSelection:
|
||||
"""Utilities for obtaining information about containers supporting selection."""
|
||||
|
||||
@staticmethod
|
||||
def get_selected_child_count(obj):
|
||||
"""Returns the selected child count of obj"""
|
||||
|
||||
if not AXObject.supports_selection(obj):
|
||||
return 0
|
||||
|
||||
try:
|
||||
count = Atspi.Selection.get_n_selected_children(obj)
|
||||
except Exception as error:
|
||||
tokens = ["AXSelection: Exception in get_selected_child_count:", error]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return 0
|
||||
|
||||
tokens = ["AXSelection:", obj, "reports", count, "selected children"]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return count
|
||||
|
||||
@staticmethod
|
||||
def get_selected_child(obj, index):
|
||||
"""Returns the nth selected child of obj."""
|
||||
|
||||
n_children = AXSelection.get_selected_child_count(obj)
|
||||
if n_children <= 0:
|
||||
return None
|
||||
|
||||
if index == -1:
|
||||
index = n_children - 1
|
||||
|
||||
if not 0 <= index < n_children:
|
||||
return None
|
||||
|
||||
try:
|
||||
child = Atspi.Selection.get_selected_child(obj, index)
|
||||
except Exception as error:
|
||||
tokens = ["AXSelection: Exception in get_selected_child:", error]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return None
|
||||
|
||||
if child == obj:
|
||||
tokens = ["AXSelection:", obj, "claims to be its own selected child"]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return None
|
||||
|
||||
tokens = ["AXSelection:", child, "is selected child #", index, "of", obj]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return child
|
||||
|
||||
@staticmethod
|
||||
def get_selected_children(obj):
|
||||
"""Returns a list of all the selected children of obj."""
|
||||
|
||||
count = AXSelection.get_selected_child_count(obj)
|
||||
children = set()
|
||||
for i in range(count):
|
||||
try:
|
||||
child = Atspi.Selection.get_selected_child(obj, i)
|
||||
except Exception as error:
|
||||
tokens = ["AXSelection: Exception in get_selected_children:", error]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return []
|
||||
|
||||
if child is not None:
|
||||
children.add(child)
|
||||
|
||||
if obj in children:
|
||||
tokens = ["AXSelection:", obj, "claims to be its own selected child"]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
children.remove(obj)
|
||||
|
||||
result = list(children)
|
||||
if len(result) != count:
|
||||
tokens = ["AXSelection: Selected child count of", obj, f"is {count}"]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
|
||||
return result
|
||||
@@ -1,206 +0,0 @@
|
||||
# Utilities for performing tasks related to accessibility inspection.
|
||||
#
|
||||
# Copyright 2023 Igalia, S.L.
|
||||
# Author: Joanmarie Diggs <jdiggs@igalia.com>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""
|
||||
Utilities for performing tasks related to accessibility inspection.
|
||||
These utilities are app-type- and toolkit-agnostic. Utilities that might have
|
||||
different implementations or results depending on the type of app (e.g. terminal,
|
||||
chat, web) or toolkit (e.g. Qt, Gtk) should be in script_utilities.py file(s).
|
||||
|
||||
N.B. There are currently utilities that should never have custom implementations
|
||||
that live in script_utilities.py files. These will be moved over time.
|
||||
"""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2023 Igalia, S.L."
|
||||
__license__ = "LGPL"
|
||||
|
||||
import inspect
|
||||
|
||||
import gi
|
||||
gi.require_version("Atspi", "2.0")
|
||||
from gi.repository import Atspi
|
||||
|
||||
from . import debug
|
||||
from .ax_object import AXObject
|
||||
from .ax_utilities_collection import AXUtilitiesCollection
|
||||
from .ax_utilities_role import AXUtilitiesRole
|
||||
from .ax_utilities_state import AXUtilitiesState
|
||||
|
||||
|
||||
class AXUtilities:
|
||||
"""Utilities for performing tasks related to accessibility inspection."""
|
||||
|
||||
COMPARE_COLLECTION_PERFORMANCE = False
|
||||
|
||||
@staticmethod
|
||||
def get_desktop():
|
||||
"""Returns the accessible desktop"""
|
||||
|
||||
try:
|
||||
desktop = Atspi.get_desktop(0)
|
||||
except Exception as error:
|
||||
tokens = ["ERROR: Exception getting desktop from Atspi:", error]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return None
|
||||
|
||||
return desktop
|
||||
|
||||
@staticmethod
|
||||
def get_all_applications(must_have_window=False):
|
||||
"""Returns a list of running applications known to Atspi, filtering out
|
||||
those which have no child windows if must_have_window is True."""
|
||||
|
||||
desktop = AXUtilities.get_desktop()
|
||||
if desktop is None:
|
||||
return []
|
||||
|
||||
def pred(obj):
|
||||
if must_have_window:
|
||||
return AXObject.get_child_count(obj) > 0
|
||||
return True
|
||||
|
||||
return list(AXObject.iter_children(desktop, pred))
|
||||
|
||||
@staticmethod
|
||||
def is_application_in_desktop(app):
|
||||
"""Returns true if app is known to Atspi"""
|
||||
|
||||
desktop = AXUtilities.get_desktop()
|
||||
if desktop is None:
|
||||
return False
|
||||
|
||||
for child in AXObject.iter_children(desktop):
|
||||
if child == app:
|
||||
return True
|
||||
|
||||
tokens = ["WARNING:", app, "is not in", desktop]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_application_with_pid(pid):
|
||||
"""Returns the accessible application with the specified pid"""
|
||||
|
||||
desktop = AXUtilities.get_desktop()
|
||||
if desktop is None:
|
||||
return None
|
||||
|
||||
for app in AXObject.iter_children(desktop):
|
||||
if AXObject.get_process_id(app) == pid:
|
||||
return app
|
||||
|
||||
tokens = ["WARNING: app with pid", pid, "is not in", desktop]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_all_static_text_leaf_nodes(obj):
|
||||
"""Returns all the descendants of obj that are static text leaf nodes"""
|
||||
|
||||
roles = [Atspi.Role.STATIC, Atspi.Role.TEXT]
|
||||
def is_not_element(acc):
|
||||
return AXObject.get_attribute(acc, "tag") in (None, "", "br")
|
||||
|
||||
result = None
|
||||
if AXObject.supports_collection(obj):
|
||||
result = AXUtilitiesCollection.find_all_with_role(obj, roles, is_not_element)
|
||||
if not AXUtilities.COMPARE_COLLECTION_PERFORMANCE:
|
||||
return result
|
||||
|
||||
def is_match(acc):
|
||||
return AXObject.get_role(acc) in roles and is_not_element(acc)
|
||||
|
||||
return AXObject.find_all_descendants(obj, is_match)
|
||||
|
||||
@staticmethod
|
||||
def get_all_widgets(obj, must_be_showing_and_visible=True):
|
||||
"""Returns all the descendants of obj with a widget role"""
|
||||
|
||||
roles = AXUtilitiesRole.get_widget_roles()
|
||||
result = None
|
||||
if AXObject.supports_collection(obj):
|
||||
if not must_be_showing_and_visible:
|
||||
result = AXUtilitiesCollection.find_all_with_role(obj, roles)
|
||||
else:
|
||||
states = [Atspi.StateType.SHOWING, Atspi.StateType.VISIBLE]
|
||||
result = AXUtilitiesCollection.find_all_with_role_and_all_states(
|
||||
obj, roles, states)
|
||||
|
||||
if not AXUtilities.COMPARE_COLLECTION_PERFORMANCE:
|
||||
return result
|
||||
|
||||
def is_match(acc):
|
||||
if AXObject.get_role(acc) not in roles:
|
||||
return False
|
||||
if must_be_showing_and_visible:
|
||||
return AXUtilitiesState.is_showing(acc) and AXUtilitiesState.is_visible(acc)
|
||||
return True
|
||||
|
||||
return AXObject.find_all_descendants(obj, is_match)
|
||||
|
||||
@staticmethod
|
||||
def get_default_button(obj):
|
||||
"""Returns the default button descendant of obj"""
|
||||
|
||||
result = None
|
||||
if AXObject.supports_collection(obj):
|
||||
result = AXUtilitiesCollection.find_default_button(obj)
|
||||
if not AXUtilities.COMPARE_COLLECTION_PERFORMANCE:
|
||||
return result
|
||||
|
||||
return AXObject.find_descendant(obj, AXUtilitiesRole.is_default_button)
|
||||
|
||||
@staticmethod
|
||||
def get_focused_object(obj):
|
||||
"""Returns the focused descendant of obj"""
|
||||
|
||||
result = None
|
||||
if AXObject.supports_collection(obj):
|
||||
result = AXUtilitiesCollection.find_focused_object(obj)
|
||||
if not AXUtilities.COMPARE_COLLECTION_PERFORMANCE:
|
||||
return result
|
||||
|
||||
return AXObject.find_descendant(obj, AXUtilitiesState.is_focused)
|
||||
|
||||
@staticmethod
|
||||
def get_status_bar(obj):
|
||||
"""Returns the status bar descendant of obj"""
|
||||
|
||||
result = None
|
||||
if AXObject.supports_collection(obj):
|
||||
result = AXUtilitiesCollection.find_status_bar(obj)
|
||||
if not AXUtilities.COMPARE_COLLECTION_PERFORMANCE:
|
||||
return result
|
||||
|
||||
return AXObject.find_descendant(obj, AXUtilitiesRole.is_status_bar)
|
||||
|
||||
|
||||
for name, method in inspect.getmembers(AXUtilitiesRole, predicate=inspect.isfunction):
|
||||
setattr(AXUtilities, name, method)
|
||||
|
||||
for name, method in inspect.getmembers(AXUtilitiesState, predicate=inspect.isfunction):
|
||||
setattr(AXUtilities, name, method)
|
||||
|
||||
for name, method in inspect.getmembers(AXUtilitiesCollection, predicate=inspect.isfunction):
|
||||
if name.startswith("find"):
|
||||
setattr(AXUtilities, name, method)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,361 +0,0 @@
|
||||
# Utilities for obtaining state-related information.
|
||||
#
|
||||
# Copyright 2023 Igalia, S.L.
|
||||
# Author: Joanmarie Diggs <jdiggs@igalia.com>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""
|
||||
Utilities for obtaining state-related information.
|
||||
These utilities are app-type- and toolkit-agnostic. Utilities that might have
|
||||
different implementations or results depending on the type of app (e.g. terminal,
|
||||
chat, web) or toolkit (e.g. Qt, Gtk) should be in script_utilities.py file(s).
|
||||
|
||||
N.B. There are currently utilities that should never have custom implementations
|
||||
that live in script_utilities.py files. These will be moved over time.
|
||||
"""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2023 Igalia, S.L."
|
||||
__license__ = "LGPL"
|
||||
|
||||
import gi
|
||||
gi.require_version("Atspi", "2.0")
|
||||
from gi.repository import Atspi
|
||||
|
||||
from . import debug
|
||||
from .ax_object import AXObject
|
||||
|
||||
|
||||
class AXUtilitiesState:
|
||||
"""Utilities for obtaining state-related information."""
|
||||
|
||||
@staticmethod
|
||||
def has_no_state(obj):
|
||||
"""Returns true if obj has an empty state set"""
|
||||
|
||||
return AXObject.get_state_set(obj).is_empty()
|
||||
|
||||
@staticmethod
|
||||
def has_popup(obj):
|
||||
"""Returns true if obj has the has-popup state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.HAS_POPUP)
|
||||
|
||||
@staticmethod
|
||||
def has_tooltip(obj):
|
||||
"""Returns true if obj has the has-tooltip state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.HAS_TOOLTIP)
|
||||
|
||||
@staticmethod
|
||||
def is_active(obj):
|
||||
"""Returns true if obj has the active state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.ACTIVE)
|
||||
|
||||
@staticmethod
|
||||
def is_animated(obj):
|
||||
"""Returns true if obj has the animated state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.ANIMATED)
|
||||
|
||||
@staticmethod
|
||||
def is_armed(obj):
|
||||
"""Returns true if obj has the armed state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.ARMED)
|
||||
|
||||
@staticmethod
|
||||
def is_busy(obj):
|
||||
"""Returns true if obj has the busy state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.BUSY)
|
||||
|
||||
@staticmethod
|
||||
def is_checkable(obj):
|
||||
"""Returns true if obj has the checkable state"""
|
||||
|
||||
if AXObject.has_state(obj, Atspi.StateType.CHECKABLE):
|
||||
return True
|
||||
|
||||
if AXObject.has_state(obj, Atspi.StateType.CHECKED):
|
||||
tokens = ["AXUtilitiesState:", obj, "is checked but lacks state checkable"]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def is_checked(obj):
|
||||
"""Returns true if obj has the checked state"""
|
||||
|
||||
if not AXObject.has_state(obj, Atspi.StateType.CHECKED):
|
||||
return False
|
||||
|
||||
if not AXObject.has_state(obj, Atspi.StateType.CHECKABLE):
|
||||
tokens = ["AXUtilitiesState:", obj, "is checked but lacks state checkable"]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def is_collapsed(obj):
|
||||
"""Returns true if obj has the collapsed state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.COLLAPSED)
|
||||
|
||||
@staticmethod
|
||||
def is_default(obj):
|
||||
"""Returns true if obj has the is-default state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.IS_DEFAULT)
|
||||
|
||||
@staticmethod
|
||||
def is_defunct(obj):
|
||||
"""Returns true if obj has the defunct state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.DEFUNCT)
|
||||
|
||||
@staticmethod
|
||||
def is_editable(obj):
|
||||
"""Returns true if obj has the editable state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.EDITABLE)
|
||||
|
||||
@staticmethod
|
||||
def is_enabled(obj):
|
||||
"""Returns true if obj has the enabled state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.ENABLED)
|
||||
|
||||
@staticmethod
|
||||
def is_expandable(obj):
|
||||
"""Returns true if obj has the expandable state"""
|
||||
|
||||
if AXObject.has_state(obj, Atspi.StateType.EXPANDABLE):
|
||||
return True
|
||||
|
||||
if AXObject.has_state(obj, Atspi.StateType.EXPANDED):
|
||||
tokens = ["AXUtilitiesState:", obj, "is expanded but lacks state expandable"]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def is_expanded(obj):
|
||||
"""Returns true if obj has the expanded state"""
|
||||
|
||||
if not AXObject.has_state(obj, Atspi.StateType.EXPANDED):
|
||||
return False
|
||||
|
||||
if not AXObject.has_state(obj, Atspi.StateType.EXPANDABLE):
|
||||
tokens = ["AXUtilitiesState:", obj, "is expanded but lacks state expandable"]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def is_focusable(obj):
|
||||
"""Returns true if obj has the focusable state"""
|
||||
|
||||
if AXObject.has_state(obj, Atspi.StateType.FOCUSABLE):
|
||||
return True
|
||||
|
||||
if AXObject.has_state(obj, Atspi.StateType.FOCUSED):
|
||||
tokens = ["AXUtilitiesState:", obj, "is focused but lacks state focusable"]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def is_focused(obj):
|
||||
"""Returns true if obj has the focused state"""
|
||||
|
||||
if not AXObject.has_state(obj, Atspi.StateType.FOCUSED):
|
||||
return False
|
||||
|
||||
if not AXObject.has_state(obj, Atspi.StateType.FOCUSABLE):
|
||||
tokens = ["AXUtilitiesState:", obj, "is focused but lacks state focusable"]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def is_horizontal(obj):
|
||||
"""Returns true if obj has the horizontal state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.HORIZONTAL)
|
||||
|
||||
@staticmethod
|
||||
def is_iconified(obj):
|
||||
"""Returns true if obj has the iconified state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.ICONIFIED)
|
||||
|
||||
@staticmethod
|
||||
def is_indeterminate(obj):
|
||||
"""Returns true if obj has the indeterminate state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.INDETERMINATE)
|
||||
|
||||
@staticmethod
|
||||
def is_invalid_state(obj):
|
||||
"""Returns true if obj has the invalid_state state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.INVALID)
|
||||
|
||||
@staticmethod
|
||||
def is_invalid_entry(obj):
|
||||
"""Returns true if obj has the invalid_entry state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.INVALID_ENTRY)
|
||||
|
||||
@staticmethod
|
||||
def is_modal(obj):
|
||||
"""Returns true if obj has the modal state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.MODAL)
|
||||
|
||||
@staticmethod
|
||||
def is_multi_line(obj):
|
||||
"""Returns true if obj has the multi_line state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.MULTI_LINE)
|
||||
|
||||
@staticmethod
|
||||
def is_multiselectable(obj):
|
||||
"""Returns true if obj has the multiselectable state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.MULTISELECTABLE)
|
||||
|
||||
@staticmethod
|
||||
def is_opaque(obj):
|
||||
"""Returns true if obj has the opaque state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.OPAQUE)
|
||||
|
||||
@staticmethod
|
||||
def is_pressed(obj):
|
||||
"""Returns true if obj has the pressed state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.PRESSED)
|
||||
|
||||
@staticmethod
|
||||
def is_read_only(obj):
|
||||
"""Returns true if obj has the read-only state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.READ_ONLY)
|
||||
|
||||
@staticmethod
|
||||
def is_required(obj):
|
||||
"""Returns true if obj has the required state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.REQUIRED)
|
||||
|
||||
@staticmethod
|
||||
def is_resizable(obj):
|
||||
"""Returns true if obj has the resizable state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.RESIZABLE)
|
||||
|
||||
@staticmethod
|
||||
def is_selectable(obj):
|
||||
"""Returns true if obj has the selectable state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.SELECTABLE)
|
||||
|
||||
@staticmethod
|
||||
def is_selectable_text(obj):
|
||||
"""Returns true if obj has the selectable-text state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.SELECTABLE_TEXT)
|
||||
|
||||
@staticmethod
|
||||
def is_selected(obj):
|
||||
"""Returns true if obj has the selected state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.SELECTED)
|
||||
|
||||
@staticmethod
|
||||
def is_sensitive(obj):
|
||||
"""Returns true if obj has the sensitive state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.SENSITIVE)
|
||||
|
||||
@staticmethod
|
||||
def is_showing(obj):
|
||||
"""Returns true if obj has the showing state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.SHOWING)
|
||||
|
||||
@staticmethod
|
||||
def is_single_line(obj):
|
||||
"""Returns true if obj has the single-line state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.SINGLE_LINE)
|
||||
|
||||
@staticmethod
|
||||
def is_stale(obj):
|
||||
"""Returns true if obj has the stale state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.STALE)
|
||||
|
||||
@staticmethod
|
||||
def is_transient(obj):
|
||||
"""Returns true if obj has the transient state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.TRANSIENT)
|
||||
|
||||
@staticmethod
|
||||
def is_truncated(obj):
|
||||
"""Returns true if obj has the truncated state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.TRUNCATED)
|
||||
|
||||
@staticmethod
|
||||
def is_vertical(obj):
|
||||
"""Returns true if obj has the vertical state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.VERTICAL)
|
||||
|
||||
@staticmethod
|
||||
def is_visible(obj):
|
||||
"""Returns true if obj has the visible state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.VISIBLE)
|
||||
|
||||
@staticmethod
|
||||
def is_visited(obj):
|
||||
"""Returns true if obj has the visited state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.VISITED)
|
||||
|
||||
@staticmethod
|
||||
def manages_descendants(obj):
|
||||
"""Returns true if obj has the manages-descendants state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.MANAGES_DESCENDANTS)
|
||||
|
||||
@staticmethod
|
||||
def supports_autocompletion(obj):
|
||||
"""Returns true if obj has the supports-autocompletion state"""
|
||||
|
||||
return AXObject.has_state(obj, Atspi.StateType.SUPPORTS_AUTOCOMPLETION)
|
||||
@@ -1,6 +0,0 @@
|
||||
cthulhu_python_PYTHON = \
|
||||
__init__.py \
|
||||
json_backend.py
|
||||
|
||||
cthulhu_pythondir=$(pkgpythondir)/backends
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
# Cthulhu
|
||||
#
|
||||
# Copyright 2010-2011 Consorcio Fernando de los Rios.
|
||||
# Author: Juanje Ojeda Croissier <jojeda@emergya.es>
|
||||
# Author: Javier Hernandez Antunez <jhernandez@emergya.es>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""JSON backend for Cthulhu settings"""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2010-2011 Consorcio Fernando de los Rios."
|
||||
__license__ = "LGPL"
|
||||
|
||||
from json import load, dump
|
||||
import os
|
||||
from cthulhu import settings, acss
|
||||
|
||||
class Backend:
|
||||
|
||||
def __init__(self, prefsDir):
|
||||
""" Initialize the JSON Backend.
|
||||
"""
|
||||
self.general = {}
|
||||
self.pronunciations = {}
|
||||
self.keybindings = {}
|
||||
self.profiles = {}
|
||||
self.settingsFile = os.path.join(prefsDir, "user-settings.conf")
|
||||
self.appPrefsDir = os.path.join(prefsDir, "app-settings")
|
||||
|
||||
self._defaultProfiles = {'default': { 'profile': settings.profile,
|
||||
'pronunciations': {},
|
||||
'keybindings': {}
|
||||
}
|
||||
}
|
||||
|
||||
def saveDefaultSettings(self, general, pronunciations, keybindings):
|
||||
""" Save default settings for all the properties from
|
||||
cthulhu.settings. """
|
||||
prefs = {'general': general,
|
||||
'profiles': self._defaultProfiles,
|
||||
'pronunciations': pronunciations,
|
||||
'keybindings': keybindings}
|
||||
|
||||
self.general = general
|
||||
self.profiles = self._defaultProfiles
|
||||
self.pronunciations = pronunciations
|
||||
self.keybindings = keybindings
|
||||
|
||||
settingsFile = open(self.settingsFile, 'w')
|
||||
dump(prefs, settingsFile, indent=4)
|
||||
settingsFile.close()
|
||||
|
||||
def getAppSettings(self, appName):
|
||||
fileName = os.path.join(self.appPrefsDir, f"{appName}.conf")
|
||||
if os.path.exists(fileName):
|
||||
settingsFile = open(fileName, 'r')
|
||||
prefs = load(settingsFile)
|
||||
settingsFile.close()
|
||||
else:
|
||||
prefs = {}
|
||||
|
||||
return prefs
|
||||
|
||||
def saveAppSettings(self, appName, profile, general, pronunciations, keybindings):
|
||||
prefs = self.getAppSettings(appName)
|
||||
profiles = prefs.get('profiles', {})
|
||||
profiles[profile] = {'general': general,
|
||||
'pronunciations': pronunciations,
|
||||
'keybindings': keybindings}
|
||||
prefs['profiles'] = profiles
|
||||
|
||||
fileName = os.path.join(self.appPrefsDir, f"{appName}.conf")
|
||||
settingsFile = open(fileName, 'w')
|
||||
dump(prefs, settingsFile, indent=4)
|
||||
settingsFile.close()
|
||||
|
||||
def saveProfileSettings(self, profile, general,
|
||||
pronunciations, keybindings):
|
||||
""" Save minimal subset defined in the profile against current
|
||||
defaults. """
|
||||
if profile is None:
|
||||
profile = 'default'
|
||||
|
||||
general['pronunciations'] = pronunciations
|
||||
general['keybindings'] = keybindings
|
||||
|
||||
with open(self.settingsFile, 'r+') as settingsFile:
|
||||
prefs = load(settingsFile)
|
||||
prefs['profiles'][profile] = general
|
||||
settingsFile.seek(0)
|
||||
settingsFile.truncate()
|
||||
dump(prefs, settingsFile, indent=4)
|
||||
|
||||
def _getSettings(self):
|
||||
""" Load from config file all settings """
|
||||
settingsFile = open(self.settingsFile)
|
||||
try:
|
||||
prefs = load(settingsFile)
|
||||
except ValueError:
|
||||
return
|
||||
self.general = prefs['general'].copy()
|
||||
self.pronunciations = prefs['pronunciations']
|
||||
self.keybindings = prefs['keybindings']
|
||||
self.profiles = prefs['profiles'].copy()
|
||||
|
||||
def getGeneral(self, profile=None):
|
||||
""" Get general settings from default settings and
|
||||
override with profile values. """
|
||||
self._getSettings()
|
||||
generalSettings = self.general.copy()
|
||||
defaultProfile = generalSettings.get('startingProfile',
|
||||
['Default', 'default'])
|
||||
if profile is None:
|
||||
profile = defaultProfile[1]
|
||||
profileSettings = self.profiles[profile].copy()
|
||||
for key, value in profileSettings.items():
|
||||
if key == 'voices':
|
||||
for voiceType, voiceDef in value.items():
|
||||
value[voiceType] = acss.ACSS(voiceDef)
|
||||
if key not in ['startingProfile', 'activeProfile']:
|
||||
generalSettings[key] = value
|
||||
try:
|
||||
generalSettings['activeProfile'] = profileSettings['profile']
|
||||
except KeyError:
|
||||
generalSettings['activeProfile'] = defaultProfile
|
||||
return generalSettings
|
||||
|
||||
def getPronunciations(self, profile='default'):
|
||||
""" Get pronunciation settings from default settings and
|
||||
override with profile values. """
|
||||
self._getSettings()
|
||||
pronunciations = self.pronunciations.copy()
|
||||
profileSettings = self.profiles[profile].copy()
|
||||
if 'pronunciations' in profileSettings:
|
||||
pronunciations = profileSettings['pronunciations']
|
||||
return pronunciations
|
||||
|
||||
def getKeybindings(self, profile='default'):
|
||||
""" Get keybindings settings from default settings and
|
||||
override with profile values. """
|
||||
self._getSettings()
|
||||
keybindings = self.keybindings.copy()
|
||||
profileSettings = self.profiles[profile].copy()
|
||||
if 'keybindings' in profileSettings:
|
||||
keybindings = profileSettings['keybindings']
|
||||
return keybindings
|
||||
|
||||
def isFirstStart(self):
|
||||
""" Check if we're in first start. """
|
||||
|
||||
return not os.path.exists(self.settingsFile)
|
||||
|
||||
def _setProfileKey(self, key, value):
|
||||
self.general[key] = value
|
||||
|
||||
with open(self.settingsFile, 'r+') as settingsFile:
|
||||
prefs = load(settingsFile)
|
||||
prefs['general'][key] = value
|
||||
settingsFile.seek(0)
|
||||
settingsFile.truncate()
|
||||
dump(prefs, settingsFile, indent=4)
|
||||
|
||||
def setFirstStart(self, value=False):
|
||||
"""Set firstStart. This user-configurable setting is primarily
|
||||
intended to serve as an indication as to whether or not initial
|
||||
configuration is needed."""
|
||||
self.general['firstStart'] = value
|
||||
self._setProfileKey('firstStart', value)
|
||||
|
||||
def availableProfiles(self):
|
||||
""" List available profiles. """
|
||||
self._getSettings()
|
||||
profiles = []
|
||||
|
||||
for profileName in self.profiles.keys():
|
||||
profileDict = self.profiles[profileName].copy()
|
||||
profiles.append(profileDict.get('profile'))
|
||||
|
||||
return profiles
|
||||
|
||||
def removeProfile(self, profile):
|
||||
"""Remove an existing profile"""
|
||||
def removeProfileFrom(dict):
|
||||
del dict[profile]
|
||||
# if we removed the last profile, restore the default ones
|
||||
if len(dict) == 0:
|
||||
for profileName in self._defaultProfiles:
|
||||
dict[profileName] = self._defaultProfiles[profileName].copy()
|
||||
|
||||
if profile in self.profiles:
|
||||
removeProfileFrom(self.profiles)
|
||||
|
||||
with open(self.settingsFile, 'r+') as settingsFile:
|
||||
prefs = load(settingsFile)
|
||||
if profile in prefs['profiles']:
|
||||
removeProfileFrom(prefs['profiles'])
|
||||
settingsFile.seek(0)
|
||||
settingsFile.truncate()
|
||||
dump(prefs, settingsFile, indent=4)
|
||||
@@ -1,322 +0,0 @@
|
||||
# Cthulhu
|
||||
#
|
||||
# Copyright 2005-2008 Sun Microsystems Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""Provides the default implementation for bookmarks in Cthulhu."""
|
||||
|
||||
import pickle
|
||||
import os
|
||||
import urllib.parse
|
||||
|
||||
from . import cmdnames
|
||||
from . import keybindings
|
||||
from . import input_event
|
||||
from . import messages
|
||||
from . import settings_manager
|
||||
from .ax_object import AXObject
|
||||
|
||||
_settingsManager = settings_manager.getManager()
|
||||
|
||||
class Bookmarks:
|
||||
"""Represents a default bookmark handler."""
|
||||
def __init__(self, script):
|
||||
self._script = script
|
||||
self._bookmarks = {}
|
||||
self._saveObservers = []
|
||||
self._loadObservers = []
|
||||
self._loadBookmarks()
|
||||
self._currentbookmarkindex = None
|
||||
self._handlers = self._setup_handlers()
|
||||
self._bindings = self._setup_bindings()
|
||||
|
||||
def get_bindings(self):
|
||||
"""Returns the bookmark keybindings."""
|
||||
|
||||
return self._bindings
|
||||
|
||||
def get_handlers(self):
|
||||
"""Returns the bookmark handlers."""
|
||||
|
||||
return self._handlers
|
||||
|
||||
def _setup_handlers(self):
|
||||
"""Sets up and returns the bookmark input event handlers."""
|
||||
|
||||
handlers = {}
|
||||
|
||||
handlers["goToPrevBookmark"] = \
|
||||
input_event.InputEventHandler(
|
||||
self.goToPrevBookmark,
|
||||
cmdnames.BOOKMARK_GO_TO_PREVIOUS)
|
||||
|
||||
handlers["goToNextBookmark"] = \
|
||||
input_event.InputEventHandler(
|
||||
self.goToNextBookmark,
|
||||
cmdnames.BOOKMARK_GO_TO_NEXT)
|
||||
|
||||
handlers["goToBookmark"] = \
|
||||
input_event.InputEventHandler(
|
||||
self.goToBookmark,
|
||||
cmdnames.BOOKMARK_GO_TO)
|
||||
|
||||
handlers["addBookmark"] = \
|
||||
input_event.InputEventHandler(
|
||||
self.addBookmark,
|
||||
cmdnames.BOOKMARK_ADD)
|
||||
|
||||
handlers["saveBookmarks"] = \
|
||||
input_event.InputEventHandler(
|
||||
self.saveBookmarks,
|
||||
cmdnames.BOOKMARK_SAVE)
|
||||
|
||||
return handlers
|
||||
|
||||
def _setup_bindings(self):
|
||||
"""Sets up and returns the date-and-time-presenter key bindings."""
|
||||
|
||||
bindings = keybindings.KeyBindings()
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"b",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.ORCA_MODIFIER_MASK,
|
||||
self._handlers.get("goToNextBookmark")))
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"b",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.ORCA_SHIFT_MODIFIER_MASK,
|
||||
self._handlers.get("goToPrevBookmark")))
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"b",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.ORCA_ALT_MODIFIER_MASK,
|
||||
self._handlers.get("saveBookmarks")))
|
||||
|
||||
for i in range(6):
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
str(i + 1),
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.ORCA_MODIFIER_MASK,
|
||||
self._handlers.get("goToBookmark")))
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
str(i + 1),
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.ORCA_ALT_MODIFIER_MASK,
|
||||
self._handlers.get("addBookmark")))
|
||||
|
||||
return bindings
|
||||
|
||||
def addSaveObserver(self, observer):
|
||||
self._saveObservers.append(observer)
|
||||
|
||||
def addLoadObserver(self, observer):
|
||||
self._loadObservers.append(observer)
|
||||
|
||||
def goToBookmark(self, script, inputEvent, index=None):
|
||||
""" Go to the bookmark indexed by inputEvent.hw_code """
|
||||
# establish the _bookmarks index
|
||||
index = index or inputEvent.hw_code
|
||||
|
||||
try:
|
||||
context = self._script.getFlatReviewContext()
|
||||
context_info = self._bookmarks[index]
|
||||
context.setCurrent(context_info['line'], context_info['zone'], \
|
||||
context_info['word'], context_info['char'])
|
||||
self._bookmarks[index] = context_info
|
||||
except KeyError:
|
||||
self._script.presentMessage(messages.BOOKMARK_NOT_FOUND)
|
||||
return
|
||||
|
||||
self._script.flatReviewPresenter.present_item(script, inputEvent)
|
||||
|
||||
# update the currentbookmark
|
||||
self._currentbookmarkindex = index
|
||||
|
||||
def addBookmark(self, script, inputEvent):
|
||||
""" Add an in-page accessible object bookmark for this key. """
|
||||
context = self._script.getFlatReviewContext()
|
||||
self._bookmarks[inputEvent.hw_code] = self._contextToBookmark(context)
|
||||
self._script.presentMessage(messages.BOOKMARK_ENTERED)
|
||||
|
||||
def saveBookmarks(self, script, inputEvent):
|
||||
""" Save the bookmarks for this script. """
|
||||
try:
|
||||
self.saveBookmarksToDisk(self._bookmarks)
|
||||
self._script.presentMessage(messages.BOOKMARKS_SAVED)
|
||||
except IOError:
|
||||
self._script.presentMessage(messages.BOOKMARKS_SAVED_FAILURE)
|
||||
|
||||
# Notify the observers
|
||||
for o in self._saveObservers:
|
||||
o()
|
||||
|
||||
def goToNextBookmark(self, script, inputEvent):
|
||||
""" Go to the next bookmark location. If no bookmark has yet to be
|
||||
selected, the first bookmark will be used. """
|
||||
|
||||
# get the hardware keys that have registered bookmarks
|
||||
hwkeys = sorted(self._bookmarks.keys())
|
||||
|
||||
# no bookmarks have been entered
|
||||
if len(hwkeys) == 0:
|
||||
self._script.presentMessage(messages.BOOKMARKS_NOT_FOUND)
|
||||
return
|
||||
# only 1 bookmark or we are just starting out
|
||||
elif len(hwkeys) == 1 or self._currentbookmarkindex is None:
|
||||
self.goToBookmark(None, index=hwkeys[0])
|
||||
return
|
||||
|
||||
# find current bookmark hw_code in our sorted list.
|
||||
# Go to next one if possible
|
||||
try:
|
||||
index = hwkeys.index(self._currentbookmarkindex)
|
||||
self.goToBookmark(None, index=hwkeys[index+1])
|
||||
except (ValueError, KeyError, IndexError):
|
||||
self.goToBookmark(None, index=hwkeys[0])
|
||||
|
||||
def goToPrevBookmark(self, script, inputEvent):
|
||||
# get the hardware keys that have registered bookmarks
|
||||
hwkeys = sorted(self._bookmarks.keys())
|
||||
|
||||
# no bookmarks have been entered
|
||||
if len(hwkeys) == 0:
|
||||
self._script.presentMessage(messages.BOOKMARKS_NOT_FOUND)
|
||||
return
|
||||
# only 1 bookmark or we are just starting out
|
||||
elif len(hwkeys) == 1 or self._currentbookmarkindex is None:
|
||||
self.goToBookmark(None, index=hwkeys[0])
|
||||
return
|
||||
|
||||
# find current bookmark hw_code in our sorted list.
|
||||
# Go to previous one if possible
|
||||
try:
|
||||
index = hwkeys.index(self._currentbookmarkindex)
|
||||
self.goToBookmark(None, index=hwkeys[index-1])
|
||||
except (ValueError, KeyError, IndexError):
|
||||
self.goToBookmark(None, index=hwkeys[0])
|
||||
|
||||
def _loadBookmarks(self):
|
||||
""" Load this scripts saved bookmarks."""
|
||||
self._bookmarks = self.readBookmarksFromDisk() or {}
|
||||
|
||||
# notify the observers
|
||||
for o in self._loadObservers:
|
||||
o()
|
||||
|
||||
def readBookmarksFromDisk(self, filename=None):
|
||||
""" Read saved bookmarks from disk. Currently an unpickled object
|
||||
that represents a bookmark """
|
||||
filename = filename or self._script.name.split(' ')[0]
|
||||
cthulhuDir = _settingsManager.getPrefsDir()
|
||||
if not cthulhuDir:
|
||||
return
|
||||
|
||||
cthulhuBookmarksDir = os.path.join(cthulhuDir, "bookmarks")
|
||||
try:
|
||||
inputFile = open( os.path.join( cthulhuBookmarksDir, \
|
||||
f'{filename}.pkl'), "r")
|
||||
bookmarks = pickle.load(inputFile.buffer)
|
||||
inputFile.close()
|
||||
return bookmarks
|
||||
except (IOError, EOFError, OSError):
|
||||
return None
|
||||
|
||||
def saveBookmarksToDisk(self, bookmarksObj, filename=None):
|
||||
""" Write bookmarks to disk. bookmarksObj must be a pickleable
|
||||
object. """
|
||||
filename = filename or self._script.name.split(' ')[0]
|
||||
cthulhuDir = _settingsManager.getPrefsDir()
|
||||
cthulhuBookmarksDir = os.path.join(cthulhuDir, "bookmarks")
|
||||
# create directory if it does not exist. correct place??
|
||||
try:
|
||||
os.stat(cthulhuBookmarksDir)
|
||||
except OSError:
|
||||
os.mkdir(cthulhuBookmarksDir)
|
||||
output = open( os.path.join( cthulhuBookmarksDir, \
|
||||
f'{filename}.pkl'), "w", os.O_CREAT)
|
||||
pickle.dump(bookmarksObj, output.buffer)
|
||||
output.close()
|
||||
|
||||
def _contextToBookmark(self, context):
|
||||
"""Converts a flat_review.Context object into a bookmark."""
|
||||
context_info = {}
|
||||
context_info['zone'] = context.zoneIndex
|
||||
context_info['char'] = context.charIndex
|
||||
context_info['word'] = context.wordIndex
|
||||
context_info['line'] = context.lineIndex
|
||||
return context_info
|
||||
|
||||
def _bookmarkToContext(self, bookmark):
|
||||
"""Converts a bookmark into a flat_review.Context object."""
|
||||
context = self._script.getFlatReviewContext()
|
||||
context.setCurrent(bookmark['line'], bookmark['zone'], \
|
||||
bookmark['word'], bookmark['char'])
|
||||
return context
|
||||
|
||||
def getURIKey(self):
|
||||
"""Returns the URI key for a given page as a URI stripped of
|
||||
parameters?query#fragment as seen in urlparse."""
|
||||
uri = self._script.utilities.documentFrameURI()
|
||||
if uri:
|
||||
parsed_uri = urllib.parse.urlparse(uri)
|
||||
return ''.join(parsed_uri[0:3])
|
||||
else:
|
||||
return None
|
||||
|
||||
def pathToObj(self, path):
|
||||
"""Return the object with the given path (relative to the
|
||||
document frame). """
|
||||
returnobj = self._script.utilities.documentFrame()
|
||||
for childnumber in path:
|
||||
returnobj = AXObject.get_child(returnobj, childnumber)
|
||||
if not returnobj:
|
||||
break
|
||||
|
||||
return returnobj
|
||||
|
||||
def _objToPath(self, start_obj=None):
|
||||
"""Given an object, return it's path from the root accessible. If obj
|
||||
is not provided, the current caret context is used. """
|
||||
if not start_obj:
|
||||
[start_obj, characterOffset] = self._script.utilities.getCaretContext()
|
||||
|
||||
if not start_obj:
|
||||
return []
|
||||
|
||||
if self._script.utilities.isDocument(start_obj):
|
||||
return []
|
||||
|
||||
path = []
|
||||
path.append(AXObject.get_index_in_parent(start_obj))
|
||||
p = AXObject.get_parent(start_obj)
|
||||
while p:
|
||||
if self._script.utilities.isDocument(p):
|
||||
path.reverse()
|
||||
return path
|
||||
path.append(AXObject.get_index_in_parent(p))
|
||||
p = AXObject.get_parent(p)
|
||||
|
||||
return []
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,527 +0,0 @@
|
||||
# Cthulhu
|
||||
#
|
||||
# Copyright 2005-2009 Sun Microsystems Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""Utilities for obtaining braille presentations for objects."""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2005-2009 Sun Microsystems Inc."
|
||||
__license__ = "LGPL"
|
||||
|
||||
import gi
|
||||
gi.require_version("Atspi", "2.0")
|
||||
from gi.repository import Atspi
|
||||
|
||||
from . import braille
|
||||
from . import debug
|
||||
from . import generator
|
||||
from . import messages
|
||||
from . import object_properties
|
||||
from . import cthulhu_state
|
||||
from . import settings
|
||||
from . import settings_manager
|
||||
from .ax_object import AXObject
|
||||
from .ax_utilities import AXUtilities
|
||||
from .braille_rolenames import shortRoleNames
|
||||
|
||||
_settingsManager = settings_manager.getManager()
|
||||
|
||||
class Space:
|
||||
"""A dummy class to indicate we want to insert a space into an
|
||||
utterance, but only if there is text prior to the space."""
|
||||
def __init__(self, delimiter=" "):
|
||||
self.delimiter = delimiter
|
||||
|
||||
SPACE = [Space()]
|
||||
|
||||
class BrailleGenerator(generator.Generator):
|
||||
"""Takes accessible objects and produces a list of braille Regions
|
||||
for those objects. See the generateBraille method, which is the
|
||||
primary entry point. Subclasses can feel free to override/extend
|
||||
the brailleGenerators instance field as they see fit."""
|
||||
|
||||
SKIP_CONTEXT_ROLES = (Atspi.Role.MENU,
|
||||
Atspi.Role.MENU_BAR,
|
||||
Atspi.Role.PAGE_TAB_LIST,
|
||||
Atspi.Role.REDUNDANT_OBJECT,
|
||||
Atspi.Role.UNKNOWN,
|
||||
Atspi.Role.COMBO_BOX)
|
||||
|
||||
def __init__(self, script):
|
||||
generator.Generator.__init__(self, script, "braille")
|
||||
|
||||
def _addGlobals(self, globalsDict):
|
||||
"""Other things to make available from the formatting string.
|
||||
"""
|
||||
generator.Generator._addGlobals(self, globalsDict)
|
||||
globalsDict['space'] = self.space
|
||||
globalsDict['Component'] = braille.Component
|
||||
globalsDict['Region'] = braille.Region
|
||||
globalsDict['Text'] = braille.Text
|
||||
globalsDict['Link'] = braille.Link
|
||||
globalsDict['asString'] = self.asString
|
||||
|
||||
def _isCandidateFocusedRegion(self, obj, region):
|
||||
if not isinstance(region, (braille.Component, braille.Text)):
|
||||
return False
|
||||
|
||||
if not AXUtilities.have_same_role(obj, region.accessible):
|
||||
return False
|
||||
|
||||
return AXObject.get_name(obj) == AXObject.get_name(region.accessible)
|
||||
|
||||
def generateBraille(self, obj, **args):
|
||||
if not _settingsManager.getSetting('enableBraille') \
|
||||
and not _settingsManager.getSetting('enableBrailleMonitor'):
|
||||
debug.printMessage(debug.LEVEL_INFO, "BRAILLE GENERATOR: generation disabled", True)
|
||||
return [[], None]
|
||||
|
||||
if obj == cthulhu_state.locusOfFocus \
|
||||
and not args.get('formatType', None):
|
||||
args['formatType'] = 'focused'
|
||||
result = self.generate(obj, **args)
|
||||
|
||||
# We guess at the focused region. It's going to be a
|
||||
# Component or Text region whose accessible is the same
|
||||
# as the object we're generating braille for. There is
|
||||
# a small hack-like thing here where we include knowledge
|
||||
# that we represent the text area of editable comboboxes
|
||||
# instead of the combobox itself. We also do the same
|
||||
# for table cells because they sometimes have children
|
||||
# that we present.
|
||||
#
|
||||
try:
|
||||
focusedRegion = result[0]
|
||||
except Exception:
|
||||
focusedRegion = None
|
||||
|
||||
for region in result:
|
||||
if isinstance(region, (braille.Component, braille.Text)) \
|
||||
and self._script.utilities.isSameObject(region.accessible, obj, True):
|
||||
focusedRegion = region
|
||||
break
|
||||
elif isinstance(region, braille.Text) \
|
||||
and AXUtilities.is_combo_box(obj) \
|
||||
and AXObject.get_parent(region.accessible) == obj:
|
||||
focusedRegion = region
|
||||
break
|
||||
elif isinstance(region, braille.Component) \
|
||||
and AXUtilities.is_table_cell(obj) \
|
||||
and AXObject.get_parent(region.accessible) == obj:
|
||||
focusedRegion = region
|
||||
break
|
||||
else:
|
||||
|
||||
def pred(x):
|
||||
return self._isCandidateFocusedRegion(obj, x)
|
||||
|
||||
candidates = list(filter(pred, result))
|
||||
tokens = ["BRAILLE GENERATOR: Could not determine focused region for",
|
||||
obj, "Candidates:", candidates]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
if len(candidates) == 1:
|
||||
focusedRegion = candidates[0]
|
||||
|
||||
return [result, focusedRegion]
|
||||
|
||||
#####################################################################
|
||||
# #
|
||||
# Name, role, and label information #
|
||||
# #
|
||||
#####################################################################
|
||||
|
||||
def _generateRoleName(self, obj, **args):
|
||||
"""Returns the role name for the object in an array of strings, with
|
||||
the exception that the Atspi.Role.UNKNOWN role will yield an
|
||||
empty array. Note that a 'role' attribute in args will
|
||||
override the accessible role of the obj.
|
||||
"""
|
||||
|
||||
if args.get('isProgressBarUpdate') \
|
||||
and not _settingsManager.getSetting('brailleProgressBarUpdates'):
|
||||
return []
|
||||
|
||||
result = []
|
||||
role = args.get('role', AXObject.get_role(obj))
|
||||
verbosityLevel = _settingsManager.getSetting('brailleVerbosityLevel')
|
||||
|
||||
doNotPresent = [Atspi.Role.UNKNOWN,
|
||||
Atspi.Role.REDUNDANT_OBJECT,
|
||||
Atspi.Role.FILLER,
|
||||
Atspi.Role.EXTENDED,
|
||||
Atspi.Role.LINK]
|
||||
|
||||
# egg-list-box, e.g. privacy panel in gnome-control-center
|
||||
if AXUtilities.is_list_box(AXObject.get_parent(obj)):
|
||||
doNotPresent.append(AXObject.get_role(obj))
|
||||
|
||||
if verbosityLevel == settings.VERBOSITY_LEVEL_BRIEF:
|
||||
doNotPresent.extend([Atspi.Role.ICON, Atspi.Role.CANVAS])
|
||||
|
||||
if role == Atspi.Role.HEADING:
|
||||
level = self._script.utilities.headingLevel(obj)
|
||||
result.append(object_properties.ROLE_HEADING_LEVEL_BRAILLE % level)
|
||||
|
||||
elif verbosityLevel == settings.VERBOSITY_LEVEL_VERBOSE \
|
||||
and not args.get('readingRow', False) and role not in doNotPresent:
|
||||
result.append(self.getLocalizedRoleName(obj, **args))
|
||||
return result
|
||||
|
||||
def getLocalizedRoleName(self, obj, **args):
|
||||
"""Returns the localized name of the given Accessible object; the name
|
||||
is suitable to be brailled.
|
||||
|
||||
Arguments:
|
||||
- obj: an Accessible object
|
||||
"""
|
||||
|
||||
if _settingsManager.getSetting('brailleRolenameStyle') \
|
||||
== settings.BRAILLE_ROLENAME_STYLE_SHORT:
|
||||
role = args.get('role', AXObject.get_role(obj))
|
||||
rv = shortRoleNames.get(role)
|
||||
if rv:
|
||||
return rv
|
||||
|
||||
return super().getLocalizedRoleName(obj, **args)
|
||||
|
||||
def _generateUnrelatedLabels(self, obj, **args):
|
||||
result = []
|
||||
labels = self._script.utilities.unrelatedLabels(obj)
|
||||
for label in labels:
|
||||
name = self._generateName(label, **args)
|
||||
result.extend(name)
|
||||
|
||||
return result
|
||||
|
||||
#####################################################################
|
||||
# #
|
||||
# Keyboard shortcut information #
|
||||
# #
|
||||
#####################################################################
|
||||
|
||||
def _generateAccelerator(self, obj, **args):
|
||||
"""Returns an array of strings (and possibly voice and audio
|
||||
specifications) that represent the accelerator for the object,
|
||||
or an empty array if no accelerator can be found.
|
||||
"""
|
||||
|
||||
verbosityLevel = _settingsManager.getSetting('brailleVerbosityLevel')
|
||||
if verbosityLevel == settings.VERBOSITY_LEVEL_BRIEF:
|
||||
return []
|
||||
|
||||
result = []
|
||||
[mnemonic, shortcut, accelerator] = \
|
||||
self._script.utilities.mnemonicShortcutAccelerator(obj)
|
||||
if accelerator:
|
||||
result.append("(" + accelerator + ")")
|
||||
return result
|
||||
|
||||
#####################################################################
|
||||
# #
|
||||
# Hierarchy and related dialog information #
|
||||
# #
|
||||
#####################################################################
|
||||
|
||||
def _generateAlertAndDialogCount(self, obj, **args):
|
||||
"""Returns an array of strings that says how many alerts and dialogs
|
||||
are associated with the application for this object. [[[WDW -
|
||||
I wonder if this string should be moved to settings.py.]]]
|
||||
"""
|
||||
result = []
|
||||
try:
|
||||
alertAndDialogCount = \
|
||||
self._script.utilities.unfocusedAlertAndDialogCount(obj)
|
||||
except Exception:
|
||||
alertAndDialogCount = 0
|
||||
if alertAndDialogCount > 0:
|
||||
result.append(messages.dialogCountBraille(alertAndDialogCount))
|
||||
|
||||
return result
|
||||
|
||||
def _generateAncestors(self, obj, **args):
|
||||
"""Returns an array of strings (and possibly voice and audio
|
||||
specifications) that represent the text of the ancestors for
|
||||
the object. This is typically used to present the context for
|
||||
an object (e.g., the names of the window, the panels, etc.,
|
||||
that the object is contained in). If the 'priorObj' attribute
|
||||
of the args dictionary is set, only the differences in
|
||||
ancestry between the 'priorObj' and the current obj will be
|
||||
computed. The 'priorObj' is typically set by Cthulhu to be the
|
||||
previous object with focus.
|
||||
"""
|
||||
result = []
|
||||
if not _settingsManager.getSetting('enableBrailleContext'):
|
||||
return result
|
||||
args['includeContext'] = False
|
||||
|
||||
# Radio button group names are treated separately from the
|
||||
# ancestors. However, they can appear in the ancestry as a
|
||||
# labeled panel. So, we need to exclude the first one of
|
||||
# these things we come across. See also the
|
||||
# generator.py:_generateRadioButtonGroup method that is
|
||||
# used to find the radio button group name.
|
||||
#
|
||||
role = args.get('role', AXObject.get_role(obj))
|
||||
excludeRadioButtonGroup = role == Atspi.Role.RADIO_BUTTON
|
||||
|
||||
parent = AXObject.get_parent_checked(obj)
|
||||
if parent and (AXObject.get_role(parent) in self.SKIP_CONTEXT_ROLES):
|
||||
parent = AXObject.get_parent_checked(parent)
|
||||
while parent:
|
||||
parentResult = []
|
||||
# [[[TODO: WDW - we might want to include more things here
|
||||
# besides just those things that have labels. For example,
|
||||
# page tab lists might be a nice thing to include. Logged
|
||||
# as bugzilla bug 319751.]]]
|
||||
#
|
||||
role = AXObject.get_role(parent)
|
||||
if role != Atspi.Role.FILLER \
|
||||
and role != Atspi.Role.INVALID \
|
||||
and role != Atspi.Role.SECTION \
|
||||
and role != Atspi.Role.SPLIT_PANE \
|
||||
and role != Atspi.Role.DESKTOP_FRAME \
|
||||
and not self._script.utilities.isLayoutOnly(parent):
|
||||
args['role'] = role
|
||||
parentResult = self.generate(parent, **args)
|
||||
# [[[TODO: HACK - we've discovered oddness in hierarchies
|
||||
# such as the gedit Edit->Preferences dialog. In this
|
||||
# dialog, we have labeled groupings of objects. The
|
||||
# grouping is done via a FILLER with two children - one
|
||||
# child is the overall label, and the other is the
|
||||
# container for the grouped objects. When we detect this,
|
||||
# we add the label to the overall context.]]]
|
||||
#
|
||||
if role in [Atspi.Role.FILLER, Atspi.Role.PANEL]:
|
||||
label = self._script.utilities.displayedLabel(parent)
|
||||
if label and len(label) and not label.isspace():
|
||||
if not excludeRadioButtonGroup:
|
||||
args['role'] = AXObject.get_role(parent)
|
||||
parentResult = self.generate(parent, **args)
|
||||
else:
|
||||
excludeRadioButtonGroup = False
|
||||
if result and parentResult:
|
||||
result.append(braille.Region(" "))
|
||||
result.extend(parentResult)
|
||||
if role == Atspi.Role.EMBEDDED:
|
||||
break
|
||||
|
||||
parent = AXObject.get_parent_checked(parent)
|
||||
result.reverse()
|
||||
return result
|
||||
|
||||
def _generateFocusedItem(self, obj, **args):
|
||||
result = []
|
||||
role = args.get('role', AXObject.get_role(obj))
|
||||
if role not in [Atspi.Role.LIST, Atspi.Role.LIST_BOX]:
|
||||
return result
|
||||
|
||||
if AXObject.supports_selection(obj):
|
||||
items = self._script.utilities.selectedChildren(obj)
|
||||
else:
|
||||
items = [AXUtilities.get_focused_object(obj)]
|
||||
if not (items and items[0]):
|
||||
return result
|
||||
|
||||
for item in map(self._generateName, items):
|
||||
result.extend(item)
|
||||
|
||||
return result
|
||||
|
||||
def _generateTermValueCount(self, obj, **args):
|
||||
count = self._script.utilities.getValueCountForTerm(obj)
|
||||
if count < 0:
|
||||
return []
|
||||
|
||||
return [f"({messages.valueCountForTerm(count)})"]
|
||||
|
||||
def _generateStatusBar(self, obj, **args):
|
||||
if not AXUtilities.is_status_bar(obj):
|
||||
return []
|
||||
|
||||
items = self._script.utilities.statusBarItems(obj)
|
||||
if not items or items == [obj]:
|
||||
return []
|
||||
|
||||
result = []
|
||||
for child in items:
|
||||
childResult = self.generate(child, includeContext=False)
|
||||
if childResult:
|
||||
result.extend(childResult)
|
||||
result.append(braille.Region(" "))
|
||||
|
||||
return result
|
||||
|
||||
def _generateListBoxItemWidgets(self, obj, **args):
|
||||
if not AXUtilities.is_list_box(AXObject.get_parent(obj)):
|
||||
return []
|
||||
|
||||
result = []
|
||||
for widget in AXUtilities.get_all_widgets(obj):
|
||||
result.extend(self.generate(widget, includeContext=False))
|
||||
result.append(braille.Region(" "))
|
||||
return result
|
||||
|
||||
def _generateProgressBarIndex(self, obj, **args):
|
||||
if not args.get('isProgressBarUpdate') \
|
||||
or not self._shouldPresentProgressBarUpdate(obj, **args):
|
||||
return []
|
||||
|
||||
acc, updateTime, updateValue = self._getMostRecentProgressBarUpdate()
|
||||
if acc != obj:
|
||||
number, count = self.getProgressBarNumberAndCount(obj)
|
||||
return [f'{number}']
|
||||
|
||||
return []
|
||||
|
||||
def _generateProgressBarValue(self, obj, **args):
|
||||
if args.get('isProgressBarUpdate') \
|
||||
and not self._shouldPresentProgressBarUpdate(obj, **args):
|
||||
return []
|
||||
|
||||
result = self._generatePercentage(obj, **args)
|
||||
if obj == cthulhu_state.locusOfFocus and not result:
|
||||
return ['']
|
||||
|
||||
return result
|
||||
|
||||
def _generatePercentage(self, obj, **args):
|
||||
percent = self._script.utilities.getValueAsPercent(obj)
|
||||
if percent is not None:
|
||||
return [f'{percent}%']
|
||||
|
||||
return []
|
||||
|
||||
def _getProgressBarUpdateInterval(self):
|
||||
interval = _settingsManager.getSetting('progressBarBrailleInterval')
|
||||
if interval is None:
|
||||
return super()._getProgressBarUpdateInterval()
|
||||
|
||||
return int(interval)
|
||||
|
||||
def _shouldPresentProgressBarUpdate(self, obj, **args):
|
||||
if not _settingsManager.getSetting('brailleProgressBarUpdates'):
|
||||
return False
|
||||
|
||||
return super()._shouldPresentProgressBarUpdate(obj, **args)
|
||||
|
||||
#####################################################################
|
||||
# #
|
||||
# Unfortunate hacks. #
|
||||
# #
|
||||
#####################################################################
|
||||
|
||||
def _generateAsPageTabOrScrollPane(self, obj, **args):
|
||||
"""If this scroll pane is labelled by a page tab, then return the page
|
||||
tab information for the braille context instead. Thunderbird
|
||||
folder properties is such a case. See bug #507922 for more
|
||||
details.
|
||||
"""
|
||||
result = []
|
||||
labels = self._script.utilities.labelsForObject(obj)
|
||||
for label in labels:
|
||||
result.extend(self.generate(label, **args))
|
||||
break
|
||||
|
||||
if not result:
|
||||
# NOTE: there is no REAL_ROLE_SCROLL_PANE in formatting.py
|
||||
# because currently fallback to the default formatting.
|
||||
# We will provide the support for someone to override this,
|
||||
# however, so we use REAL_ROLE_SCROLL_PANE here.
|
||||
#
|
||||
oldRole = self._overrideRole('REAL_ROLE_SCROLL_PANE', args)
|
||||
result.extend(self.generate(obj, **args))
|
||||
self._restoreRole(oldRole, args)
|
||||
return result
|
||||
|
||||
def _generateIncludeContext(self, obj, **args):
|
||||
"""Returns True or False to indicate whether context should be
|
||||
included or not.
|
||||
"""
|
||||
|
||||
if args.get('isProgressBarUpdate'):
|
||||
return False
|
||||
|
||||
# For multiline text areas, we only show the context if we
|
||||
# are on the very first line. Otherwise, we show only the
|
||||
# line.
|
||||
#
|
||||
include = _settingsManager.getSetting('enableBrailleContext')
|
||||
if not include:
|
||||
return include
|
||||
try:
|
||||
text = obj.queryText()
|
||||
except NotImplementedError:
|
||||
text = None
|
||||
if text and (self._script.utilities.isTextArea(obj) or AXUtilities.is_label(obj)):
|
||||
try:
|
||||
[lineString, startOffset, endOffset] = text.getTextAtOffset(
|
||||
text.caretOffset, Atspi.TextBoundaryType.LINE_START)
|
||||
except Exception:
|
||||
return include
|
||||
|
||||
include = startOffset == 0
|
||||
if include:
|
||||
relation = AXObject.get_relation(obj, Atspi.RelationType.FLOWS_FROM)
|
||||
if relation:
|
||||
include = not self._script.utilities.isTextArea(relation.get_target(0))
|
||||
return include
|
||||
|
||||
#####################################################################
|
||||
# #
|
||||
# Other things for spacing #
|
||||
# #
|
||||
#####################################################################
|
||||
|
||||
def _generateEol(self, obj, **args):
|
||||
result = []
|
||||
if not _settingsManager.getSetting('disableBrailleEOL'):
|
||||
if not args.get('mode', None):
|
||||
args['mode'] = self._mode
|
||||
args['stringType'] = 'eol'
|
||||
result.append(self._script.formatting.getString(**args))
|
||||
return result
|
||||
|
||||
def space(self, delimiter=" "):
|
||||
if delimiter == " ":
|
||||
return SPACE
|
||||
else:
|
||||
return [Space(delimiter)]
|
||||
|
||||
def asString(self, content, delimiter=" "):
|
||||
combined = ""
|
||||
prior = None
|
||||
if isinstance(content, str):
|
||||
combined = content
|
||||
elif content and isinstance(content, list):
|
||||
# Strip off leading and trailing spaces.
|
||||
#
|
||||
while content and isinstance(content[0], Space):
|
||||
content = content[1:]
|
||||
while content and isinstance(content[-1], Space):
|
||||
content = content[0:-1]
|
||||
for element in content:
|
||||
if isinstance(element, Space) and prior:
|
||||
combined += element.delimiter
|
||||
prior = None
|
||||
else:
|
||||
prior = self.asString(element)
|
||||
combined = self._script.utilities.appendString(
|
||||
combined, prior, delimiter)
|
||||
return combined
|
||||
@@ -1,290 +0,0 @@
|
||||
# Cthulhu
|
||||
#
|
||||
# Copyright 2012 Igalia, S.L.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""Dictionary of abbreviated rolenames for use with braille."""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2012 Igalia, S.L."
|
||||
__license__ = "LGPL"
|
||||
|
||||
import gi
|
||||
gi.require_version("Atspi", "2.0")
|
||||
from gi.repository import Atspi
|
||||
|
||||
from .cthulhu_i18n import _
|
||||
from .cthulhu_i18n import C_
|
||||
|
||||
shortRoleNames = {
|
||||
# Translators: short braille for the rolename of an invalid GUI object.
|
||||
# We strive to keep it under three characters to preserve real estate.
|
||||
Atspi.Role.INVALID: _("???"),
|
||||
|
||||
# Translators: short braille for the rolename of an alert dialog.
|
||||
# NOTE for all the short braille words: they we strive to keep them
|
||||
# around three characters to preserve real estate on the braille
|
||||
# display. The letters are chosen to make them unique across all
|
||||
# other rolenames, and they typically act like an abbreviation.
|
||||
Atspi.Role.ALERT: _("alrt"),
|
||||
|
||||
# Translators: short braille for the rolename of an animation widget.
|
||||
Atspi.Role.ANIMATION: _("anim"),
|
||||
|
||||
# Translators: short braille for the rolename of an arrow widget.
|
||||
Atspi.Role.ARROW: _("arw"),
|
||||
|
||||
# Translators: short braille for the rolename of a calendar widget.
|
||||
Atspi.Role.CALENDAR: _("cal"),
|
||||
|
||||
# Translators: short braille for the rolename of a canvas widget.
|
||||
Atspi.Role.CANVAS: _("cnv"),
|
||||
|
||||
# Translators: short braille for the rolename of a caption (e.g.,
|
||||
# table caption).
|
||||
Atspi.Role.CAPTION: _("cptn"),
|
||||
|
||||
# Translators: short braille for the rolename of a checkbox.
|
||||
Atspi.Role.CHECK_BOX: _("chk"),
|
||||
|
||||
# Translators: short braille for the rolename of a check menu item.
|
||||
Atspi.Role.CHECK_MENU_ITEM: _("chk"),
|
||||
|
||||
# Translators: short braille for the rolename of a color chooser.
|
||||
Atspi.Role.COLOR_CHOOSER: _("clrchsr"),
|
||||
|
||||
# Translators: short braille for the rolename of a column header.
|
||||
Atspi.Role.COLUMN_HEADER: _("colhdr"),
|
||||
|
||||
# Translators: short braille for the rolename of a combo box.
|
||||
Atspi.Role.COMBO_BOX: _("cbo"),
|
||||
|
||||
# Translators: short braille for the rolename of a date editor.
|
||||
Atspi.Role.DATE_EDITOR: _("dat"),
|
||||
|
||||
# Translators: short braille for the rolename of a desktop icon.
|
||||
Atspi.Role.DESKTOP_ICON: _("icn"),
|
||||
|
||||
# Translators: short braille for the rolename of a desktop frame.
|
||||
Atspi.Role.DESKTOP_FRAME: _("frm"),
|
||||
|
||||
# Translators: short braille for the rolename of a dial.
|
||||
# You should attempt to treat it as an abbreviation of
|
||||
# the translated word for "dial". It is OK to use an
|
||||
# unabbreviated word as long as it is relatively short.
|
||||
Atspi.Role.DIAL: C_("shortbraille", "dial"),
|
||||
|
||||
# Translators: short braille for the rolename of a dialog.
|
||||
Atspi.Role.DIALOG: _("dlg"),
|
||||
|
||||
# Translators: short braille for the rolename of a directory pane.
|
||||
Atspi.Role.DIRECTORY_PANE: _("dip"),
|
||||
|
||||
# Translators: short braille for the rolename of an HTML document frame.
|
||||
Atspi.Role.DOCUMENT_FRAME: _("html"),
|
||||
|
||||
# Translators: short braille for the rolename of a drawing area.
|
||||
Atspi.Role.DRAWING_AREA: _("draw"),
|
||||
|
||||
# Translators: short braille for the rolename of a file chooser.
|
||||
Atspi.Role.FILE_CHOOSER: _("fchsr"),
|
||||
|
||||
# Translators: short braille for the rolename of a filler.
|
||||
Atspi.Role.FILLER: _("flr"),
|
||||
|
||||
# Translators: short braille for the rolename of a font chooser.
|
||||
Atspi.Role.FONT_CHOOSER: _("fnt"),
|
||||
|
||||
# Translators: short braille for the rolename of a form.
|
||||
# You should attempt to treat it as an abbreviation of
|
||||
# the translated word for "form". It is OK to use an
|
||||
# unabbreviated word as long as it is relatively short.
|
||||
Atspi.Role.FORM: C_("shortbraille", "form"),
|
||||
|
||||
# Translators: short braille for the rolename of a frame.
|
||||
Atspi.Role.FRAME: _("frm"),
|
||||
|
||||
# Translators: short braille for the rolename of a glass pane.
|
||||
Atspi.Role.GLASS_PANE: _("gpn"),
|
||||
|
||||
# Translators: short braille for the rolename of a heading.
|
||||
Atspi.Role.HEADING: _("hdng"),
|
||||
|
||||
# Translators: short braille for the rolename of an html container.
|
||||
Atspi.Role.HTML_CONTAINER: _("html"),
|
||||
|
||||
# Translators: short braille for the rolename of a icon.
|
||||
Atspi.Role.ICON: _("icn"),
|
||||
|
||||
# Translators: short braille for the rolename of a image.
|
||||
Atspi.Role.IMAGE: _("img"),
|
||||
|
||||
# Translators: short braille for the rolename of an internal frame.
|
||||
Atspi.Role.INTERNAL_FRAME: _("ifrm"),
|
||||
|
||||
# Translators: short braille for the rolename of a label.
|
||||
Atspi.Role.LABEL: _("lbl"),
|
||||
|
||||
# Translators: short braille for the rolename of a layered pane.
|
||||
Atspi.Role.LAYERED_PANE: _("lyrdpn"),
|
||||
|
||||
# Translators: short braille for the rolename of a link.
|
||||
Atspi.Role.LINK: _("lnk"),
|
||||
|
||||
# Translators: short braille for the rolename of a list.
|
||||
Atspi.Role.LIST: _("lst"),
|
||||
|
||||
# Translators: short braille for the rolename of a list item.
|
||||
Atspi.Role.LIST_ITEM: _("lstitm"),
|
||||
|
||||
# Translators: short braille for the rolename of a menu.
|
||||
Atspi.Role.MENU: _("mnu"),
|
||||
|
||||
# Translators: short braille for the rolename of a menu bar.
|
||||
Atspi.Role.MENU_BAR: _("mnubr"),
|
||||
|
||||
# Translators: short braille for the rolename of a menu item.
|
||||
Atspi.Role.MENU_ITEM: _("mnuitm"),
|
||||
|
||||
# Translators: short braille for the rolename of an option pane.
|
||||
Atspi.Role.OPTION_PANE: _("optnpn"),
|
||||
|
||||
# Translators: short braille for the rolename of a page tab.
|
||||
Atspi.Role.PAGE_TAB: _("pgt"),
|
||||
|
||||
# Translators: short braille for the rolename of a page tab list.
|
||||
Atspi.Role.PAGE_TAB_LIST: _("tblst"),
|
||||
|
||||
# Translators: short braille for the rolename of a panel.
|
||||
Atspi.Role.PANEL: _("pnl"),
|
||||
|
||||
# Translators: short braille for the rolename of a password field.
|
||||
Atspi.Role.PASSWORD_TEXT: _("pwd"),
|
||||
|
||||
# Translators: short braille for the rolename of a popup menu.
|
||||
Atspi.Role.POPUP_MENU: _("popmnu"),
|
||||
|
||||
# Translators: short braille for the rolename of a progress bar.
|
||||
Atspi.Role.PROGRESS_BAR: _("pgbar"),
|
||||
|
||||
# Translators: short braille for the rolename of a push button.
|
||||
Atspi.Role.PUSH_BUTTON: _("btn"),
|
||||
|
||||
# Translators: short braille for the rolename of a radio button.
|
||||
Atspi.Role.RADIO_BUTTON: _("radio"),
|
||||
|
||||
# Translators: short braille for the rolename of a radio menu item.
|
||||
Atspi.Role.RADIO_MENU_ITEM: _("rdmnuitm"),
|
||||
|
||||
# Translators: short braille for the rolename of a root pane.
|
||||
Atspi.Role.ROOT_PANE: _("rtpn"),
|
||||
|
||||
# Translators: short braille for the rolename of a row header.
|
||||
Atspi.Role.ROW_HEADER: _("rwhdr"),
|
||||
|
||||
# Translators: short braille for the rolename of a scroll bar.
|
||||
Atspi.Role.SCROLL_BAR: _("scbr"),
|
||||
|
||||
# Translators: short braille for the rolename of a scroll pane.
|
||||
Atspi.Role.SCROLL_PANE: _("scpn"),
|
||||
|
||||
# Translators: short braille for the rolename of a section (e.g., in html).
|
||||
Atspi.Role.SECTION: _("sctn"),
|
||||
|
||||
# Translators: short braille for the rolename of a separator.
|
||||
Atspi.Role.SEPARATOR: _("seprtr"),
|
||||
|
||||
# Translators: short braille for the rolename of a slider.
|
||||
Atspi.Role.SLIDER: _("sldr"),
|
||||
|
||||
# Translators: short braille for the rolename of a split pane.
|
||||
Atspi.Role.SPLIT_PANE: _("spltpn"),
|
||||
|
||||
# Translators: short braille for the rolename of a spin button.
|
||||
Atspi.Role.SPIN_BUTTON: _("spin"),
|
||||
|
||||
# Translators: short braille for the rolename of a statusbar.
|
||||
Atspi.Role.STATUS_BAR: _("statbr"),
|
||||
|
||||
# Translators: short braille for the rolename of a table.
|
||||
Atspi.Role.TABLE: _("tbl"),
|
||||
|
||||
# Translators: short braille for the rolename of a table cell.
|
||||
Atspi.Role.TABLE_CELL: _("cll"),
|
||||
|
||||
# Translators: short braille for the rolename of a table column header.
|
||||
Atspi.Role.TABLE_COLUMN_HEADER: _("colhdr"),
|
||||
|
||||
# Translators: short braille for the rolename of a table row header.
|
||||
Atspi.Role.TABLE_ROW_HEADER: _("rwhdr"),
|
||||
|
||||
# Translators: short braille for the rolename of a tear off menu item.
|
||||
Atspi.Role.TEAROFF_MENU_ITEM: _("tomnuitm"),
|
||||
|
||||
# Translators: short braille for the rolename of a terminal.
|
||||
Atspi.Role.TERMINAL: _("term"),
|
||||
|
||||
# Translators: short braille for the rolename of a text entry field.
|
||||
Atspi.Role.TEXT: _("txt"),
|
||||
|
||||
# Translators: short braille for the rolename of a toggle button.
|
||||
Atspi.Role.TOGGLE_BUTTON: _("tglbtn"),
|
||||
|
||||
# Translators: short braille for the rolename of a toolbar.
|
||||
Atspi.Role.TOOL_BAR: _("tbar"),
|
||||
|
||||
# Translators: short braille for the rolename of a tooltip.
|
||||
Atspi.Role.TOOL_TIP: _("tip"),
|
||||
|
||||
# Translators: short braille for the rolename of a tree.
|
||||
Atspi.Role.TREE: _("tre"),
|
||||
|
||||
# Translators: short braille for the rolename of a tree table.
|
||||
Atspi.Role.TREE_TABLE: _("trtbl"),
|
||||
|
||||
# Translators: short braille for when the rolename of an object is unknown.
|
||||
Atspi.Role.UNKNOWN: _("unk"),
|
||||
|
||||
# Translators: short braille for the rolename of a viewport.
|
||||
Atspi.Role.VIEWPORT: _("vwprt"),
|
||||
|
||||
# Translators: short braille for the rolename of a window.
|
||||
Atspi.Role.WINDOW: _("wnd"),
|
||||
|
||||
# Translators: short braille for the rolename of a header.
|
||||
Atspi.Role.HEADER: _("hdr"),
|
||||
|
||||
# Translators: short braille for the rolename of a footer.
|
||||
Atspi.Role.FOOTER: _("ftr"),
|
||||
|
||||
# Translators: short braille for the rolename of a paragraph.
|
||||
Atspi.Role.PARAGRAPH: _("para"),
|
||||
|
||||
# Translators: short braille for the rolename of a application.
|
||||
Atspi.Role.APPLICATION: _("app"),
|
||||
|
||||
# Translators: short braille for the rolename of a autocomplete.
|
||||
Atspi.Role.AUTOCOMPLETE: _("auto"),
|
||||
|
||||
# Translators: short braille for the rolename of an editbar.
|
||||
Atspi.Role.EDITBAR: _("edtbr"),
|
||||
|
||||
# Translators: short braille for the rolename of an embedded component.
|
||||
Atspi.Role.EMBEDDED: _("emb")
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Cthulhu
|
||||
#
|
||||
# Copyright 2006-2008 Sun Microsystems Inc.
|
||||
# Copyright 2011 The Cthulhu Team.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""Provides a graphical braille display, mainly for development tasks."""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc." \
|
||||
"Copyright (c) 2011 The Cthulhu Team."
|
||||
__license__ = "LGPL"
|
||||
|
||||
import brlapi
|
||||
from gi.repository import Gtk
|
||||
|
||||
from . import cthulhu_state
|
||||
from .input_event import BrailleEvent
|
||||
|
||||
# Attribute/Selection mask strings:
|
||||
#
|
||||
DOT_7 = '\x40' # 01000000
|
||||
DOT_8 = '\x80' # 10000000
|
||||
DOTS_78 = '\xc0' # 11000000
|
||||
|
||||
class BrlDot(Gtk.Alignment):
|
||||
"""A single braille dot."""
|
||||
|
||||
MARKUP_NORMAL = '<tt><small>%s</small></tt>'
|
||||
SYMBOL_LOWERED = '\u25CB' # '○'
|
||||
SYMBOL_RAISED = '\u25CF' # '●'
|
||||
|
||||
def __init__(self, dotNumber, isRaised=False):
|
||||
"""Create a new BrlDot.
|
||||
|
||||
Arguments:
|
||||
- dotNumber: an integer reflecting the location of the dot within
|
||||
an 8-dot braille cell, using traditional braille dot values.
|
||||
"""
|
||||
|
||||
super().__init__()
|
||||
if dotNumber in [1, 2, 3, 7]:
|
||||
self.set(1.0, 0.5, 0.0, 0.0)
|
||||
self.set_padding(3, 0, 0, 3)
|
||||
else:
|
||||
self.set(0.0, 0.5, 0.0, 0.0)
|
||||
self.set_padding(3, 0, 3, 0)
|
||||
|
||||
self.label = Gtk.Label()
|
||||
self.add(self.label)
|
||||
if isRaised:
|
||||
self.raiseDot()
|
||||
else:
|
||||
self.lowerDot()
|
||||
|
||||
def raiseDot(self):
|
||||
self.set(0.5, 0.5, 0, 0)
|
||||
self.label.set_markup(self.MARKUP_NORMAL % self.SYMBOL_RAISED)
|
||||
|
||||
def lowerDot(self):
|
||||
self.set(0.5, 0.5, 0, 0)
|
||||
self.label.set_markup(self.MARKUP_NORMAL % self.SYMBOL_LOWERED)
|
||||
|
||||
class BrlCell(Gtk.Button):
|
||||
"""A single graphical braille cell with cursor routing capability."""
|
||||
|
||||
MARKUP_NORMAL = '<tt><big>%s</big></tt>'
|
||||
MARKUP_CURSOR_CELL = '<b><u>%s</u></b>'
|
||||
|
||||
def __init__(self, position):
|
||||
"""Create a new BrlCell.
|
||||
|
||||
Arguments:
|
||||
- position: The location of the cell with respect to the monitor.
|
||||
"""
|
||||
|
||||
Gtk.Button.__init__(self)
|
||||
self.set_size_request(30, 45)
|
||||
self._position = position
|
||||
self._displayedChar = Gtk.Label()
|
||||
self._dot7 = BrlDot(7)
|
||||
self._dot8 = BrlDot(8)
|
||||
|
||||
grid = Gtk.Grid()
|
||||
grid.attach(self._displayedChar, 0, 0, 2, 3)
|
||||
grid.attach(self._dot7, 0, 3, 1, 1)
|
||||
grid.attach(self._dot8, 1, 3, 1, 1)
|
||||
self.add(grid)
|
||||
|
||||
self.connect("clicked", self._onCellClicked)
|
||||
|
||||
def _onCellClicked(self, widget):
|
||||
"""Callback for the 'clicked' signal on the push button. Synthesizes
|
||||
a fake brlapi command to route the cursor to the current cell, similar
|
||||
to what occurs when a user presses the cursor routing key on his/her
|
||||
hardware braille display."""
|
||||
|
||||
if not cthulhu_state.activeScript:
|
||||
return
|
||||
|
||||
fakeKeyPress = {}
|
||||
fakeKeyPress['command'] = brlapi.KEY_CMD_ROUTE
|
||||
fakeKeyPress['argument'] = self._position
|
||||
event = BrailleEvent(fakeKeyPress)
|
||||
cthulhu_state.activeScript.processRoutingKey(event)
|
||||
|
||||
def clear(self):
|
||||
"""Clears the braille cell."""
|
||||
|
||||
self._displayedChar.set_markup("")
|
||||
self._dot7.lowerDot()
|
||||
self._dot8.lowerDot()
|
||||
|
||||
def display(self, char, mask=None, isCursorCell=False):
|
||||
"""Displays the specified character in the cell.
|
||||
|
||||
Arguments:
|
||||
- char: The character to display in the cell.
|
||||
- isCursorCell: If True, the cursor/caret is at this cell and this
|
||||
should be indicated visually.
|
||||
"""
|
||||
|
||||
if char == '&':
|
||||
char = '&'
|
||||
elif char == '<':
|
||||
char = '<'
|
||||
elif char == '\t':
|
||||
char = '$t'
|
||||
|
||||
markup = self.MARKUP_NORMAL
|
||||
if isCursorCell:
|
||||
markup = markup % self.MARKUP_CURSOR_CELL
|
||||
self._displayedChar.set_markup(markup % char)
|
||||
|
||||
if mask in [DOT_7, DOTS_78]:
|
||||
self._dot7.raiseDot()
|
||||
if mask in [DOT_8, DOTS_78]:
|
||||
self._dot8.raiseDot()
|
||||
|
||||
class BrlMon(Gtk.Window):
|
||||
"""Displays a GUI braille monitor that mirrors what would be displayed
|
||||
by Cthulhu on a connected, configured, and enabled braille display. Cursor
|
||||
routing functionality is emulated by each cell being a push button.
|
||||
Panning and other functionality found on hardware braille displays will
|
||||
be added."""
|
||||
|
||||
def __init__(self, numCells=32):
|
||||
"""Create a new BrlMon.
|
||||
|
||||
Arguments:
|
||||
- numCells: how many braille cells to make
|
||||
"""
|
||||
|
||||
super().__init__()
|
||||
self.set_title("Braille Monitor")
|
||||
|
||||
grid = Gtk.Grid()
|
||||
self.add(grid)
|
||||
|
||||
self.cells = []
|
||||
for i in range(numCells):
|
||||
cell = BrlCell(i)
|
||||
grid.attach(cell, i, 0, 1, 1)
|
||||
self.cells.append(cell)
|
||||
|
||||
self.set_resizable(False)
|
||||
self.set_property("accept-focus", False)
|
||||
self.set_skip_taskbar_hint(True)
|
||||
self.set_skip_pager_hint(True)
|
||||
|
||||
def clear(self):
|
||||
"""Clears the braille monitor display."""
|
||||
|
||||
for cell in self.cells:
|
||||
cell.clear()
|
||||
|
||||
def writeText(self, cursorCell, string, mask=None):
|
||||
"""Display the given text and highlight the given
|
||||
cursor cell. A cursorCell of 0 means no cell has
|
||||
the cursor.
|
||||
|
||||
Arguments:
|
||||
- cursorCell: 1-based index of cell with cursor
|
||||
- string: len must be <= num cells.
|
||||
"""
|
||||
|
||||
self.clear()
|
||||
length = min(len(string), len(self.cells))
|
||||
for i in range(length):
|
||||
isCursorCell = i == cursorCell - 1
|
||||
try:
|
||||
cellMask = mask[i]
|
||||
except (IndexError, TypeError):
|
||||
cellMask = None
|
||||
self.cells[i].display(string[i], cellMask, isCursorCell)
|
||||
@@ -1,160 +0,0 @@
|
||||
# Cthulhu
|
||||
#
|
||||
# Copyright 2004-2009 Sun Microsystems Inc.
|
||||
# Copyright 2010-2013 The Cthulhu Team
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""Braille translation table names. These have been put in their own module
|
||||
so that we can present them in the correct language when users change the
|
||||
synthesizer language on the fly without having to reload a bunch of modules."""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004-2009 Sun Microsystems Inc." \
|
||||
"Copyright (c) 2010-2013 The Cthulhu Team"
|
||||
__license__ = "LGPL"
|
||||
|
||||
from .cthulhu_i18n import _
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
CZ_CZ_G1 = _("Czech Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
ES_ES_G1 = _("Spanish Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
FR_CA_G2 = _("Canada French Grade 2")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
FR_FR_G2 = _("France French Grade 2")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
LV_LV_G1 = _("Latvian Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
NL_NL_G1 = _("Netherlands Dutch Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
NO_NO_G0 = _("Norwegian Grade 0")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
NO_NO_G1 = _("Norwegian Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
NO_NO_G2 = _("Norwegian Grade 2")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
NO_NO_G3 = _("Norwegian Grade 3")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
PL_PL_G1 = _("Polish Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
PT_PT_G1 = _("Portuguese Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
SE_SE_G1 = _("Swedish Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
AR_AR_G1 = _("Arabic Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
CY_CY_G1 = _("Welsh Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
CY_CY_G2 = _("Welsh Grade 2")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
DE_DE_G0 = _("German Grade 0")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
DE_DE_G1 = _("German Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
DE_DE_G2 = _("German Grade 2")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
EN_GB_G2 = _("U.K. English Grade 2")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
EN_GB_G1 = _("U.K. English Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
EN_US_G1 = _("U.S. English Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
EN_US_G2 = _("U.S. English Grade 2")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
FR_CA_G1 = _("Canada French Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
FR_FR_G1 = _("France French Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
GR_GR_G1 = _("Greek Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
HI_IN_G1 = _("Hindi Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
HU_HU_8DOT = _("Hungarian 8 dot computer")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
HU_HU_G1 = _("Hungarian Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
HU_HU_G2 = _("Hungarian Grade 2")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
IT_IT_G1 = _("Italian Grade 1")
|
||||
|
||||
# Translators: This is the name of a braille translation table. To learn more
|
||||
# about braille translation tables, see http://en.wikipedia.org/wiki/Braille.
|
||||
NL_BE_G1 = _("Belgium Dutch Grade 1")
|
||||
@@ -1,457 +0,0 @@
|
||||
# Cthulhu
|
||||
#
|
||||
# Copyright 2013-2015 Igalia, S.L.
|
||||
# Author: Joanmarie Diggs <jdiggs@igalia.com>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""Provides an Cthulhu-controlled caret for text content."""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2013-2015 Igalia, S.L."
|
||||
__license__ = "LGPL"
|
||||
|
||||
from . import cmdnames
|
||||
from . import debug
|
||||
from . import input_event
|
||||
from . import keybindings
|
||||
from . import messages
|
||||
from . import settings_manager
|
||||
|
||||
|
||||
class CaretNavigation:
|
||||
"""Implements the caret navigation support available to scripts."""
|
||||
|
||||
def __init__(self, script):
|
||||
if not (script and script.app):
|
||||
msg = "CARET NAVIGATION: Caret navigation requires a script and app."
|
||||
debug.printMessage(debug.LEVEL_INFO, msg)
|
||||
|
||||
self._script = script
|
||||
self._handlers = self._setup_handlers()
|
||||
self._bindings = self._setup_bindings()
|
||||
|
||||
def handles_navigation(self, handler):
|
||||
"""Returns True if handler is a navigation command."""
|
||||
|
||||
if handler not in self._handlers.values():
|
||||
return False
|
||||
|
||||
if handler.function == self._toggle_enabled:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def get_bindings(self):
|
||||
"""Returns the caret-navigation keybindings."""
|
||||
|
||||
return self._bindings
|
||||
|
||||
def get_handlers(self):
|
||||
"""Returns the caret-navigation handlers."""
|
||||
|
||||
return self._handlers
|
||||
|
||||
def _setup_handlers(self):
|
||||
"""Sets up and returns the caret-navigation input event handlers."""
|
||||
|
||||
handlers = {}
|
||||
|
||||
if not (self._script and self._script.app):
|
||||
return handlers
|
||||
|
||||
handlers["toggle_enabled"] = \
|
||||
input_event.InputEventHandler(
|
||||
self._toggle_enabled,
|
||||
cmdnames.CARET_NAVIGATION_TOGGLE)
|
||||
|
||||
handlers["next_character"] = \
|
||||
input_event.InputEventHandler(
|
||||
self._next_character,
|
||||
cmdnames.CARET_NAVIGATION_NEXT_CHAR)
|
||||
|
||||
handlers["previous_character"] = \
|
||||
input_event.InputEventHandler(
|
||||
self._previous_character,
|
||||
cmdnames.CARET_NAVIGATION_PREV_CHAR)
|
||||
|
||||
handlers["next_word"] = \
|
||||
input_event.InputEventHandler(
|
||||
self._next_word,
|
||||
cmdnames.CARET_NAVIGATION_NEXT_WORD)
|
||||
|
||||
handlers["previous_word"] = \
|
||||
input_event.InputEventHandler(
|
||||
self._previous_word,
|
||||
cmdnames.CARET_NAVIGATION_PREV_WORD)
|
||||
|
||||
handlers["next_line"] = \
|
||||
input_event.InputEventHandler(
|
||||
self._next_line,
|
||||
cmdnames.CARET_NAVIGATION_NEXT_LINE)
|
||||
|
||||
handlers["previous_line"] = \
|
||||
input_event.InputEventHandler(
|
||||
self._previous_line,
|
||||
cmdnames.CARET_NAVIGATION_PREV_LINE)
|
||||
|
||||
handlers["start_of_file"] = \
|
||||
input_event.InputEventHandler(
|
||||
self._start_of_file,
|
||||
cmdnames.CARET_NAVIGATION_FILE_START)
|
||||
|
||||
handlers["end_of_file"] = \
|
||||
input_event.InputEventHandler(
|
||||
self._end_of_file,
|
||||
cmdnames.CARET_NAVIGATION_FILE_END)
|
||||
|
||||
handlers["start_of_line"] = \
|
||||
input_event.InputEventHandler(
|
||||
self._start_of_line,
|
||||
cmdnames.CARET_NAVIGATION_LINE_START)
|
||||
|
||||
handlers["end_of_line"] = \
|
||||
input_event.InputEventHandler(
|
||||
self._end_of_line,
|
||||
cmdnames.CARET_NAVIGATION_LINE_END)
|
||||
|
||||
return handlers
|
||||
|
||||
def _setup_bindings(self):
|
||||
"""Sets up and returns the caret-navigation key bindings."""
|
||||
|
||||
bindings = keybindings.KeyBindings()
|
||||
|
||||
if not (self._script and self._script.app):
|
||||
return bindings
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"F12",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.ORCA_MODIFIER_MASK,
|
||||
self._handlers.get("toggle_enabled")))
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"Right",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.NO_MODIFIER_MASK,
|
||||
self._handlers.get("next_character")))
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"Left",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.NO_MODIFIER_MASK,
|
||||
self._handlers.get("previous_character")))
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"Right",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.CTRL_MODIFIER_MASK,
|
||||
self._handlers.get("next_word")))
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"Left",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.CTRL_MODIFIER_MASK,
|
||||
self._handlers.get("previous_word")))
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"Down",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.NO_MODIFIER_MASK,
|
||||
self._handlers.get("next_line")))
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"Up",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.NO_MODIFIER_MASK,
|
||||
self._handlers.get("previous_line")))
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"End",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.NO_MODIFIER_MASK,
|
||||
self._handlers.get("end_of_line")))
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"Home",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.NO_MODIFIER_MASK,
|
||||
self._handlers.get("start_of_line")))
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"End",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.CTRL_MODIFIER_MASK,
|
||||
self._handlers.get("end_of_file")))
|
||||
|
||||
bindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"Home",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.CTRL_MODIFIER_MASK,
|
||||
self._handlers.get("start_of_file")))
|
||||
|
||||
return bindings
|
||||
|
||||
@staticmethod
|
||||
def _toggle_enabled(script, event):
|
||||
"""Toggles caret navigation."""
|
||||
|
||||
if not event:
|
||||
return False
|
||||
|
||||
_settings_manager = settings_manager.getManager()
|
||||
enabled = not _settings_manager.getSetting('caretNavigationEnabled')
|
||||
if enabled:
|
||||
string = messages.CARET_CONTROL_ORCA
|
||||
else:
|
||||
string = messages.CARET_CONTROL_APP
|
||||
|
||||
script.presentMessage(string)
|
||||
_settings_manager.setSetting('caretNavigationEnabled', enabled)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _next_character(script, event):
|
||||
"""Moves to the next character."""
|
||||
|
||||
if not event:
|
||||
return False
|
||||
|
||||
obj, offset = script.utilities.nextContext()
|
||||
if not obj:
|
||||
return False
|
||||
|
||||
script.utilities.setCaretPosition(obj, offset)
|
||||
script.updateBraille(obj)
|
||||
script.sayCharacter(obj)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _previous_character(script, event):
|
||||
"""Moves to the previous character."""
|
||||
|
||||
if not event:
|
||||
return False
|
||||
|
||||
obj, offset = script.utilities.previousContext()
|
||||
if not obj:
|
||||
return False
|
||||
|
||||
script.utilities.setCaretPosition(obj, offset)
|
||||
script.updateBraille(obj)
|
||||
script.sayCharacter(obj)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _next_word(script, event):
|
||||
"""Moves to the next word."""
|
||||
|
||||
if not event:
|
||||
return False
|
||||
|
||||
obj, offset = script.utilities.nextContext(skipSpace=True)
|
||||
contents = script.utilities.getWordContentsAtOffset(obj, offset)
|
||||
if not contents:
|
||||
return False
|
||||
|
||||
obj, end, string = contents[-1][0], contents[-1][2], contents[-1][3]
|
||||
if string and string[-1].isspace():
|
||||
end -= 1
|
||||
|
||||
script.utilities.setCaretPosition(obj, end)
|
||||
script.updateBraille(obj)
|
||||
script.sayWord(obj)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _previous_word(script, event):
|
||||
"""Moves to the previous word."""
|
||||
|
||||
if not event:
|
||||
return False
|
||||
|
||||
obj, offset = script.utilities.previousContext(skipSpace=True)
|
||||
contents = script.utilities.getWordContentsAtOffset(obj, offset)
|
||||
if not contents:
|
||||
return False
|
||||
|
||||
obj, start = contents[0][0], contents[0][1]
|
||||
script.utilities.setCaretPosition(obj, start)
|
||||
script.updateBraille(obj)
|
||||
script.sayWord(obj)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _next_line(script, event):
|
||||
"""Moves to the next line."""
|
||||
|
||||
if not event:
|
||||
return False
|
||||
|
||||
if script.inSayAll():
|
||||
_settings_manager = settings_manager.getManager()
|
||||
if _settings_manager.getSetting('rewindAndFastForwardInSayAll'):
|
||||
msg = "CARET NAVIGATION: inSayAll and rewindAndFastforwardInSayAll is enabled"
|
||||
debug.printMessage(debug.LEVEL_INFO, msg)
|
||||
return True
|
||||
|
||||
obj, offset = script.utilities.getCaretContext()
|
||||
line = script.utilities.getLineContentsAtOffset(obj, offset)
|
||||
if not (line and line[0]):
|
||||
return False
|
||||
|
||||
contents = script.utilities.getNextLineContents()
|
||||
if not contents:
|
||||
return False
|
||||
|
||||
obj, start = contents[0][0], contents[0][1]
|
||||
script.utilities.setCaretPosition(obj, start)
|
||||
script.speakContents(contents, priorObj=line[-1][0])
|
||||
script.displayContents(contents)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _previous_line(script, event):
|
||||
"""Moves to the previous line."""
|
||||
|
||||
if not event:
|
||||
return False
|
||||
|
||||
if script.inSayAll():
|
||||
_settings_manager = settings_manager.getManager()
|
||||
if _settings_manager.getSetting('rewindAndFastForwardInSayAll'):
|
||||
msg = "CARET NAVIGATION: inSayAll and rewindAndFastforwardInSayAll is enabled"
|
||||
debug.printMessage(debug.LEVEL_INFO, msg)
|
||||
return True
|
||||
|
||||
|
||||
contents = script.utilities.getPreviousLineContents()
|
||||
if not contents:
|
||||
return False
|
||||
|
||||
obj, start = contents[0][0], contents[0][1]
|
||||
script.utilities.setCaretPosition(obj, start)
|
||||
script.speakContents(contents)
|
||||
script.displayContents(contents)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _start_of_line(script, event):
|
||||
"""Moves to the start of the line."""
|
||||
|
||||
if not event:
|
||||
return False
|
||||
|
||||
obj, offset = script.utilities.getCaretContext()
|
||||
line = script.utilities.getLineContentsAtOffset(obj, offset)
|
||||
if not (line and line[0]):
|
||||
return False
|
||||
|
||||
obj, start = line[0][0], line[0][1]
|
||||
script.utilities.setCaretPosition(obj, start)
|
||||
script.sayCharacter(obj)
|
||||
script.displayContents(line)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _end_of_line(script, event):
|
||||
"""Moves to the end of the line."""
|
||||
|
||||
if not event:
|
||||
return False
|
||||
|
||||
obj, offset = script.utilities.getCaretContext()
|
||||
line = script.utilities.getLineContentsAtOffset(obj, offset)
|
||||
if not (line and line[0]):
|
||||
return False
|
||||
|
||||
obj, end, string = line[-1][0], line[-1][2], line[-1][3]
|
||||
if string.strip() and string[-1].isspace():
|
||||
end -= 1
|
||||
|
||||
script.utilities.setCaretPosition(obj, end)
|
||||
script.sayCharacter(obj)
|
||||
script.displayContents(line)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _start_of_file(script, event):
|
||||
"""Moves to the start of the file."""
|
||||
|
||||
if not event:
|
||||
return False
|
||||
|
||||
document = script.utilities.documentFrame()
|
||||
obj, offset = script.utilities.findFirstCaretContext(document, 0)
|
||||
contents = script.utilities.getLineContentsAtOffset(obj, offset)
|
||||
if not contents:
|
||||
return False
|
||||
|
||||
obj, offset = contents[0][0], contents[0][1]
|
||||
script.utilities.setCaretPosition(obj, offset)
|
||||
script.speakContents(contents)
|
||||
script.displayContents(contents)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _end_of_file(script, event):
|
||||
"""Moves to the end of the file."""
|
||||
|
||||
if not event:
|
||||
return False
|
||||
|
||||
document = script.utilities.documentFrame()
|
||||
tokens = ["CARET NAVIGATION: Go to end of", document]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
|
||||
obj = script.utilities.getLastObjectInDocument(document)
|
||||
tokens = ["CARET NAVIGATION: Last object in", document, "is", obj]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
|
||||
offset = 0
|
||||
text = script.utilities.queryNonEmptyText(obj)
|
||||
if text:
|
||||
offset = text.characterCount - 1
|
||||
|
||||
while obj:
|
||||
lastobj, lastoffset = script.utilities.nextContext(obj, offset)
|
||||
if not lastobj:
|
||||
break
|
||||
obj, offset = lastobj, lastoffset
|
||||
|
||||
contents = script.utilities.getLineContentsAtOffset(obj, offset)
|
||||
if not contents:
|
||||
return False
|
||||
|
||||
obj, offset = contents[-1][0], contents[-1][2]
|
||||
script.utilities.setCaretPosition(obj, offset)
|
||||
script.speakContents(contents)
|
||||
script.displayContents(contents)
|
||||
return True
|
||||
@@ -1,929 +0,0 @@
|
||||
# Cthulhu
|
||||
#
|
||||
# Copyright 2010-2011 The Cthulhu Team
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""Implements generic chat support."""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2010-2011 The Cthulhu Team"
|
||||
__license__ = "LGPL"
|
||||
|
||||
from . import cmdnames
|
||||
from . import debug
|
||||
from . import guilabels
|
||||
from . import input_event
|
||||
from . import keybindings
|
||||
from . import messages
|
||||
from . import cthulhu_state
|
||||
from . import settings
|
||||
from . import settings_manager
|
||||
from .ax_object import AXObject
|
||||
from .ax_utilities import AXUtilities
|
||||
|
||||
_settingsManager = settings_manager.getManager()
|
||||
|
||||
#############################################################################
|
||||
# #
|
||||
# Ring List. A fixed size circular list by Flavio Catalani #
|
||||
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/435902 #
|
||||
# #
|
||||
# Included here to keep track of conversation histories. #
|
||||
# #
|
||||
#############################################################################
|
||||
|
||||
class RingList:
|
||||
def __init__(self, length):
|
||||
self.__data__ = []
|
||||
self.__full__ = 0
|
||||
self.__max__ = length
|
||||
self.__cur__ = 0
|
||||
|
||||
def append(self, x):
|
||||
if self.__full__ == 1:
|
||||
for i in range (0, self.__cur__ - 1):
|
||||
self.__data__[i] = self.__data__[i + 1]
|
||||
self.__data__[self.__cur__ - 1] = x
|
||||
else:
|
||||
self.__data__.append(x)
|
||||
self.__cur__ += 1
|
||||
if self.__cur__ == self.__max__:
|
||||
self.__full__ = 1
|
||||
|
||||
def get(self):
|
||||
return self.__data__
|
||||
|
||||
def remove(self):
|
||||
if (self.__cur__ > 0):
|
||||
del self.__data__[self.__cur__ - 1]
|
||||
self.__cur__ -= 1
|
||||
|
||||
def size(self):
|
||||
return self.__cur__
|
||||
|
||||
def maxsize(self):
|
||||
return self.__max__
|
||||
|
||||
def __str__(self):
|
||||
return ''.join(self.__data__)
|
||||
|
||||
#############################################################################
|
||||
# #
|
||||
# Conversation #
|
||||
# #
|
||||
#############################################################################
|
||||
|
||||
class Conversation:
|
||||
|
||||
# The number of messages to keep in the history
|
||||
#
|
||||
MESSAGE_LIST_LENGTH = 9
|
||||
|
||||
def __init__(self, name, accHistory, inputArea=None):
|
||||
|
||||
"""Creates a new instance of the Conversation class.
|
||||
|
||||
Arguments:
|
||||
- name: the chatroom/conversation name
|
||||
- accHistory: the accessible which holds the conversation history
|
||||
- inputArea: the editable text object for this conversation.
|
||||
"""
|
||||
|
||||
self.name = name
|
||||
self.accHistory = accHistory
|
||||
self.inputArea = inputArea
|
||||
|
||||
# A cyclic list to hold the chat room history for this conversation
|
||||
#
|
||||
self._messageHistory = RingList(Conversation.MESSAGE_LIST_LENGTH)
|
||||
|
||||
# Initially populate the cyclic lists with empty strings.
|
||||
#
|
||||
i = 0
|
||||
while i < self._messageHistory.maxsize():
|
||||
self.addMessage("")
|
||||
i += 1
|
||||
|
||||
# Keep track of the last typing status because some platforms (e.g.
|
||||
# MSN) seem to issue the status constantly and even though it has
|
||||
# not changed.
|
||||
#
|
||||
self._typingStatus = ""
|
||||
|
||||
def addMessage(self, message):
|
||||
"""Adds the current message to the message history.
|
||||
|
||||
Arguments:
|
||||
- message: A string containing the message to add
|
||||
"""
|
||||
|
||||
self._messageHistory.append(message)
|
||||
|
||||
def getNthMessage(self, messageNumber):
|
||||
"""Returns the specified message from the message history.
|
||||
|
||||
Arguments:
|
||||
- messageNumber: the index of the message to get.
|
||||
"""
|
||||
|
||||
messages = self._messageHistory.get()
|
||||
|
||||
return messages[messageNumber]
|
||||
|
||||
def getTypingStatus(self):
|
||||
"""Returns the typing status of the buddy in this conversation."""
|
||||
|
||||
return self._typingStatus
|
||||
|
||||
def setTypingStatus(self, status):
|
||||
"""Sets the typing status of the buddy in this conversation.
|
||||
|
||||
Arguments:
|
||||
- status: a string describing the current status.
|
||||
"""
|
||||
|
||||
self._typingStatus = status
|
||||
|
||||
#############################################################################
|
||||
# #
|
||||
# ConversationList #
|
||||
# #
|
||||
#############################################################################
|
||||
|
||||
class ConversationList:
|
||||
|
||||
def __init__(self, messageListLength):
|
||||
|
||||
"""Creates a new instance of the ConversationList class.
|
||||
|
||||
Arguments:
|
||||
- messageListLength: the size of the message history to keep.
|
||||
"""
|
||||
|
||||
self.conversations = []
|
||||
|
||||
# A cyclic list to hold the most recent (messageListLength) previous
|
||||
# messages for all conversations in the ConversationList.
|
||||
#
|
||||
self._messageHistory = RingList(messageListLength)
|
||||
|
||||
# A corresponding cyclic list to hold the name of the conversation
|
||||
# associated with each message in the messageHistory.
|
||||
#
|
||||
self._roomHistory = RingList(messageListLength)
|
||||
|
||||
# Initially populate the cyclic lists with empty strings.
|
||||
#
|
||||
i = 0
|
||||
while i < self._messageHistory.maxsize():
|
||||
self.addMessage("", None)
|
||||
i += 1
|
||||
|
||||
def addMessage(self, message, conversation):
|
||||
"""Adds the current message to the message history.
|
||||
|
||||
Arguments:
|
||||
- message: A string containing the message to add
|
||||
- conversation: The instance of the Conversation class with which
|
||||
the message is associated
|
||||
"""
|
||||
|
||||
if not conversation:
|
||||
name = ""
|
||||
else:
|
||||
if not self.hasConversation(conversation):
|
||||
self.addConversation(conversation)
|
||||
name = conversation.name
|
||||
|
||||
self._messageHistory.append(message)
|
||||
self._roomHistory.append(name)
|
||||
|
||||
def getNthMessageAndName(self, messageNumber):
|
||||
"""Returns a list containing the specified message from the message
|
||||
history and the name of the chatroom/conversation associated with
|
||||
that message.
|
||||
|
||||
Arguments:
|
||||
- messageNumber: the index of the message to get.
|
||||
"""
|
||||
|
||||
messages = self._messageHistory.get()
|
||||
rooms = self._roomHistory.get()
|
||||
|
||||
return messages[messageNumber], rooms[messageNumber]
|
||||
|
||||
def hasConversation(self, conversation):
|
||||
"""Returns True if we know about this conversation.
|
||||
|
||||
Arguments:
|
||||
- conversation: the conversation of interest
|
||||
"""
|
||||
|
||||
return conversation in self.conversations
|
||||
|
||||
def getNConversations(self):
|
||||
"""Returns the number of conversations we currently know about."""
|
||||
|
||||
return len(self.conversations)
|
||||
|
||||
def addConversation(self, conversation):
|
||||
"""Adds conversation to the list of conversations.
|
||||
|
||||
Arguments:
|
||||
- conversation: the conversation to add
|
||||
"""
|
||||
|
||||
self.conversations.append(conversation)
|
||||
|
||||
def removeConversation(self, conversation):
|
||||
"""Removes conversation from the list of conversations.
|
||||
|
||||
Arguments:
|
||||
- conversation: the conversation to remove
|
||||
|
||||
Returns True if conversation was successfully removed.
|
||||
"""
|
||||
|
||||
# TODO - JD: In the Pidgin script, I do not believe we handle the
|
||||
# case where a conversation window is closed. I *think* it remains
|
||||
# in the overall chat history. What do we want to do in that case?
|
||||
# I would assume that we'd want to remove it.... So here's a method
|
||||
# to do so. Nothing in the Chat class uses it yet.
|
||||
#
|
||||
try:
|
||||
self.conversations.remove(conversation)
|
||||
except Exception:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
#############################################################################
|
||||
# #
|
||||
# Chat #
|
||||
# #
|
||||
#############################################################################
|
||||
|
||||
class Chat:
|
||||
"""This class implements the chat functionality which is available to
|
||||
scripts.
|
||||
"""
|
||||
|
||||
def __init__(self, script, buddyListAncestries):
|
||||
"""Creates an instance of the Chat class.
|
||||
|
||||
Arguments:
|
||||
- script: the script with which this instance is associated.
|
||||
- buddyListAncestries: a list of lists of roles beginning
|
||||
with the object serving as the actual buddy list (e.g.
|
||||
ROLE_TREE_TABLE) and ending with the top level object (e.g.
|
||||
ROLE_FRAME).
|
||||
"""
|
||||
|
||||
self._script = script
|
||||
self._buddyListAncestries = buddyListAncestries
|
||||
|
||||
# Keybindings to provide conversation message history. The message
|
||||
# review order will be based on the index within the list. Thus F1
|
||||
# is associated with the most recent message, F2 the message before
|
||||
# that, and so on. A script could override this. Setting messageKeys
|
||||
# to ["a", "b", "c" ... ] will cause "a" to be associated with the
|
||||
# most recent message, "b" to be associated with the message before
|
||||
# that, etc. Scripts can also override the messageKeyModifier.
|
||||
#
|
||||
self.messageKeys = \
|
||||
["F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"]
|
||||
self.messageKeyModifier = keybindings.ORCA_MODIFIER_MASK
|
||||
self.inputEventHandlers = {}
|
||||
self.setupInputEventHandlers()
|
||||
self.keyBindings = self.getKeyBindings()
|
||||
|
||||
# The length of the message history will be based on how many keys
|
||||
# are bound to the task of providing it.
|
||||
#
|
||||
self.messageListLength = len(self.messageKeys)
|
||||
self._conversationList = ConversationList(self.messageListLength)
|
||||
|
||||
self.focusedChannelRadioButton = None
|
||||
self.allChannelsRadioButton = None
|
||||
self.allMessagesRadioButton = None
|
||||
self.buddyTypingCheckButton = None
|
||||
self.chatRoomHistoriesCheckButton = None
|
||||
self.speakNameCheckButton = None
|
||||
|
||||
def setupInputEventHandlers(self):
|
||||
"""Defines InputEventHandler fields for chat functions which
|
||||
will be used by the script associated with this chat instance."""
|
||||
|
||||
self.inputEventHandlers["togglePrefixHandler"] = \
|
||||
input_event.InputEventHandler(
|
||||
self.togglePrefix,
|
||||
cmdnames.CHAT_TOGGLE_ROOM_NAME_PREFIX)
|
||||
|
||||
self.inputEventHandlers["toggleBuddyTypingHandler"] = \
|
||||
input_event.InputEventHandler(
|
||||
self.toggleBuddyTyping,
|
||||
cmdnames.CHAT_TOGGLE_BUDDY_TYPING)
|
||||
|
||||
self.inputEventHandlers["toggleMessageHistoriesHandler"] = \
|
||||
input_event.InputEventHandler(
|
||||
self.toggleMessageHistories,
|
||||
cmdnames.CHAT_TOGGLE_MESSAGE_HISTORIES)
|
||||
|
||||
self.inputEventHandlers["reviewMessage"] = \
|
||||
input_event.InputEventHandler(
|
||||
self.readPreviousMessage,
|
||||
cmdnames.CHAT_PREVIOUS_MESSAGE)
|
||||
|
||||
return
|
||||
|
||||
def getKeyBindings(self):
|
||||
"""Defines the chat-related key bindings which will be used by
|
||||
the script associated with this chat instance.
|
||||
|
||||
Returns: an instance of keybindings.KeyBindings.
|
||||
"""
|
||||
|
||||
keyBindings = keybindings.KeyBindings()
|
||||
|
||||
keyBindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.NO_MODIFIER_MASK,
|
||||
self.inputEventHandlers["togglePrefixHandler"]))
|
||||
|
||||
keyBindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.NO_MODIFIER_MASK,
|
||||
self.inputEventHandlers["toggleBuddyTypingHandler"]))
|
||||
|
||||
keyBindings.add(
|
||||
keybindings.KeyBinding(
|
||||
"",
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.NO_MODIFIER_MASK,
|
||||
self.inputEventHandlers["toggleMessageHistoriesHandler"]))
|
||||
|
||||
for messageKey in self.messageKeys:
|
||||
keyBindings.add(
|
||||
keybindings.KeyBinding(
|
||||
messageKey,
|
||||
self.messageKeyModifier,
|
||||
keybindings.ORCA_MODIFIER_MASK,
|
||||
self.inputEventHandlers["reviewMessage"]))
|
||||
|
||||
return keyBindings
|
||||
|
||||
def getAppPreferencesGUI(self):
|
||||
"""Return a GtkGrid containing the application unique configuration
|
||||
GUI items for the current application. """
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
grid = Gtk.Grid()
|
||||
grid.set_border_width(12)
|
||||
|
||||
label = guilabels.CHAT_SPEAK_ROOM_NAME
|
||||
value = _settingsManager.getSetting('chatSpeakRoomName')
|
||||
self.speakNameCheckButton = Gtk.CheckButton.new_with_mnemonic(label)
|
||||
self.speakNameCheckButton.set_active(value)
|
||||
grid.attach(self.speakNameCheckButton, 0, 0, 1, 1)
|
||||
|
||||
label = guilabels.CHAT_ANNOUNCE_BUDDY_TYPING
|
||||
value = _settingsManager.getSetting('chatAnnounceBuddyTyping')
|
||||
self.buddyTypingCheckButton = Gtk.CheckButton.new_with_mnemonic(label)
|
||||
self.buddyTypingCheckButton.set_active(value)
|
||||
grid.attach(self.buddyTypingCheckButton, 0, 1, 1, 1)
|
||||
|
||||
label = guilabels.CHAT_SEPARATE_MESSAGE_HISTORIES
|
||||
value = _settingsManager.getSetting('chatRoomHistories')
|
||||
self.chatRoomHistoriesCheckButton = \
|
||||
Gtk.CheckButton.new_with_mnemonic(label)
|
||||
self.chatRoomHistoriesCheckButton.set_active(value)
|
||||
grid.attach(self.chatRoomHistoriesCheckButton, 0, 2, 1, 1)
|
||||
|
||||
messagesFrame = Gtk.Frame()
|
||||
grid.attach(messagesFrame, 0, 3, 1, 1)
|
||||
label = Gtk.Label(f"<b>{guilabels.CHAT_SPEAK_MESSAGES_FROM}</b>")
|
||||
label.set_use_markup(True)
|
||||
messagesFrame.set_label_widget(label)
|
||||
|
||||
messagesAlignment = Gtk.Alignment.new(0.5, 0.5, 1, 1)
|
||||
messagesAlignment.set_padding(0, 0, 12, 0)
|
||||
messagesFrame.add(messagesAlignment)
|
||||
messagesGrid = Gtk.Grid()
|
||||
messagesAlignment.add(messagesGrid)
|
||||
|
||||
value = _settingsManager.getSetting('chatMessageVerbosity')
|
||||
|
||||
label = guilabels.CHAT_SPEAK_MESSAGES_ALL
|
||||
rb1 = Gtk.RadioButton.new_with_mnemonic(None, label)
|
||||
rb1.set_active(value == settings.CHAT_SPEAK_ALL)
|
||||
self.allMessagesRadioButton = rb1
|
||||
messagesGrid.attach(self.allMessagesRadioButton, 0, 0, 1, 1)
|
||||
|
||||
label = guilabels.CHAT_SPEAK_MESSAGES_ACTIVE
|
||||
rb2 = Gtk.RadioButton.new_with_mnemonic(None, label)
|
||||
rb2.join_group(rb1)
|
||||
rb2.set_active(value == settings.CHAT_SPEAK_FOCUSED_CHANNEL)
|
||||
self.focusedChannelRadioButton = rb2
|
||||
messagesGrid.attach(self.focusedChannelRadioButton, 0, 1, 1, 1)
|
||||
|
||||
label = guilabels.CHAT_SPEAK_MESSAGES_ALL_IF_FOCUSED % \
|
||||
AXObject.get_name(self._script.app)
|
||||
rb3 = Gtk.RadioButton.new_with_mnemonic(None, label)
|
||||
rb3.join_group(rb1)
|
||||
rb3.set_active(value == settings.CHAT_SPEAK_ALL_IF_FOCUSED)
|
||||
self.allChannelsRadioButton = rb3
|
||||
messagesGrid.attach(self.allChannelsRadioButton, 0, 2, 1, 1)
|
||||
|
||||
grid.show_all()
|
||||
|
||||
return grid
|
||||
|
||||
def getPreferencesFromGUI(self):
|
||||
"""Returns a dictionary with the app-specific preferences."""
|
||||
|
||||
if self.allChannelsRadioButton.get_active():
|
||||
verbosity = settings.CHAT_SPEAK_ALL_IF_FOCUSED
|
||||
elif self.focusedChannelRadioButton.get_active():
|
||||
verbosity = settings.CHAT_SPEAK_FOCUSED_CHANNEL
|
||||
else:
|
||||
verbosity = settings.CHAT_SPEAK_ALL
|
||||
|
||||
return {
|
||||
'chatMessageVerbosity': verbosity,
|
||||
'chatSpeakRoomName': self.speakNameCheckButton.get_active(),
|
||||
'chatAnnounceBuddyTyping': self.buddyTypingCheckButton.get_active(),
|
||||
'chatRoomHistories': self.chatRoomHistoriesCheckButton.get_active(),
|
||||
}
|
||||
|
||||
########################################################################
|
||||
# #
|
||||
# InputEvent handlers and supporting utilities #
|
||||
# #
|
||||
########################################################################
|
||||
|
||||
def togglePrefix(self, script, inputEvent):
|
||||
""" Toggle whether we prefix chat room messages with the name of
|
||||
the chat room.
|
||||
|
||||
Arguments:
|
||||
- script: the script associated with this event
|
||||
- inputEvent: if not None, the input event that caused this action.
|
||||
"""
|
||||
|
||||
line = messages.CHAT_ROOM_NAME_PREFIX_ON
|
||||
speakRoomName = _settingsManager.getSetting('chatSpeakRoomName')
|
||||
_settingsManager.setSetting('chatSpeakRoomName', not speakRoomName)
|
||||
if speakRoomName:
|
||||
line = messages.CHAT_ROOM_NAME_PREFIX_OFF
|
||||
self._script.presentMessage(line)
|
||||
|
||||
return True
|
||||
|
||||
def toggleBuddyTyping(self, script, inputEvent):
|
||||
""" Toggle whether we announce when our buddies are typing a message.
|
||||
|
||||
Arguments:
|
||||
- script: the script associated with this event
|
||||
- inputEvent: if not None, the input event that caused this action.
|
||||
"""
|
||||
|
||||
line = messages.CHAT_BUDDY_TYPING_ON
|
||||
announceTyping = _settingsManager.getSetting('chatAnnounceBuddyTyping')
|
||||
_settingsManager.setSetting(
|
||||
'chatAnnounceBuddyTyping', not announceTyping)
|
||||
if announceTyping:
|
||||
line = messages.CHAT_BUDDY_TYPING_OFF
|
||||
self._script.presentMessage(line)
|
||||
|
||||
return True
|
||||
|
||||
def toggleMessageHistories(self, script, inputEvent):
|
||||
""" Toggle whether we provide chat room specific message histories.
|
||||
|
||||
Arguments:
|
||||
- script: the script associated with this event
|
||||
- inputEvent: if not None, the input event that caused this action.
|
||||
"""
|
||||
|
||||
line = messages.CHAT_SEPARATE_HISTORIES_ON
|
||||
roomHistories = _settingsManager.getSetting('chatRoomHistories')
|
||||
_settingsManager.setSetting('chatRoomHistories', not roomHistories)
|
||||
if roomHistories:
|
||||
line = messages.CHAT_SEPARATE_HISTORIES_OFF
|
||||
self._script.presentMessage(line)
|
||||
|
||||
return True
|
||||
|
||||
def readPreviousMessage(self, script, inputEvent=None, index=0):
|
||||
""" Speak/braille a previous chat room message.
|
||||
|
||||
Arguments:
|
||||
- script: the script associated with this event
|
||||
- inputEvent: if not None, the input event that caused this action.
|
||||
- index: The index of the message to read -- by default, the most
|
||||
recent message. If we get an inputEvent, however, the value of
|
||||
index is ignored and the index of the event_string with respect
|
||||
to self.messageKeys is used instead.
|
||||
"""
|
||||
|
||||
try:
|
||||
index = self.messageKeys.index(inputEvent.event_string)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
messageNumber = self.messageListLength - (index + 1)
|
||||
message, chatRoomName = None, None
|
||||
|
||||
if _settingsManager.getSetting('chatRoomHistories'):
|
||||
conversation = self.getConversation(cthulhu_state.locusOfFocus)
|
||||
if conversation:
|
||||
message = conversation.getNthMessage(messageNumber)
|
||||
chatRoomName = conversation.name
|
||||
else:
|
||||
message, chatRoomName = \
|
||||
self._conversationList.getNthMessageAndName(messageNumber)
|
||||
|
||||
if message and chatRoomName:
|
||||
self.utterMessage(chatRoomName, message, True)
|
||||
|
||||
def utterMessage(self, chatRoomName, message, focused=True):
|
||||
""" Speak/braille a chat room message.
|
||||
|
||||
Arguments:
|
||||
- chatRoomName: name of the chat room this message came from
|
||||
- message: the chat room message
|
||||
- focused: whether or not the current chatroom has focus. Defaults
|
||||
to True so that we can use this method to present chat history
|
||||
as well as incoming messages.
|
||||
"""
|
||||
|
||||
# Only speak/braille the new message if it matches how the user
|
||||
# wants chat messages spoken.
|
||||
#
|
||||
verbosity = _settingsManager.getAppSetting(self._script.app, 'chatMessageVerbosity')
|
||||
if cthulhu_state.activeScript.name != self._script.name \
|
||||
and verbosity == settings.CHAT_SPEAK_ALL_IF_FOCUSED:
|
||||
return
|
||||
elif not focused and verbosity == settings.CHAT_SPEAK_FOCUSED_CHANNEL:
|
||||
return
|
||||
|
||||
text = ""
|
||||
if chatRoomName and \
|
||||
_settingsManager.getAppSetting(self._script.app, 'chatSpeakRoomName'):
|
||||
text = messages.CHAT_MESSAGE_FROM_ROOM % chatRoomName
|
||||
|
||||
if not settings.presentChatRoomLast:
|
||||
text = self._script.utilities.appendString(text, message)
|
||||
else:
|
||||
text = self._script.utilities.appendString(message, text)
|
||||
|
||||
if len(text.strip()):
|
||||
voice = self._script.speechGenerator.voice(string=text)
|
||||
self._script.speakMessage(text, voice=voice)
|
||||
self._script.displayBrailleMessage(text)
|
||||
|
||||
def getMessageFromEvent(self, event):
|
||||
"""Get the actual displayed message. This will almost always be the
|
||||
unaltered any_data from an event of type object:text-changed:insert.
|
||||
|
||||
Arguments:
|
||||
- event: the Event from which to take the text.
|
||||
|
||||
Returns the string which should be presented as the newly-inserted
|
||||
text. (Things like chatroom name prefacing get handled elsewhere.)
|
||||
"""
|
||||
|
||||
return event.any_data
|
||||
|
||||
def presentInsertedText(self, event):
|
||||
"""Gives the Chat class an opportunity to present the text from the
|
||||
text inserted Event.
|
||||
|
||||
Arguments:
|
||||
- event: the text inserted Event
|
||||
|
||||
Returns True if we handled this event here; otherwise False, which
|
||||
tells the associated script that is not a chat event that requires
|
||||
custom handling.
|
||||
"""
|
||||
|
||||
if not event \
|
||||
or not event.type.startswith("object:text-changed:insert") \
|
||||
or not event.any_data:
|
||||
return False
|
||||
|
||||
if self.isGenericTextObject(event.source):
|
||||
# The script should handle non-chat specific text areas (e.g.,
|
||||
# adding a new account).
|
||||
#
|
||||
return False
|
||||
|
||||
elif self.isInBuddyList(event.source):
|
||||
# These are status changes. What the Pidgin script currently
|
||||
# does for these is ignore them. It might be nice to add
|
||||
# some options to allow the user to customize what status
|
||||
# changes are presented. But for now, we'll ignore them
|
||||
# across the board.
|
||||
#
|
||||
return True
|
||||
|
||||
elif self.isTypingStatusChangedEvent(event):
|
||||
self.presentTypingStatusChange(event, event.any_data)
|
||||
return True
|
||||
|
||||
elif self.isChatRoomMsg(event.source):
|
||||
# We always automatically go back to focus tracking mode when
|
||||
# someone sends us a message.
|
||||
#
|
||||
if self._script.flatReviewPresenter.is_active():
|
||||
self._script.flatReviewPresenter.quit()
|
||||
|
||||
if self.isNewConversation(event.source):
|
||||
name = self.getChatRoomName(event.source)
|
||||
conversation = Conversation(name, event.source)
|
||||
else:
|
||||
conversation = self.getConversation(event.source)
|
||||
name = conversation.name
|
||||
message = self.getMessageFromEvent(event).strip("\n")
|
||||
if message:
|
||||
self.addMessageToHistory(message, conversation)
|
||||
|
||||
# The user may or may not want us to present this message. Also,
|
||||
# don't speak the name if it's the focused chat.
|
||||
#
|
||||
focused = self.isFocusedChat(event.source)
|
||||
if focused:
|
||||
name = ""
|
||||
if message:
|
||||
self.utterMessage(name, message, focused)
|
||||
return True
|
||||
|
||||
elif self.isAutoCompletedTextEvent(event):
|
||||
text = event.any_data
|
||||
voice = self._script.speechGenerator.voice(string=text)
|
||||
self._script.speakMessage(text, voice=voice)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def presentTypingStatusChange(self, event, status):
|
||||
"""Presents a change in typing status for the current conversation
|
||||
if the status has indeed changed and if the user wants to hear it.
|
||||
|
||||
Arguments:
|
||||
- event: the accessible Event
|
||||
- status: a string containing the status change
|
||||
|
||||
Returns True if we spoke the change; False otherwise
|
||||
"""
|
||||
|
||||
if _settingsManager.getSetting('chatAnnounceBuddyTyping'):
|
||||
conversation = self.getConversation(event.source)
|
||||
if conversation and (status != conversation.getTypingStatus()):
|
||||
voice = self._script.speechGenerator.voice(string=status)
|
||||
self._script.speakMessage(status, voice=voice)
|
||||
conversation.setTypingStatus(status)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def addMessageToHistory(self, message, conversation):
|
||||
"""Adds message to both the individual conversation's history
|
||||
as well as to the complete history stored in our conversation
|
||||
list.
|
||||
|
||||
Arguments:
|
||||
- message: a string containing the message to be added
|
||||
- conversation: the instance of the Conversation class to which
|
||||
this message belongs
|
||||
"""
|
||||
|
||||
conversation.addMessage(message)
|
||||
self._conversationList.addMessage(message, conversation)
|
||||
|
||||
########################################################################
|
||||
# #
|
||||
# Convenience methods for identifying, locating different accessibles #
|
||||
# #
|
||||
########################################################################
|
||||
|
||||
def isGenericTextObject(self, obj):
|
||||
"""Returns True if the given accessible seems to be something
|
||||
unrelated to the custom handling we're attempting to do here.
|
||||
|
||||
Arguments:
|
||||
- obj: the accessible object to examine.
|
||||
"""
|
||||
|
||||
return AXUtilities.is_editable(obj) and AXUtilities.is_single_line(obj)
|
||||
|
||||
def isBuddyList(self, obj):
|
||||
"""Returns True if obj is the list of buddies in the buddy list
|
||||
window. Note that this method relies upon a hierarchical check,
|
||||
using a list of hierarchies provided by the script. Scripts
|
||||
which have more reliable means of identifying the buddy list
|
||||
can override this method.
|
||||
|
||||
Arguments:
|
||||
- obj: the accessible being examined
|
||||
"""
|
||||
|
||||
if obj is None:
|
||||
return False
|
||||
|
||||
for roleList in self._buddyListAncestries:
|
||||
if self._script.utilities.hasMatchingHierarchy(obj, roleList):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def isInBuddyList(self, obj, includeList=True):
|
||||
"""Returns True if obj is, or is inside of, the buddy list.
|
||||
|
||||
Arguments:
|
||||
- obj: the accessible being examined
|
||||
- includeList: whether or not the list itself should be
|
||||
considered "in" the buddy list.
|
||||
"""
|
||||
|
||||
if includeList and self.isBuddyList(obj):
|
||||
return True
|
||||
|
||||
for roleList in self._buddyListAncestries:
|
||||
role = roleList[0]
|
||||
candidate = AXObject.find_ancestor(obj, lambda x: AXObject.get_role(x) == role)
|
||||
if self.isBuddyList(candidate):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def isNewConversation(self, obj):
|
||||
"""Returns True if the given accessible is the chat history
|
||||
associated with a new conversation.
|
||||
|
||||
Arguments:
|
||||
- obj: the accessible object to examine.
|
||||
"""
|
||||
|
||||
conversation = self.getConversation(obj)
|
||||
return not self._conversationList.hasConversation(conversation)
|
||||
|
||||
def getConversation(self, obj):
|
||||
"""Attempts to locate the conversation associated with obj.
|
||||
|
||||
Arguments:
|
||||
- obj: the accessible of interest
|
||||
|
||||
Returns the conversation if found; None otherwise
|
||||
"""
|
||||
|
||||
if not obj:
|
||||
return None
|
||||
|
||||
name = ""
|
||||
# TODO - JD: If we have multiple chats going on and those
|
||||
# chats have the same name, and we're in the input area,
|
||||
# this approach will fail. What I should probably do instead
|
||||
# is, upon creation of a new conversation, figure out where
|
||||
# the input area is and save it. For now, I just want to get
|
||||
# things working. And people should not be in multiple chat
|
||||
# rooms with identical names anyway. :-)
|
||||
#
|
||||
if (AXUtilities.is_text(obj) or AXObject.is_entry(obj)) \
|
||||
and AXUtilities.is_editable(obj):
|
||||
name = self.getChatRoomName(obj)
|
||||
|
||||
for conversation in self._conversationList.conversations:
|
||||
if name:
|
||||
if name == conversation.name:
|
||||
return conversation
|
||||
# Doing an equality check seems to be preferable here to
|
||||
# utilities.isSameObject as a result of false positives.
|
||||
#
|
||||
elif obj == conversation.accHistory:
|
||||
return conversation
|
||||
|
||||
return None
|
||||
|
||||
def isChatRoomMsg(self, obj):
|
||||
"""Returns True if the given accessible is the text object for
|
||||
associated with a chat room conversation.
|
||||
|
||||
Arguments:
|
||||
- obj: the accessible object to examine.
|
||||
"""
|
||||
|
||||
if AXUtilities.is_text(obj) and AXUtilities.is_scroll_pane(AXObject.get_parent(obj)):
|
||||
return not AXUtilities.is_editable(obj) and AXUtilities.is_multi_line(obj)
|
||||
return False
|
||||
|
||||
def isFocusedChat(self, obj):
|
||||
"""Returns True if we plan to treat this chat as focused for
|
||||
the purpose of deciding whether or not a message should be
|
||||
presented to the user.
|
||||
|
||||
Arguments:
|
||||
- obj: the accessible object to examine.
|
||||
"""
|
||||
|
||||
if AXUtilities.is_showing(obj):
|
||||
active = self._script.utilities.topLevelObjectIsActiveAndCurrent(obj)
|
||||
tokens = ["INFO:", obj, "'s window is focused chat:", active]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return active
|
||||
|
||||
tokens = ["INFO:", obj, "is not focused chat (not showing)"]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
return False
|
||||
|
||||
def getChatRoomName(self, obj):
|
||||
"""Attempts to find the name of the current chat room.
|
||||
|
||||
Arguments:
|
||||
- obj: The accessible of interest
|
||||
|
||||
Returns a string containing what we think is the chat room name.
|
||||
"""
|
||||
|
||||
# Most of the time, it seems that the name can be found in the
|
||||
# page tab which is the ancestor of the chat history. Failing
|
||||
# that, we'll look at the frame name. Failing that, scripts
|
||||
# should override this method. :-)
|
||||
#
|
||||
def pred(x):
|
||||
return AXUtilities.is_page_tab(x) or AXUtilities.is_frame(x)
|
||||
|
||||
ancestor = AXObject.find_ancestor(obj, pred)
|
||||
name = ""
|
||||
try:
|
||||
text = self._script.utilities.displayedText(ancestor)
|
||||
if text.lower().strip() != self._script.name.lower().strip():
|
||||
name = text
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Some applications don't trash their page tab list when there is
|
||||
# only one active chat, but instead they remove the text or hide
|
||||
# the item. Therefore, we'll give it one more shot.
|
||||
#
|
||||
if not name:
|
||||
ancestor = AXObject.find_ancestor(obj, AXUtilities.is_frame)
|
||||
try:
|
||||
text = self._script.utilities.displayedText(ancestor)
|
||||
if text.lower().strip() != self._script.name.lower().strip():
|
||||
name = text
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return name
|
||||
|
||||
def isAutoCompletedTextEvent(self, event):
|
||||
"""Returns True if event is associated with text being autocompleted.
|
||||
|
||||
Arguments:
|
||||
- event: the accessible event being examined
|
||||
"""
|
||||
|
||||
if not AXUtilities.is_text(event.source):
|
||||
return False
|
||||
|
||||
lastKey, mods = self._script.utilities.lastKeyAndModifiers()
|
||||
if lastKey == "Tab" and event.any_data and event.any_data != "\t":
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def isTypingStatusChangedEvent(self, event):
|
||||
"""Returns True if event is associated with a change in typing status.
|
||||
|
||||
Arguments:
|
||||
- event: the accessible event being examined
|
||||
"""
|
||||
|
||||
# TODO - JD: I still need to figure this one out. Pidgin seems to
|
||||
# no longer be presenting this change in the conversation history
|
||||
# as it was doing before. And I'm not yet sure what other apps do.
|
||||
# In the meantime, scripts can override this.
|
||||
#
|
||||
return False
|
||||
@@ -1,817 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Cthulhu
|
||||
#
|
||||
# Copyright 2004-2008 Sun Microsystems Inc.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""Provides getCharacterName that maps punctuation marks and other
|
||||
individual characters into localized words."""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
|
||||
__license__ = "LGPL"
|
||||
|
||||
from . import mathsymbols
|
||||
from . import cthulhu_state
|
||||
from .cthulhu_i18n import _
|
||||
|
||||
# chnames is a dictionary where the keys represent a unicode character
|
||||
# and the values represent the common term used for the character.
|
||||
#
|
||||
chnames = {}
|
||||
|
||||
# Translators: this is the spoken word for the space character
|
||||
#
|
||||
chnames[" "] = _("space")
|
||||
|
||||
# Translators: this is the spoken word for the newline character
|
||||
#
|
||||
chnames["\n"] = _("newline")
|
||||
|
||||
# Translators: this is the spoken word for the tab character
|
||||
#
|
||||
chnames["\t"] = _("tab")
|
||||
|
||||
# Translators: this is the spoken word for the character '!' (U+0021)
|
||||
#
|
||||
chnames["!"] = _("exclaim")
|
||||
|
||||
# Translators: this is the spoken word for the character '"' (U+0022)
|
||||
#
|
||||
chnames["\""] = _("quote")
|
||||
|
||||
# Translators: this is the spoken word for the character '#' (U+0023)
|
||||
#
|
||||
chnames["#"] = _("number")
|
||||
|
||||
# Translators: this is the spoken word for the character '$' (U+0024)
|
||||
#
|
||||
chnames["$"] = _("dollar")
|
||||
|
||||
# Translators: this is the spoken word for the character '%' (U+0025)
|
||||
#
|
||||
chnames["%"] = _("percent")
|
||||
|
||||
# Translators: this is the spoken word for the character '&' (U+0026)
|
||||
#
|
||||
chnames["&"] = _("and")
|
||||
|
||||
# Translators: this is the spoken word for the character ''' (U+0027)
|
||||
#
|
||||
chnames["'"] = _("apostrophe")
|
||||
|
||||
# Translators: this is the spoken word for the character '(' (U+0028)
|
||||
#
|
||||
chnames["("] = _("left paren")
|
||||
|
||||
# Translators: this is the spoken word for the character ')' (U+0029)
|
||||
#
|
||||
chnames[")"] = _("right paren")
|
||||
|
||||
# Translators: this is the spoken word for the character '*' (U+002a)
|
||||
#
|
||||
chnames["*"] = _("star")
|
||||
|
||||
# Translators: this is the spoken word for the character '+' (U+002b)
|
||||
#
|
||||
chnames["+"] = _("plus")
|
||||
|
||||
# Translators: this is the spoken word for the character ',' (U+002c)
|
||||
#
|
||||
chnames[","] = _("comma")
|
||||
|
||||
# Translators: this is the spoken word for the character '-' (U+002d)
|
||||
#
|
||||
chnames["-"] = _("dash")
|
||||
|
||||
# Translators: this is the spoken word for the character '.' (U+002e)
|
||||
#
|
||||
chnames["."] = _("dot")
|
||||
|
||||
# Translators: this is the spoken word for the character '/' (U+002f)
|
||||
#
|
||||
chnames["/"] = _("slash")
|
||||
|
||||
# Translators: this is the spoken word for the character ':' (U+003a)
|
||||
#
|
||||
chnames[":"] = _("colon")
|
||||
|
||||
# Translators: this is the spoken word for the character ';' (U+003b)
|
||||
#
|
||||
chnames[";"] = _("semicolon")
|
||||
|
||||
# Translators: this is the spoken word for the character '< ' (U+003c)
|
||||
#
|
||||
chnames["<"] = _("less")
|
||||
|
||||
# Translators: this is the spoken word for the character '=' (U+003d)
|
||||
#
|
||||
chnames["="] = _("equals")
|
||||
|
||||
# Translators: this is the spoken word for the character '> ' (U+003e)
|
||||
#
|
||||
chnames[">"] = _("greater")
|
||||
|
||||
# Translators: this is the spoken word for the character '?' (U+003f)
|
||||
#
|
||||
chnames["?"] = _("question")
|
||||
|
||||
# Translators: this is the spoken word for the character '@' (U+0040)
|
||||
#
|
||||
chnames["@"] = _("at")
|
||||
|
||||
# Translators: this is the spoken word for the character '[' (U+005b)
|
||||
#
|
||||
chnames["["] = _("left bracket")
|
||||
|
||||
# Translators: this is the spoken word for the character '\' (U+005c)
|
||||
#
|
||||
chnames["\\"] = _("backslash")
|
||||
|
||||
# Translators: this is the spoken word for the character ']' (U+005d)
|
||||
#
|
||||
chnames["]"] = _("right bracket")
|
||||
|
||||
# Translators: this is the spoken word for the character '^' (U+005e)
|
||||
#
|
||||
chnames["^"] = _("caret")
|
||||
|
||||
# Translators: this is the spoken word for the character '_' (U+005f)
|
||||
#
|
||||
chnames["_"] = _("underline")
|
||||
|
||||
# Translators: this is the spoken word for the character '`' (U+0060)
|
||||
#
|
||||
chnames["`"] = _("grave")
|
||||
|
||||
# Translators: this is the spoken word for the character '{' (U+007b)
|
||||
#
|
||||
chnames["{"] = _("left brace")
|
||||
|
||||
# Translators: this is the spoken word for the character '|' (U+007c)
|
||||
#
|
||||
chnames["|"] = _("vertical bar")
|
||||
|
||||
# Translators: this is the spoken word for the character '}' (U+007d)
|
||||
#
|
||||
chnames["}"] = _("right brace")
|
||||
|
||||
# Translators: this is the spoken word for the character '~' (U+007e)
|
||||
#
|
||||
chnames["~"] = _("tilde")
|
||||
|
||||
# Translators: this is the spoken character for the no break space
|
||||
# character (e.g., " " in HTML -- U+00a0)
|
||||
#
|
||||
chnames['\u00a0'] = _("no break space")
|
||||
|
||||
# Translators: this is the spoken word for the character '¡' (U+00a1)
|
||||
#
|
||||
chnames['\u00a1'] = _("inverted exclamation point")
|
||||
|
||||
# Translators: this is the spoken word for the character '¢' (U+00a2)
|
||||
#
|
||||
chnames['\u00a2'] = _("cents")
|
||||
|
||||
# Translators: this is the spoken word for the character '£' (U+00a3)
|
||||
#
|
||||
chnames['\u00a3'] = _("pounds")
|
||||
|
||||
# Translators: this is the spoken word for the character '¤' (U+00a4)
|
||||
#
|
||||
chnames['\u00a4'] = _("currency sign")
|
||||
|
||||
# Translators: this is the spoken word for the character '¥' (U+00a5)
|
||||
#
|
||||
chnames['\u00a5'] = _("yen")
|
||||
|
||||
# Translators: this is the spoken word for the character '¦' (U+00a6)
|
||||
#
|
||||
chnames['\u00a6'] = _("broken bar")
|
||||
|
||||
# Translators: this is the spoken word for the character '§' (U+00a7)
|
||||
#
|
||||
chnames['\u00a7'] = _("section")
|
||||
|
||||
# Translators: this is the spoken word for the character '¨' (U+00a8)
|
||||
#
|
||||
chnames['\u00a8'] = _("diaeresis")
|
||||
|
||||
# Translators: this is the spoken word for the character '©' (U+00a9)
|
||||
#
|
||||
chnames['\u00a9'] = _("copyright")
|
||||
|
||||
# Translators: this is the spoken word for the character 'ª' (U+00aa)
|
||||
#
|
||||
chnames['\u00aa'] = _("superscript a")
|
||||
|
||||
# Translators: this is the spoken word for the character '«' (U+00ab)
|
||||
#
|
||||
chnames['\u00ab'] = _("left double angle bracket")
|
||||
|
||||
# Translators: this is the spoken word for the character '¬' (U+00ac)
|
||||
#
|
||||
chnames['\u00ac'] = _("logical not")
|
||||
|
||||
# Translators: this is the spoken word for the character '' (U+00ad)
|
||||
#
|
||||
chnames['\u00ad'] = _("soft hyphen")
|
||||
|
||||
# Translators: this is the spoken word for the character '®' (U+00ae)
|
||||
#
|
||||
chnames['\u00ae'] = _("registered")
|
||||
|
||||
# Translators: this is the spoken word for the character '¯' (U+00af)
|
||||
#
|
||||
chnames['\u00af'] = _("macron")
|
||||
|
||||
# Translators: this is the spoken word for the character '°' (U+00b0)
|
||||
#
|
||||
chnames['\u00b0'] = _("degrees")
|
||||
|
||||
# Translators: this is the spoken word for the character '±' (U+00b1)
|
||||
#
|
||||
chnames['\u00b1'] = _("plus or minus")
|
||||
|
||||
# Translators: this is the spoken word for the character '²' (U+00b2)
|
||||
#
|
||||
chnames['\u00b2'] = _("superscript 2")
|
||||
|
||||
# Translators: this is the spoken word for the character '³' (U+00b3)
|
||||
#
|
||||
chnames['\u00b3'] = _("superscript 3")
|
||||
|
||||
# Translators: this is the spoken word for the character '´' (U+00b4)
|
||||
#
|
||||
chnames['\u00b4'] = _("acute")
|
||||
|
||||
# Translators: this is the spoken word for the character 'µ' (U+00b5)
|
||||
#
|
||||
chnames['\u00b5'] = _("mu")
|
||||
|
||||
# Translators: this is the spoken word for the character '¶' (U+00b6)
|
||||
#
|
||||
chnames['\u00b6'] = _("paragraph marker")
|
||||
|
||||
# Translators: this is the spoken word for the character '·' (U+00b7)
|
||||
#
|
||||
chnames['\u00b7'] = _("middle dot")
|
||||
|
||||
# Translators: this is the spoken word for the character '¸' (U+00b8)
|
||||
#
|
||||
chnames['\u00b8'] = _("cedilla")
|
||||
|
||||
# Translators: this is the spoken word for the character '¹' (U+00b9)
|
||||
#
|
||||
chnames['\u00b9'] = _("superscript 1")
|
||||
|
||||
# Translators: this is the spoken word for the character 'º' (U+00ba)
|
||||
#
|
||||
chnames['\u00ba'] = _("ordinal")
|
||||
|
||||
# Translators: this is the spoken word for the character '»' (U+00bb)
|
||||
#
|
||||
chnames['\u00bb'] = _("right double angle bracket")
|
||||
|
||||
# Translators: this is the spoken word for the character '¼' (U+00bc)
|
||||
#
|
||||
chnames['\u00bc'] = _("one fourth")
|
||||
|
||||
# Translators: this is the spoken word for the character '½' (U+00bd)
|
||||
#
|
||||
chnames['\u00bd'] = _("one half")
|
||||
|
||||
# Translators: this is the spoken word for the character '¾' (U+00be)
|
||||
#
|
||||
chnames['\u00be'] = _("three fourths")
|
||||
|
||||
# Translators: this is the spoken word for the character '¿' (U+00bf)
|
||||
#
|
||||
chnames['\u00bf'] = _("inverted question mark")
|
||||
|
||||
# Translators: this is the spoken word for the character 'á' (U+00e1)
|
||||
#
|
||||
chnames['\u00e1'] = _("a acute")
|
||||
|
||||
# Translators: this is the spoken word for the character 'À' (U+00c0)
|
||||
#
|
||||
chnames['\u00c0'] = _("A GRAVE")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Á' (U+00c1)
|
||||
#
|
||||
chnames['\u00c1'] = _("A ACUTE")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Â' (U+00c2)
|
||||
#
|
||||
chnames['\u00c2'] = _("A CIRCUMFLEX")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Ã' (U+00c3)
|
||||
#
|
||||
chnames['\u00c3'] = _("A TILDE")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Ä' (U+00c4)
|
||||
#
|
||||
chnames['\u00c4'] = _("A UMLAUT")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Å' (U+00c5)
|
||||
#
|
||||
chnames['\u00c5'] = _("A RING")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Æ' (U+00c6)
|
||||
#
|
||||
chnames['\u00c6'] = _("A E")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Ç' (U+00c7)
|
||||
#
|
||||
chnames['\u00c7'] = _("C CEDILLA")
|
||||
|
||||
# Translators: this is the spoken word for the character 'È' (U+00c8)
|
||||
#
|
||||
chnames['\u00c8'] = _("E GRAVE")
|
||||
|
||||
# Translators: this is the spoken word for the character 'É' (U+00c9)
|
||||
#
|
||||
chnames['\u00c9'] = _("E ACUTE")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Ê' (U+00ca)
|
||||
#
|
||||
chnames['\u00ca'] = _("E CIRCUMFLEX")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Ë' (U+00cb)
|
||||
#
|
||||
chnames['\u00cb'] = _("E UMLAUT")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Ì' (U+00cc)
|
||||
#
|
||||
chnames['\u00cc'] = _("I GRAVE")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Í' (U+00cd)
|
||||
#
|
||||
chnames['\u00cd'] = _("I ACUTE")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Î' (U+00ce)
|
||||
#
|
||||
chnames['\u00ce'] = _("I CIRCUMFLEX")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Ï' (U+00cf)
|
||||
#
|
||||
chnames['\u00cf'] = _("I UMLAUT")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Ð' (U+00d0)
|
||||
#
|
||||
chnames['\u00d0'] = _("ETH")
|
||||
|
||||
# Translators: this is the spoken word for the character 'Ñ' (U+00d1)
|
||||
#
|
||||