Remove files that should be ignored based on .gitignore

This commit is contained in:
Storm Dragon
2024-10-17 16:28:08 -04:00
parent 3278dd8a6c
commit 9569c50a0b
1448 changed files with 0 additions and 1537952 deletions
-655
View File
@@ -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
View File
@@ -1 +0,0 @@
SUBDIRS = cthulhu
-655
View File
@@ -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.%
-9
View File
@@ -1,9 +0,0 @@
Makefile
Makefile.in
cthulhu
cthulhu-setup
cthulhu_bin.py
cthulhu_i18n.py
cthulhu_platform.py
*.pyc
*~
-111
View File
@@ -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
-6
View File
@@ -1,6 +0,0 @@
"""Cthulhu Screen Reader"""
__copyright__ = "Copyright (c) 2005-2006 Sun Microsystems Inc."
__license__ = "LGPL"
-125
View File
@@ -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)
-178
View File
@@ -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
-148
View File
@@ -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
-581
View File
@@ -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
-124
View File
@@ -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
-206
View File
@@ -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
-361
View File
@@ -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)
-6
View File
@@ -1,6 +0,0 @@
cthulhu_python_PYTHON = \
__init__.py \
json_backend.py
cthulhu_pythondir=$(pkgpythondir)/backends
View File
-215
View File
@@ -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)
-322
View File
@@ -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
-527
View File
@@ -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
-290
View File
@@ -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")
}
-212
View File
@@ -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 = '&amp;'
elif char == '<':
char = '&lt;'
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)
-160
View File
@@ -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")
-457
View File
@@ -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
-929
View File
@@ -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
-817
View File
@@ -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., "&nbsp;" 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)
#