summaryrefslogtreecommitdiff
path: root/caribou
diff options
context:
space:
mode:
authorEitan Isaacson <eitan@monotonous.org>2011-04-24 16:57:28 -0700
committerEitan Isaacson <eitan@monotonous.org>2011-05-02 10:21:08 -0700
commite1ae6ee91507a47cbe2fe537f3107c17390bc0e2 (patch)
tree04ea360d747c6e489e50c9649fe33cd6723f83a8 /caribou
parentf195b7aafa878568e7aae8ccc27e3716ae7c21cc (diff)
downloadcaribou-e1ae6ee91507a47cbe2fe537f3107c17390bc0e2.tar.gz
Major re-work of Python modules:
* Created Antler to use new keyboard model via PyGI * Rearanged settings to accomodate view/model seperation. * Created in preferences executable, it will be a standalone generic caribou settings UI where we will eventually get the important stuff in GNOME's control panel. * DBusified the UI.
Diffstat (limited to 'caribou')
-rw-r--r--caribou/Makefile.am10
-rw-r--r--caribou/__init__.py3
-rw-r--r--caribou/antler/Makefile.am10
-rw-r--r--caribou/antler/__init__.py0
-rw-r--r--caribou/antler/keyboard_view.py164
-rw-r--r--caribou/antler/main.py30
-rw-r--r--caribou/antler/window.py (renamed from caribou/ui/window.py)147
-rw-r--r--caribou/common/Makefile.am11
-rw-r--r--caribou/common/__init__.py1
-rw-r--r--caribou/common/const.py43
-rw-r--r--caribou/common/settings.py161
-rw-r--r--caribou/daemon/Makefile.am8
-rw-r--r--caribou/daemon/__init__.py1
-rw-r--r--caribou/daemon/main.py150
-rw-r--r--caribou/i18n.py.in (renamed from caribou/ui/i18n.py.in)0
-rw-r--r--caribou/settings/Makefile.am11
-rw-r--r--caribou/settings/__init__.py5
-rw-r--r--caribou/settings/caribou_settings.py64
-rw-r--r--caribou/settings/preferences_window.py (renamed from caribou/ui/preferences_window.py)70
-rw-r--r--caribou/settings/setting_types.py (renamed from caribou/common/setting_types.py)10
-rw-r--r--caribou/settings/settings_manager.py (renamed from caribou/common/settings_manager.py)9
-rw-r--r--caribou/ui/Makefile.am16
-rw-r--r--caribou/ui/__init__.py1
-rw-r--r--caribou/ui/keyboard.py535
-rw-r--r--caribou/ui/main.py275
-rw-r--r--caribou/ui/opacity.py83
-rw-r--r--caribou/ui/scan.py216
27 files changed, 604 insertions, 1430 deletions
diff --git a/caribou/Makefile.am b/caribou/Makefile.am
index 962a2b4..713d8ad 100644
--- a/caribou/Makefile.am
+++ b/caribou/Makefile.am
@@ -1,11 +1,15 @@
cariboudir = $(pkgpythondir)/
caribou_PYTHON = \
- __init__.py
+ __init__.py \
+ i18n.py
SUBDIRS = \
- common/ \
- ui/
+ antler/ \
+ settings/ \
+ daemon/
+
+DISTCLEANFILES = i18n.py
clean-local:
rm -rf *.pyc *.pyo
diff --git a/caribou/__init__.py b/caribou/__init__.py
index 1aadcdc..cf8fdc7 100644
--- a/caribou/__init__.py
+++ b/caribou/__init__.py
@@ -1 +1,2 @@
-data_path = "data/"
+from i18n import _
+APP_NAME=_("Caribou")
diff --git a/caribou/antler/Makefile.am b/caribou/antler/Makefile.am
new file mode 100644
index 0000000..51efe0f
--- /dev/null
+++ b/caribou/antler/Makefile.am
@@ -0,0 +1,10 @@
+caribou_antlerdir = $(pkgpythondir)/antler/
+
+caribou_antler_PYTHON = \
+ __init__.py \
+ keyboard_view.py \
+ main.py \
+ window.py
+
+clean-local:
+ rm -rf *.pyc *.pyo
diff --git a/caribou/antler/__init__.py b/caribou/antler/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/caribou/antler/__init__.py
diff --git a/caribou/antler/keyboard_view.py b/caribou/antler/keyboard_view.py
new file mode 100644
index 0000000..6cea11b
--- /dev/null
+++ b/caribou/antler/keyboard_view.py
@@ -0,0 +1,164 @@
+from caribou.settings.preferences_window import PreferencesDialog
+from caribou.settings import CaribouSettings
+from gi.repository import Gtk
+from gi.repository import Gdk
+from gi.repository import Caribou
+import gobject
+
+PRETTY_LABELS = {
+ "BackSpace" : u'\u232b',
+ "space" : u' ',
+ "Return" : u'\u23ce',
+ 'Caribou_Prefs' : u'\u2328',
+ 'Caribou_ShiftUp' : u'\u2b06',
+ 'Caribou_ShiftDown' : u'\u2b07',
+ 'Caribou_Emoticons' : u'\u263a',
+ 'Caribou_Symbols' : u'123',
+ 'Caribou_Symbols_More' : u'{#*',
+ 'Caribou_Alpha' : u'Abc'
+}
+
+class AntlerKey(Gtk.Button):
+ def __init__(self, key):
+ gobject.GObject.__init__(self)
+ self.caribou_key = key
+ self.connect("pressed", self._on_pressed)
+ self.connect("released", self._on_released)
+ self.set_label(self._get_key_label())
+ if key.props.name == "Caribou_Prefs":
+ key.connect("key-clicked", self._on_prefs_clicked)
+ if key.get_extended_keys ():
+ self._sublevel = AntlerSubLevel(self)
+
+ def _on_prefs_clicked(self, key):
+ p = PreferencesDialog(CaribouSettings())
+ p.show_all()
+ p.run()
+ p.destroy()
+
+ def _get_key_label(self):
+ label = self.caribou_key.props.name
+ if PRETTY_LABELS.has_key(self.caribou_key.props.name):
+ label = PRETTY_LABELS[self.caribou_key.props.name]
+ elif self.caribou_key.props.name.startswith('Caribou_'):
+ label = self.caribou_key.name.replace('Caribou_', '')
+ else:
+ unichar = unichr(Gdk.keyval_to_unicode(self.caribou_key.props.keyval))
+ if not unichar.isspace() and unichar != u'\x00':
+ label = unichar
+
+ return label
+
+ def _on_pressed(self, button):
+ self.caribou_key.press()
+
+ def _on_released(self, button):
+ self.caribou_key.release()
+
+ def do_get_preferred_width_for_height(self, w):
+ return (w, w)
+
+ def do_get_request_mode(self):
+ return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH
+
+class AntlerSubLevel(Gtk.Window):
+ def __init__(self, key):
+ gobject.GObject.__init__(self, type=Gtk.WindowType.POPUP)
+
+ self.set_decorated(False)
+ self.set_resizable(False)
+ self.set_accept_focus(False)
+ self.set_position(Gtk.WindowPosition.MOUSE)
+ self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
+
+ key.caribou_key.connect("notify::show-subkeys", self._on_show_subkeys)
+ self._key = key
+
+ layout = AntlerLayout()
+ layout.add_row(key.caribou_key.get_extended_keys())
+ self.add(layout)
+
+ def _on_show_subkeys(self, key, prop):
+ parent = self._key.get_toplevel()
+ if key.props.show_subkeys:
+ self.set_transient_for(parent)
+ parent.set_sensitive(False)
+ self.show_all()
+ else:
+ parent.set_sensitive(True)
+ self.hide()
+
+class AntlerLayout(Gtk.Grid):
+ KEY_SPAN = 4
+
+ def __init__(self, level=None):
+ gobject.GObject.__init__(self)
+ self.set_column_homogeneous(True)
+ self.set_row_homogeneous(True)
+ self.set_row_spacing(4)
+ self.set_column_spacing(4)
+ if level:
+ self.load_rows(level.get_rows ())
+
+ def add_row(self, row, row_num=0):
+ col_num = 0
+ for i, key in enumerate(row):
+ antler_key = AntlerKey(key)
+ self.attach(antler_key,
+ col_num + int(key.props.margin_left * self.KEY_SPAN),
+ row_num * self.KEY_SPAN,
+ int(self.KEY_SPAN * key.props.width),
+ self.KEY_SPAN)
+ col_num += int((key.props.width + key.props.margin_left ) * self.KEY_SPAN)
+
+
+ def load_rows(self, rows):
+ for row_num, row in enumerate(rows):
+ self.add_row(row.get_keys(), row_num)
+
+class AntlerKeyboardView(Gtk.Notebook):
+ def __init__(self):
+ gobject.GObject.__init__(self)
+ self.set_show_tabs(False)
+ self.keyboard_model = Caribou.KeyboardModel()
+ self.keyboard_model.connect("notify::active-group", self._on_group_changed)
+ self.layers = {}
+ for gname in self.keyboard_model.get_groups():
+ group = self.keyboard_model.get_group(gname)
+ self.layers[gname] = {}
+ group.connect("notify::active-level", self._on_level_changed)
+ for lname in group.get_levels():
+ level = group.get_level(lname)
+ layout = AntlerLayout(level)
+ layout.show()
+ self.layers[gname][lname] = self.append_page(layout, None)
+
+ self._set_to_active_layer()
+
+ def _on_level_changed(self, group, prop):
+ self._set_to_active_layer()
+
+ def _on_group_changed(self, kb, prop):
+ self._set_to_active_layer()
+
+ def _set_to_active_layer(self):
+ active_group_name = self.keyboard_model.props.active_group
+ active_group = self.keyboard_model.get_group(active_group_name)
+ active_level_name = active_group.props.active_level
+
+ self.set_current_page(self.layers[active_group_name][active_level_name])
+
+
+if __name__ == "__main__":
+ import signal
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+ w = Gtk.Window()
+ w.set_accept_focus(False)
+
+ kb = AntlerKeyboardView()
+ w.add(kb)
+
+ w.show_all()
+
+ Gtk.main()
diff --git a/caribou/antler/main.py b/caribou/antler/main.py
new file mode 100644
index 0000000..ce0eca3
--- /dev/null
+++ b/caribou/antler/main.py
@@ -0,0 +1,30 @@
+from gi.repository import Caribou
+from window import AntlerWindowEntry
+from keyboard_view import AntlerKeyboardView
+import gobject
+
+class AntlerKeyboardService(Caribou.KeyboardService):
+ def __init__(self):
+ gobject.GObject.__init__(self)
+ self.register_keyboard("Antler")
+ self.window = AntlerWindowEntry(AntlerKeyboardView())
+
+ def run(self):
+ loop = gobject.MainLoop()
+ loop.run()
+
+ def do_show(self):
+ self.window.show_all()
+
+ def do_hide(self):
+ self.window.hide()
+
+ def do_set_cursor_location (self, x, y, w, h):
+ self.window.set_cursor_location(x, y, w, h)
+
+ def do_set_entry_location (self, x, y, w, h):
+ self.window.set_entry_location(x, y, w, h)
+
+if __name__ == "__main__":
+ antler_keyboard_service = AntlerKeyboardService()
+ antler_keyboard_service.run()
diff --git a/caribou/ui/window.py b/caribou/antler/window.py
index 2dc7423..973824a 100644
--- a/caribou/ui/window.py
+++ b/caribou/antler/window.py
@@ -20,9 +20,6 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-from caribou import data_path
-from opacity import ProximityWindowBase
-
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import Clutter
@@ -30,10 +27,71 @@ import os
import sys
import gobject
-Clutter.init("caribou")
+Clutter.init("antler")
+
+class ProximityWindowBase(object):
+ def __init__(self, min_alpha=1.0, max_alpha=1.0, max_distance=100):
+ if self.__class__ == ProximityWindowBase:
+ raise TypeError, \
+ "ProximityWindowBase is an abstract class, " \
+ "must be subclassed with a Gtk.Window"
+ self.connect('map-event', self.__onmapped)
+ self.max_distance = max_distance
+ if max_alpha < min_alpha:
+ raise ValueError, "min_alpha can't be larger than max_alpha"
+ self.min_alpha = min_alpha
+ self.max_alpha = max_alpha
+
+ def __onmapped(self, obj, event):
+ if self.is_composited():
+ self.set_opacity(self.max_alpha)
+ if self.max_alpha != self.min_alpha:
+ # Don't waste CPU if the max and min are equal.
+ glib.timeout_add(80, self._proximity_check)
+
+ def _proximity_check(self):
+ px, py = self.get_pointer()
+
+ ww = self.get_allocated_width()
+ wh = self.get_allocated_height()
+
+ distance = self._get_distance_to_bbox(px, py, ww, wh)
-class CaribouWindow(Gtk.Window, Clutter.Animatable, ProximityWindowBase):
- __gtype_name__ = "CaribouWindow"
+ opacity = (self.max_alpha - self.min_alpha) * \
+ (1 - min(distance, self.max_distance)/self.max_distance)
+ opacity += self.min_alpha
+
+ self.set_opacity(opacity)
+ return self.props.visible
+
+ def _get_distance_to_bbox(self, px, py, bw, bh):
+ if px < 0:
+ x_distance = float(abs(px))
+ elif px > bw:
+ x_distance = float(px - bw)
+ else:
+ x_distance = 0.0
+
+ if py < 0:
+ y_distance = float(abs(px))
+ elif py > bh:
+ y_distance = float(py - bh)
+ else:
+ y_distance = 0.0
+
+ if y_distance == 0 and x_distance == 0:
+ return 0.0
+ elif y_distance != 0 and x_distance == 0:
+ return y_distance
+ elif y_distance == 0 and x_distance != 0:
+ return x_distance
+ else:
+ x2 = 0 if x_distance > 0 else bw
+ y2 = 0 if y_distance > 0 else bh
+ return sqrt((px - x2)**2 + (py - y2)**2)
+
+class AntlerWindow(Gtk.Window, Clutter.Animatable, ProximityWindowBase):
+ __gtype_name__ = "AntlerWindow"
__gproperties__ = {
'animated-window-position' : (gobject.TYPE_PYOBJECT, 'Window position',
'Window position in X, Y coordinates',
@@ -49,7 +107,7 @@ class CaribouWindow(Gtk.Window, Clutter.Animatable, ProximityWindowBase):
max_alpha=max_alpha,
max_distance=max_distance)
- self.set_name("CaribouWindow")
+ self.set_name("AntlerWindow")
self._vbox = Gtk.VBox()
self.add(self._vbox)
@@ -61,7 +119,7 @@ class CaribouWindow(Gtk.Window, Clutter.Animatable, ProximityWindowBase):
self._cursor_location = Rectangle()
self._entry_location = Rectangle()
self._default_placement = default_placement or \
- CaribouWindowPlacement()
+ AntlerWindowPlacement()
self.connect('show', self._on_window_show)
@@ -111,12 +169,12 @@ class CaribouWindow(Gtk.Window, Clutter.Animatable, ProximityWindowBase):
self.keyboard.destroy()
super(Gtk.Window, self).destroy()
- def set_cursor_location(self, cursor_location):
- self._cursor_location = cursor_location
+ def set_cursor_location(self, x, y, w, h):
+ self._cursor_location = Rectangle(x, y, w, h)
self._update_position()
- def set_entry_location(self, entry_location):
- self._entry_location = entry_location
+ def set_entry_location(self, x, y, w, h):
+ self._entry_location = Rectangle(x, y, w, h)
self._update_position()
def set_default_placement(self, default_placement):
@@ -174,25 +232,25 @@ class CaribouWindow(Gtk.Window, Clutter.Animatable, ProximityWindowBase):
def _calculate_axis(self, axis_placement, root_bbox):
bbox = root_bbox
- if axis_placement.stickto == CaribouWindowPlacement.CURSOR:
+ if axis_placement.stickto == AntlerWindowPlacement.CURSOR:
bbox = self._cursor_location
- elif axis_placement.stickto == CaribouWindowPlacement.ENTRY:
+ elif axis_placement.stickto == AntlerWindowPlacement.ENTRY:
bbox = self._entry_location
offset = axis_placement.get_offset(bbox.x, bbox.y)
- if axis_placement.align == CaribouWindowPlacement.END:
+ if axis_placement.align == AntlerWindowPlacement.END:
offset += axis_placement.get_length(bbox.width, bbox.height)
- if axis_placement.gravitate == CaribouWindowPlacement.INSIDE:
+ if axis_placement.gravitate == AntlerWindowPlacement.INSIDE:
offset -= axis_placement.get_length(
self.get_allocated_width(),
self.get_allocated_height())
- elif axis_placement.align == CaribouWindowPlacement.START:
- if axis_placement.gravitate == CaribouWindowPlacement.OUTSIDE:
+ elif axis_placement.align == AntlerWindowPlacement.START:
+ if axis_placement.gravitate == AntlerWindowPlacement.OUTSIDE:
offset -= axis_placement.get_length(
self.get_allocated_width(),
self.get_allocated_height())
- elif axis_placement.align == CaribouWindowPlacement.CENTER:
+ elif axis_placement.align == AntlerWindowPlacement.CENTER:
offset += axis_placement.get_length(bbox.width, bbox.height)/2
return offset
@@ -211,18 +269,18 @@ class CaribouWindow(Gtk.Window, Clutter.Animatable, ProximityWindowBase):
req = child.size_request()
self.resize(req.width + border, req.height + border)
-class CaribouWindowDocked(CaribouWindow):
- __gtype_name__ = "CaribouWindowDocked"
+class AntlerWindowDocked(AntlerWindow):
+ __gtype_name__ = "AntlerWindowDocked"
def __init__(self, text_entry_mech):
- placement = CaribouWindowPlacement(
- xalign=CaribouWindowPlacement.END,
- yalign=CaribouWindowPlacement.START,
- xstickto=CaribouWindowPlacement.SCREEN,
- ystickto=CaribouWindowPlacement.SCREEN,
- xgravitate=CaribouWindowPlacement.INSIDE)
+ placement = AntlerWindowPlacement(
+ xalign=AntlerWindowPlacement.END,
+ yalign=AntlerWindowPlacement.START,
+ xstickto=AntlerWindowPlacement.SCREEN,
+ ystickto=AntlerWindowPlacement.SCREEN,
+ xgravitate=AntlerWindowPlacement.INSIDE)
- CaribouWindow.__init__(self, text_entry_mech, placement)
+ AntlerWindow.__init__(self, text_entry_mech, placement)
self.connect('map-event', self.__onmapped)
@@ -240,32 +298,32 @@ class CaribouWindowDocked(CaribouWindow):
def hide(self):
animation = self._roll_out()
- animation.connect('completed', lambda x: CaribouWindow.hide(self))
+ animation.connect('completed', lambda x: AntlerWindow.hide(self))
-class CaribouWindowEntry(CaribouWindow):
- __gtype_name__ = "CaribouWindowEntry"
+class AntlerWindowEntry(AntlerWindow):
+ __gtype_name__ = "AntlerWindowEntry"
def __init__(self, text_entry_mech):
- placement = CaribouWindowPlacement(
- xalign=CaribouWindowPlacement.START,
- xstickto=CaribouWindowPlacement.ENTRY,
- ystickto=CaribouWindowPlacement.ENTRY,
- xgravitate=CaribouWindowPlacement.INSIDE,
- ygravitate=CaribouWindowPlacement.OUTSIDE)
+ placement = AntlerWindowPlacement(
+ xalign=AntlerWindowPlacement.START,
+ xstickto=AntlerWindowPlacement.ENTRY,
+ ystickto=AntlerWindowPlacement.ENTRY,
+ xgravitate=AntlerWindowPlacement.INSIDE,
+ ygravitate=AntlerWindowPlacement.OUTSIDE)
- CaribouWindow.__init__(self, text_entry_mech, placement)
+ AntlerWindow.__init__(self, text_entry_mech, placement)
def _calculate_axis(self, axis_placement, root_bbox):
- offset = CaribouWindow._calculate_axis(self, axis_placement, root_bbox)
+ offset = AntlerWindow._calculate_axis(self, axis_placement, root_bbox)
if axis_placement.axis == 'y':
if offset + self.get_allocated_height() > root_bbox.height + root_bbox.y:
- new_axis_placement = axis_placement.copy(align=CaribouWindowPlacement.START)
- offset = CaribouWindow._calculate_axis(self, new_axis_placement, root_bbox)
+ new_axis_placement = axis_placement.copy(align=AntlerWindowPlacement.START)
+ offset = AntlerWindow._calculate_axis(self, new_axis_placement, root_bbox)
return offset
-class CaribouWindowPlacement(object):
+class AntlerWindowPlacement(object):
START = 'start'
END = 'end'
CENTER = 'center'
@@ -326,7 +384,6 @@ class CaribouWindowPlacement(object):
ystickto or self.CURSOR,
ygravitate or self.OUTSIDE)
-
class Rectangle(object):
def __init__(self, x=0, y=0, width=0, height=0):
self.x = x
@@ -335,11 +392,11 @@ class Rectangle(object):
self.height = height
if __name__ == "__main__":
- import keyboard
+ import keyboard_view
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
- w = CaribouWindowDocked(keyboard.CaribouKeyboard())
+ w = AntlerWindowDocked(keyboard_view.AntlerKeyboardView())
w.show_all()
try:
diff --git a/caribou/common/Makefile.am b/caribou/common/Makefile.am
deleted file mode 100644
index 74805af..0000000
--- a/caribou/common/Makefile.am
+++ /dev/null
@@ -1,11 +0,0 @@
-caribou_commondir = $(pkgpythondir)/common/
-
-caribou_common_PYTHON = \
- __init__.py \
- const.py \
- settings_manager.py \
- settings.py \
- setting_types.py
-
-clean-local:
- rm -rf *.pyc *.pyo
diff --git a/caribou/common/__init__.py b/caribou/common/__init__.py
deleted file mode 100644
index 8d1c8b6..0000000
--- a/caribou/common/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/caribou/common/const.py b/caribou/common/const.py
deleted file mode 100644
index 0591e5d..0000000
--- a/caribou/common/const.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Caribou - text entry and UI navigation application
-#
-# Copyright (C) 2010 Warp Networks S.L.
-# * Contributor: David Pellicer <dpellicer@warp.es>
-#
-# This program 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 program 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 program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-from os.path import join
-from os.path import dirname
-
-from caribou import data_path
-
-# Application name
-APP_NAME = 'Caribou'
-APP_SLUG_NAME = 'caribou'
-
-# Paths
-DATA_DIR = data_path
-LAYOUTS_DIR = join(DATA_DIR, 'layouts')
-
-# Preferences
-CARIBOU_GCONF = '/org/gnome/caribou/osk'
-
-# Key types
-NORMAL_KEY_TYPE = 'normal'
-LAYOUT_SWITCHER_KEY_TYPE = 'layout_switcher'
-PREFERENCES_KEY_TYPE = 'preferences'
-DUMMY_KEY_TYPE = 'dummy'
-MASK_KEY_TYPE = 'mask'
diff --git a/caribou/common/settings.py b/caribou/common/settings.py
deleted file mode 100644
index 7837508..0000000
--- a/caribou/common/settings.py
+++ /dev/null
@@ -1,161 +0,0 @@
-import os
-from setting_types import *
-from gettext import gettext as _
-import caribou.common.const as const
-import caribou.ui.i18n
-import xml.dom.minidom
-import json
-
-GSETTINGS_SCHEMA = "org.gnome.caribou"
-
-settings = SettingsGroup("_top", "", [
- SettingsGroup("keyboard", _("Keyboard"), [
- SettingsGroup("general", _("General"), [
- StringSetting(
- "geometry", _("Keyboard geometry"), "natural",
- _("The keyboard geometery Caribou should use"),
- _("The keyboard geometery determines the shape "
- "and complexity of the keyboard, it could range from "
- "a 'natural' look and feel good for composing simple "
- "text, to a fullscale keyboard."),
- allowed=[(('natural'), _('Natural'))])]),
- SettingsGroup("color", _("Color"), [
- BooleanSetting(
- "default_colors", _("Use system theme"), True,
- _("Use the default theme colors"),
- insensitive_when_true=["normal_color",
- "mouse_over_color"]),
- ColorSetting(
- "normal_color", _("Normal state"), "grey80",
- _("Color of the keys when there is no "
- "event on them")),
- ColorSetting(
- "mouse_over_color", _("Mouse over"), "yellow",
- _("Color of the keys when the mouse goes "
- "over the key"))]),
- SettingsGroup("fontandsize", _("Font and size"), [
- BooleanSetting(
- "default_font", _("Use system fonts"), True,
- _("Use the default system font for keyboard"),
- insensitive_when_true=["key_font"]),
- FontSetting("key_font", _("Key font"), "Sans 12",
- _("Custom font for keyboard"))
- ])
- ]),
- SettingsGroup("scanning", _("Scanning"), [
- BooleanSetting(
- "scan_enabled", _("Enable scanning"), False,
- _("Enable switch scanning"),
- insensitive_when_false=["scanning_general",
- "scanning_input",
- "scanning_color"]),
- SettingsGroup("scanning_general", _("General"), [
- StringSetting("scanning_type", _("Scanning mode"),
- "block",
- _("Scanning type, block or row"),
- allowed=[("block", _("Block")),
- ("row", _("Row"))]),
- FloatSetting("step_time", _("Step time"), 1.0,
- _("Time between key transitions"),
- min=0.1, max=60.0),
- BooleanSetting("reverse_scanning",
- _("Reverse scanning"), False,
- _("Scan in reverse order"))
- ]),
- SettingsGroup("scanning_input", _("Input"), [
- StringSetting("switch_type", _("Switch device"),
- "keyboard",
- _("Switch device, keyboard or mouse"),
- entry_type=ENTRY_RADIO,
- allowed=[("keyboard", _("Keyboard")),
- ("mouse", _("Mouse"))],
- children=[
- StringSetting("keyboard_key", _("Switch key"),
- "Shift_R",
- _(
- "Key to use with scanning mode"),
- allowed=[
- ("Shift_R", _("Right shift")),
- ("Shift_L", _("Left shift")),
- ("ISO_Level3_Shift", _("Alt Gr")),
- ("Num_Lock", _("Num lock"))]),
- StringSetting("mouse_button", _("Switch button"),
- "2",
- _(
- "Mouse button to use in the scanning "
- "mode"),
- allowed=[("1", _("Button 1")),
- ("2", _("Button 2")),
- ("3", _("Button 3"))])
- ]),
- ]),
- SettingsGroup("scanning_color", _("Color"), [
- ColorSetting("block_scanning_color", _("Block color"),
- "purple", _("Color of block scans")),
- ColorSetting("row_scanning_color", _("Row color"),
- "green", _("Color of row scans")),
- ColorSetting("button_scanning_color", _("Key color"),
- "cyan", _("Color of key scans")),
- ColorSetting("cancel_scanning_color",
- _("Cancel color"),
- "red", _("Color of cancel scan"))
- ])
- ])
- ])
-
-if __name__ == "__main__":
- from gi.repository import GLib
-
- class SchemasMaker:
- def create_schemas(self):
- doc = xml.dom.minidom.Document()
- schemafile = doc.createElement('schemalist')
- schema = doc.createElement('schema')
- schema.setAttribute("id", GSETTINGS_SCHEMA)
- schema.setAttribute("path", "/org/gnome/caribou/osk/")
- schemafile.appendChild(schema)
- self._create_schema(settings, doc, schema)
-
- self._pretty_xml(schemafile)
-
- def _attribs(self, e):
- if not e.attributes.items():
- return ""
- return ' ' + ' '.join(['%s="%s"' % (k,v) \
- for k,v in e.attributes.items()])
-
- def _pretty_xml(self, e, indent=0):
- if not e.childNodes or \
- (len(e.childNodes) == 1 and \
- e.firstChild.nodeType == e.TEXT_NODE):
- print '%s%s' % (' '*indent*2, e.toxml().strip())
- else:
- print '%s<%s%s>' % (' '*indent*2, e.tagName, self._attribs(e))
- for c in e.childNodes:
- self._pretty_xml(c, indent + 1)
- print '%s</%s>' % (' '*indent*2, e.tagName)
-
- def _append_children_element_value_pairs(self, doc, element, pairs):
- for e, t in pairs:
- el = doc.createElement(e)
- te = doc.createTextNode(str(t))
- el.appendChild(te)
- element.appendChild(el)
-
- def _create_schema(self, setting, doc, schemalist):
- if hasattr(setting, 'gsettings_key'):
- key = doc.createElement('key')
- key.setAttribute('name', setting.gsettings_key)
- key.setAttribute('type', setting.variant_type)
- schemalist.appendChild(key)
- self._append_children_element_value_pairs(
- doc, key, [('default',
- getattr(setting.gvariant, "print")(False)),
- ('_summary', setting.short_desc),
- ('_description', setting.long_desc)])
-
- for s in setting:
- self._create_schema(s, doc, schemalist)
-
- maker = SchemasMaker()
- maker.create_schemas()
diff --git a/caribou/daemon/Makefile.am b/caribou/daemon/Makefile.am
new file mode 100644
index 0000000..859c29e
--- /dev/null
+++ b/caribou/daemon/Makefile.am
@@ -0,0 +1,8 @@
+caribou_daemondir = $(pkgpythondir)/daemon/
+
+caribou_daemon_PYTHON = \
+ __init__.py \
+ main.py
+
+clean-local:
+ rm -rf *.pyc *.pyo
diff --git a/caribou/daemon/__init__.py b/caribou/daemon/__init__.py
new file mode 100644
index 0000000..fd9812d
--- /dev/null
+++ b/caribou/daemon/__init__.py
@@ -0,0 +1 @@
+from main import CaribouDaemon
diff --git a/caribou/daemon/main.py b/caribou/daemon/main.py
new file mode 100644
index 0000000..b9df5cf
--- /dev/null
+++ b/caribou/daemon/main.py
@@ -0,0 +1,150 @@
+import pyatspi
+import dbus
+from gi.repository import Gio
+from string import Template
+
+from caribou.i18n import _
+from caribou import APP_NAME
+
+debug = False
+
+class CaribouDaemon:
+ def __init__(self, keyboard_name="Antler"):
+ if not self._get_a11y_enabled():
+ self._show_no_a11y_dialogs()
+ bus = dbus.SessionBus()
+ try:
+ dbus_obj = bus.get_object("org.gnome.Caribou.%s" % keyboard_name,
+ "/org/gnome/Caribou/%s" % keyboard_name)
+ except dbus.DBusException:
+ print "%s is not running, and is not provided by any .service file" % \
+ keyboard_name
+ return
+ self.keyboard_proxy = dbus.Interface(dbus_obj, "org.gnome.Caribou.Keyboard")
+ self._current_acc = None
+ self._register_event_listeners()
+
+ def _show_no_a11y_dialogs(self):
+ from gi.repository import Gtk
+ msgdialog = Gtk.MessageDialog(None,
+ Gtk.DialogFlags.MODAL,
+ Gtk.MessageType.QUESTION,
+ Gtk.ButtonsType.YES_NO,
+ _("In order to use %s, accessibility needs "
+ "to be enabled. Do you want to enable "
+ "it now?") % APP_NAME)
+ resp = msgdialog.run()
+ if resp == Gtk.ResponseType.NO:
+ msgdialog.destroy()
+ quit()
+ if resp == Gtk.ResponseType.YES:
+ settings = Gio.Settings('org.gnome.desktop.interface')
+ atspi = settings.set_boolean("toolkit-accessibility", True)
+ msgdialog2 = Gtk.MessageDialog(msgdialog,
+ Gtk.DialogFlags.MODAL,
+ Gtk.MessageType.INFO,
+ Gtk.ButtonsType.OK,
+ _("Accessibility has been enabled. "
+ "Log out and back in again to use "
+ "%s." % APP_NAME))
+ msgdialog2.run()
+ msgdialog2.destroy()
+ msgdialog.destroy()
+ quit()
+
+
+ def _register_event_listeners(self):
+ pyatspi.Registry.registerEventListener(
+ self.on_focus, "object:state-changed:focused")
+ pyatspi.Registry.registerEventListener(self.on_focus, "focus")
+ pyatspi.Registry.registerEventListener(
+ self.on_text_caret_moved, "object:text-caret-moved")
+
+ def _deregister_event_listeners(self):
+ pyatspi.Registry.deregisterEventListener(
+ self.on_focus, "object:state-changed:focused")
+ pyatspi.Registry.deregisterEventListener(self.on_focus, "focus")
+ pyatspi.Registry.deregisterEventListener(
+ self.on_text_caret_moved, "object:text-caret-moved")
+
+ def _get_a11y_enabled(self):
+ try:
+ try:
+ settings = Gio.Settings('org.gnome.desktop.interface')
+ atspi = settings.get_boolean("toolkit-accessibility")
+ return atspi
+ except:
+ raise
+ from gi.repository import GConf
+ gconfc = GConf.Client.get_default()
+ atspi1 = gconfc.get_bool(
+ "/desktop/gnome/interface/accessibility")
+ atspi2 = gconfc.get_bool(
+ "/desktop/gnome/interface/accessibility2")
+ return atspi1 or atspi2
+ except:
+ raise
+ return False
+
+ def on_text_caret_moved(self, event):
+ if self._current_acc == event.source:
+ text = self._current_acc.queryText()
+ x, y, w, h = text.getCharacterExtents(text.caretOffset,
+ pyatspi.DESKTOP_COORDS)
+ if (x, y, w, h) == (0, 0, 0, 0):
+ component = self._current_acc.queryComponent()
+ bb = component.getExtents(pyatspi.DESKTOP_COORDS)
+ x, y, w, h = bb.x, bb.y, bb.width, bb.height
+
+ self.keyboard_proxy.SetCursorLocation(x, y, w, h)
+ if debug == True:
+ print "object:text-caret-moved in", event.host_application.name,
+ print event.detail1, event.source.description
+
+ def _set_entry_location(self, acc):
+ text = acc.queryText()
+ bx, by, bw, bh = text.getCharacterExtents(text.caretOffset,
+ pyatspi.DESKTOP_COORDS)
+
+ component = acc.queryComponent()
+ entry_bb = component.getExtents(pyatspi.DESKTOP_COORDS)
+
+ if (bx, by, bw, bh) == (0, 0, 0, 0):
+ bx, by, bw, bh = entry_bb.x, entry_bb.y, entry_bb.width, entry_bb.height
+
+ self.keyboard_proxy.SetCursorLocation(bx, by, bw, bh)
+
+ self.keyboard_proxy.SetEntryLocation(entry_bb.x, entry_bb.y,
+ entry_bb.width, entry_bb.height)
+
+ self.keyboard_proxy.Show()
+
+ def on_focus(self, event):
+ acc = event.source
+ source_role = acc.getRole()
+ if acc.getState().contains(pyatspi.STATE_EDITABLE) or \
+ source_role == pyatspi.ROLE_TERMINAL:
+ if source_role in (pyatspi.ROLE_TEXT,
+ pyatspi.ROLE_PARAGRAPH,
+ pyatspi.ROLE_PASSWORD_TEXT,
+ pyatspi.ROLE_TERMINAL,
+ pyatspi.ROLE_ENTRY):
+ if event.type.startswith("focus") or event.detail1 == 1:
+ self._set_entry_location(acc)
+ self._current_acc = event.source
+ if debug == True:
+ print "enter text widget in", event.host_application.name
+ elif event.detail1 == 0 and acc == self._current_acc:
+ self.keyboard_proxy.Hide()
+ self._current_acc = None
+ if debug == True:
+ print "leave text widget in", event.host_application.name
+ else:
+ if debug == True:
+ print _("WARNING - Caribou: unhandled editable widget:"), \
+ event.source
+
+ def clean_exit(self):
+ self._deregister_event_listeners()
+
+
diff --git a/caribou/ui/i18n.py.in b/caribou/i18n.py.in
index bf7ad0f..bf7ad0f 100644
--- a/caribou/ui/i18n.py.in
+++ b/caribou/i18n.py.in
diff --git a/caribou/settings/Makefile.am b/caribou/settings/Makefile.am
new file mode 100644
index 0000000..468adab
--- /dev/null
+++ b/caribou/settings/Makefile.am
@@ -0,0 +1,11 @@
+caribou_settingsdir = $(pkgpythondir)/settings/
+
+caribou_settings_PYTHON = \
+ __init__.py \
+ preferences_window.py \
+ settings_manager.py \
+ caribou_settings.py \
+ setting_types.py
+
+clean-local:
+ rm -rf *.pyc *.pyo
diff --git a/caribou/settings/__init__.py b/caribou/settings/__init__.py
new file mode 100644
index 0000000..0ec8b40
--- /dev/null
+++ b/caribou/settings/__init__.py
@@ -0,0 +1,5 @@
+GSETTINGS_SCHEMA = "org.gnome.caribou"
+
+from caribou_settings import CaribouSettings
+
+AllSettings = [CaribouSettings]
diff --git a/caribou/settings/caribou_settings.py b/caribou/settings/caribou_settings.py
new file mode 100644
index 0000000..638e1c4
--- /dev/null
+++ b/caribou/settings/caribou_settings.py
@@ -0,0 +1,64 @@
+from caribou.settings.setting_types import *
+from caribou.i18n import _
+
+CaribouSettings = SettingsTopGroup(
+ _("Caribou Preferences"), "/org/gnome/caribou/", "org.gnome.caribou",
+ [SettingsGroup("keyboard", _("Keyboard"), [
+ SettingsGroup("general", _("General"), [
+ StringSetting(
+ "keyboard_type", _("Keyboard Type"), "touch",
+ _("The keyboard geometery Caribou should use"),
+ _("The keyboard geometery determines the shape "
+ "and complexity of the keyboard, it could range from "
+ "a 'natural' look and feel good for composing simple "
+ "text, to a fullscale keyboard."),
+ allowed=[(('touch'), _('Touch'))])]),
+ ]),
+ SettingsGroup("scanning", _("Scanning"), [
+ BooleanSetting(
+ "scan_enabled", _("Enable scanning"), False,
+ _("Enable switch scanning"),
+ insensitive_when_false=["scanning_general",
+ "scanning_input"]),
+ SettingsGroup("scanning_general", _("General"), [
+ StringSetting("scanning_type", _("Scanning mode"),
+ "block",
+ _("Scanning type, block or row"),
+ allowed=[("block", _("Block")),
+ ("row", _("Row"))]),
+ FloatSetting("step_time", _("Step time"), 1.0,
+ _("Time between key transitions"),
+ min=0.1, max=60.0),
+ BooleanSetting("reverse_scanning",
+ _("Reverse scanning"), False,
+ _("Scan in reverse order"))
+ ]),
+ SettingsGroup("scanning_input", _("Input"), [
+ StringSetting("switch_type", _("Switch device"),
+ "keyboard",
+ _("Switch device, keyboard or mouse"),
+ entry_type=ENTRY_RADIO,
+ allowed=[("keyboard", _("Keyboard")),
+ ("mouse", _("Mouse"))],
+ children=[
+ StringSetting("keyboard_key", _("Switch key"),
+ "Shift_R",
+ _(
+ "Key to use with scanning mode"),
+ allowed=[
+ ("Shift_R", _("Right shift")),
+ ("Shift_L", _("Left shift")),
+ ("ISO_Level3_Shift", _("Alt Gr")),
+ ("Num_Lock", _("Num lock"))]),
+ StringSetting("mouse_button", _("Switch button"),
+ "2",
+ _(
+ "Mouse button to use in the scanning "
+ "mode"),
+ allowed=[("1", _("Button 1")),
+ ("2", _("Button 2")),
+ ("3", _("Button 3"))])
+ ]),
+ ]),
+ ])
+ ])
diff --git a/caribou/ui/preferences_window.py b/caribou/settings/preferences_window.py
index 0158ffb..58d1b4c 100644
--- a/caribou/ui/preferences_window.py
+++ b/caribou/settings/preferences_window.py
@@ -18,47 +18,24 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-import caribou.common.const as const
-from caribou.common.setting_types import *
-from caribou.common.settings_manager import SettingsManager
+from caribou.settings.setting_types import *
-from gi.repository import GConf
import gobject
from gi.repository import Gdk
from gi.repository import Gtk
-from gi.repository import Pango
-import sys
-import virtkey
-import os
-import traceback
-from i18n import _
-try:
- import json
-except ImportError:
- HAS_JSON = False
-else:
- HAS_JSON = True
-import xml.etree.ElementTree as ET
-from xml.dom import minidom
-import gettext
-import i18n
-
-class PreferencesWindow(Gtk.Dialog):
- __gtype_name__ = "PreferencesWindow"
-
- def __init__(self):
- gobject.GObject.__init__(self)
- self.set_title(_("Caribou Preferences"))
- self.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE)
- self.set_border_width(6)
+class AbstractPreferencesUI:
+ def populate_settings(self, groups):
notebook = Gtk.Notebook()
- vbox = self.get_content_area()
- vbox.add(notebook)
- self._populate_settings(notebook, SettingsManager.groups)
+ self._populate_settings(notebook, groups)
+ if notebook.get_n_pages() == 1:
+ notebook.set_show_tabs(False)
+
+ return notebook
def _populate_settings(self, parent, setting, level=0):
if level == 0:
+ self.set_title(setting.label)
for s in setting:
vbox = Gtk.VBox()
parent.append_page(vbox, Gtk.Label(label=s.label))
@@ -248,11 +225,38 @@ class PreferencesWindow(Gtk.Dialog):
self._update_setting(setting, combo.get_active_id(),
handler_id)
+class PreferencesDialog(Gtk.Dialog, AbstractPreferencesUI):
+ __gtype_name__ = "PreferencesDialog"
+
+ def __init__(self, settings_manager):
+ gobject.GObject.__init__(self)
+ self.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE)
+ self.set_border_width(6)
+
+ notebook = self.populate_settings(settings_manager.groups)
+ vbox = self.get_content_area()
+ vbox.add(notebook)
+
+class PreferencesWindow(Gtk.Window, AbstractPreferencesUI):
+ __gtype_name__ = "PreferencesWindow"
+
+ def __init__(self, settings_manager):
+ gobject.GObject.__init__(self)
+ self.set_border_width(6)
+
+ notebook = self.populate_settings(settings_manager.groups)
+ self.add(notebook)
+
if __name__ == "__main__":
+ from caribou.settings.settings_manager import SettingsManager
+ from caribou.settings import CaribouSettings
+
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
- w = PreferencesWindow()
+
+ w = PreferencesDialog(CaribouSettings())
w.show_all()
+
try:
w.run()
except KeyboardInterrupt:
diff --git a/caribou/common/setting_types.py b/caribou/settings/setting_types.py
index 109bf1b..b0e374d 100644
--- a/caribou/common/setting_types.py
+++ b/caribou/settings/setting_types.py
@@ -53,6 +53,16 @@ class Setting(gobject.GObject):
class SettingsGroup(Setting):
pass
+class SettingsTopGroup(SettingsGroup):
+ def __init__(self, label, path, schema_id, children=[]):
+ SettingsGroup.__init__(self, "_top", label, children)
+ self.path = path
+ self.schema_id = schema_id
+
+ def __call__(self):
+ from caribou.settings.settings_manager import SettingsManager
+ return SettingsManager(self)
+
class ValueSetting(Setting):
variant_type = ''
entry_type=ENTRY_DEFAULT
diff --git a/caribou/common/settings_manager.py b/caribou/settings/settings_manager.py
index 66119f8..1368f3a 100644
--- a/caribou/common/settings_manager.py
+++ b/caribou/settings/settings_manager.py
@@ -1,10 +1,9 @@
import os
from gi.repository import Gio
-from setting_types import *
-from settings import settings, GSETTINGS_SCHEMA
-import const
+from caribou.settings.setting_types import *
+from caribou.settings import GSETTINGS_SCHEMA
-class _SettingsManager(object):
+class SettingsManager(object):
def __init__(self, settings):
self.groups = settings
self._gsettings = Gio.Settings(GSETTINGS_SCHEMA)
@@ -66,5 +65,3 @@ class _SettingsManager(object):
def __call__(self):
return self
-
-SettingsManager = _SettingsManager(settings)
diff --git a/caribou/ui/Makefile.am b/caribou/ui/Makefile.am
deleted file mode 100644
index fb24db4..0000000
--- a/caribou/ui/Makefile.am
+++ /dev/null
@@ -1,16 +0,0 @@
-caribou_uidir = $(pkgpythondir)/ui/
-
-caribou_ui_PYTHON = \
- __init__.py \
- i18n.py \
- keyboard.py \
- scan.py \
- main.py \
- opacity.py \
- preferences_window.py \
- window.py
-
-DISTCLEANFILES = i18n.py
-
-clean-local:
- rm -rf *.pyc *.pyo
diff --git a/caribou/ui/__init__.py b/caribou/ui/__init__.py
deleted file mode 100644
index 8b13789..0000000
--- a/caribou/ui/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/caribou/ui/keyboard.py b/caribou/ui/keyboard.py
deleted file mode 100644
index 60c9ae1..0000000
--- a/caribou/ui/keyboard.py
+++ /dev/null
@@ -1,535 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Caribou - text entry and UI navigation application
-#
-# Copyright (C) 2009 Adaptive Technology Resource Centre
-# * Contributor: Ben Konrath <ben@bagu.org>
-# Copyright (C) 2009 Eitan Isaacson <eitan@monotonous.org>
-# Copyright (C) 2010 Igalia S.L.
-# * Contributor: Joaquim Rocha <jrocha@igalia.com>
-#
-# This program 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 program 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 program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-import caribou.common.const as const
-from caribou.common.settings_manager import SettingsManager
-from preferences_window import PreferencesWindow
-from caribou import data_path
-from gi.repository import GConf
-import gobject
-from gi.repository import Gdk
-from gi.repository import Gtk
-from gi.repository import Pango
-from gi.repository import Caribou
-import sys
-import os
-import traceback
-from caribou.ui.i18n import _
-from caribou.common.setting_types import *
-
-try:
- import json
-except ImportError:
- HAS_JSON = False
-else:
- HAS_JSON = True
-import xml.etree.ElementTree as ET
-from xml.dom import minidom
-import gettext
-import i18n
-
-PRETTY_LABELS = {
- "BackSpace" : u'\u232b',
- "space" : u' ',
- "Return" : u'\u23ce',
- 'Caribou_Prefs' : u'\u2328',
- 'Caribou_ShiftUp' : u'\u2b06',
- 'Caribou_ShiftDown' : u'\u2b07',
- 'Caribou_Emoticons' : u'\u263a',
- 'Caribou_Symbols' : u'123',
- 'Caribou_Symbols_More' : u'{#*',
- 'Caribou_Alpha' : u'Abc'
-}
-
-class BaseKey(object):
- '''An abstract class the represents a key on the keyboard.
- Inheriting classes also need to inherit from Gtk.Button or any
- of it's subclasses.'''
-
- def __init__(self, **kwargs):
- if not kwargs.has_key("name"):
- raise TypeError, "%r requires a 'name' parameter" % self.__class__
- self.margin_left = 0
- self.width = 1
-
- for k, v in kwargs.items():
- setattr(self, k, v)
- if hasattr(self, "extended_names"):
- self.extended_keys = \
- [self.__class__(name=n) for n in [self.name] + self.extended_names]
- else:
- self.extended_keys = []
-
- self.keyval, self.key_label = self._get_keyval_and_label(self.name)
- self.set_label(self.key_label)
-
- ctx = self.get_style_context()
- ctx.add_class("caribou-keyboard-button")
-
- for name in ["default_font", "key_font"]:
- getattr(SettingsManager, name).connect("value-changed",
- self._key_font_changed)
-
- if not SettingsManager.default_font.value:
- self._key_font_changed(None, None)
-
- def _get_keyval_and_label(self, name):
- keyval = Gdk.keyval_from_name(name)
- if PRETTY_LABELS.has_key(name):
- label = PRETTY_LABELS[name]
- elif name.startswith('Caribou_'):
- label = name.replace('Caribou_', '')
- else:
- unichar = unichr(Gdk.keyval_to_unicode(keyval))
- if unichar.isspace() or unichar == u'\x00':
- label = name
- else:
- label = unichar
-
- return keyval, label
-
- def _key_font_changed(self, setting, value):
- if SettingsManager.default_font.value:
- self.reset_font()
- else:
- self.set_font(SettingsManager.key_font.value)
-
- def scan_highlight_key(self):
- raise NotImplemented
-
- def scan_highlight_row(self):
- raise NotImplemented
-
- def scan_highlight_block(self):
- raise NotImplemented
-
- def scan_highlight_cancel(self):
- raise NotImplemented
-
- def scan_highlight_clear(self):
- raise NotImplemented
-
- def set_font(self, font):
- raise NotImplemented
-
- def reset_font(self):
- raise NotImplemented
-
-class CaribouSubKeys(Gtk.Window):
- def __init__(self, keys):
- gobject.GObject.__init__(self, type=Gtk.WindowType.POPUP)
- self.set_decorated(False)
- self.set_resizable(False)
- self.set_accept_focus(False)
- self.set_position(Gtk.WindowPosition.MOUSE)
- self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
-
- for key in keys:
- key.connect("clicked", self._on_key_clicked)
-
- layout = KeyboardLayout(key.name, "subkeyboard")
- layout.add_row(keys)
- self.add(layout)
-
- def show_subkeys(self, parent):
- self.set_transient_for(parent)
- self._parent = parent
- self._parent.set_sensitive(False)
- self.show_all()
-
- def _on_key_clicked(self, key):
- self._parent.set_sensitive(True)
- self.hide()
-
-class Key(Gtk.Button, BaseKey):
- def __init__(self, **kwargs):
- gobject.GObject.__init__(self)
- BaseKey.__init__(self, **kwargs)
- if self.extended_keys:
- self.sub_keys = CaribouSubKeys(self.extended_keys)
- else:
- self.sub_keys = None
-
- child = self.get_child()
- child.set_padding(4, 4)
-
- def do_get_preferred_width_for_height(self, w):
- return (w, w)
-
- def do_get_request_mode(self):
- return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH
-
- def set_font(self, font):
- child = self.get_child()
- if isinstance(child, Gtk.Label):
- child.modify_font(Pango.font_description_from_string(font))
- if child is not None:
- child.queue_resize()
-
- def reset_font(self):
- label = self.get_child()
- if not isinstance(label, Gtk.Label):
- return
- label.modify_font(None)
-
- def _replace_scan_class_style(self, scan_class=None):
- ctx = self.get_style_context()
- for cls in ctx.list_classes():
- if cls.startswith('caribou-scan'):
- ctx.remove_class(cls)
- if scan_class:
- ctx.add_class(scan_class)
- self.queue_draw()
-
- def scan_highlight_key(self):
- self._replace_scan_class_style("caribou-scan-key")
-
- def scan_highlight_row(self):
- self._replace_scan_class_style("caribou-scan-row")
-
- def scan_highlight_block(self):
- self._replace_scan_class_style("caribou-scan-block")
-
- def scan_highlight_cancel(self):
- self._replace_scan_class_style("caribou-scan-cancel")
-
- def scan_highlight_clear(self):
- self._replace_scan_class_style()
-
-class ModifierKey(Gtk.ToggleButton, Key):
- pass
-
-class KeyboardLayout(Gtk.Grid):
- KEY_SPAN = 4
-
- def __init__(self, name, mode):
- gobject.GObject.__init__(self)
- self.layout_name = name
- self.mode = mode
- self.rows = []
- self.set_column_homogeneous(True)
- self.set_row_homogeneous(True)
- self.set_row_spacing(4)
- self.set_column_spacing(4)
-
- def add_row(self, row):
- row_num = len(self.rows)
- self.rows.append(row)
- col_num = 0
- for i, key in enumerate(row):
- self.attach(key,
- col_num + int(key.margin_left * self.KEY_SPAN),
- row_num * self.KEY_SPAN,
- int(self.KEY_SPAN * key.width),
- self.KEY_SPAN)
- col_num += int((key.width + key.margin_left ) * self.KEY_SPAN)
-
- def get_scan_rows(self):
- return [filter(lambda x: x.is_sensitive(), row) for row in self.rows]
-
- def get_scan_blocks(self, optimal_block_size=8):
- # TODO: smarter division using optimal block size.
- scan_rows = self.get_scan_rows()
- col_num = max([len(row) for row in scan_rows])
- blocks = []
-
- for row_index in xrange(len(scan_rows)):
- for col_index in xrange(max([len(row) for row in scan_rows])):
- try:
- key = scan_rows[row_index][col_index]
- except IndexError:
- continue
-
- try:
- group = blocks[row_index/2]
- except IndexError:
- group = []
- blocks.append(group)
-
- try:
- group[col_index/3].append(key)
- except IndexError:
- block = []
- block.append(key)
- group.append(block)
-
- return reduce(lambda a, b: a + b, blocks)
-
-class KbLayoutDeserializer(object):
- def _get_layout_file(self, group, variant):
- layout_path = os.path.join(data_path, "layouts",
- SettingsManager.geometry.value)
- for fn in ('%s_%s.json' % (group, variant),
- '%s.json' % group):
- layout_file = os.path.join(layout_path, fn)
- if os.path.exists(layout_file):
- return layout_file
- return None
-
- def deserialize(self, group, variant):
- kb_file = self._get_layout_file(group, variant)
- if not kb_file:
- return []
- kb_file_obj = open(kb_file)
- contents = kb_file_obj.read()
- kb_file_obj.close()
- basename, ext = os.path.splitext(kb_file)
- try:
- kb_layouts = self._deserialize_from_format(ext, contents)
- except Exception, e:
- traceback.print_exc()
- else:
- return kb_layouts
- return []
-
- def _deserialize_from_format(self, format, contents):
- if format == '.xml':
- return self._deserialize_from_xml(contents)
- if HAS_JSON and format == '.json':
- return self._deserialize_from_json(contents)
- return []
-
- def _deserialize_from_json(self, contents):
- contents_dict = json.loads(contents)
- layouts = self._create_kb_layout_from_dict(contents_dict)
- return layouts
-
- def _convert_xml_to_dict(self, element):
- if element.text and element.text.strip():
- return element.text
- attributes = element.attrib
- for child in element.getchildren():
- if attributes.get(child.tag):
- attributes[child.tag] += [self._convert_xml_to_dict(child)]
- else:
- attributes[child.tag] = [self._convert_xml_to_dict(child)]
- for key, value in attributes.items():
- if isinstance(value, list) and len(value) == 1:
- attributes[key] = value[0]
- return attributes
-
- def _deserialize_from_xml(self, xml_string):
- element = ET.fromstring(xml_string)
- layout_dict = self._convert_xml_to_dict(element)
- return self._create_kb_layout_from_dict(layout_dict)
-
- def _create_kb_layout_from_dict(self, dictionary):
- if not isinstance(dictionary, dict):
- return None
- layouts_encoded = []
- for name, level in dictionary.items():
- kb_layout = KeyboardLayout(name, level.get("mode", "locked"))
- rows_list = self._get_dict_value_as_list(level, 'rows')
- for row in rows_list:
- keys = self._get_keys_from_list(row)
- kb_layout.add_row(keys)
- layouts_encoded.append(kb_layout)
- return layouts_encoded
-
- def _get_keys_from_list(self, keys_list):
- keys = []
- for key_vars in keys_list:
- vars = {}
- for key, value in key_vars.items():
- vars[str(key)] = value
- if vars.get('modifier', False) == const.MASK_KEY_TYPE:
- key = ModifierKey(self.vk, **vars)
- else:
- key = Key(**vars)
- keys.append(key)
- return keys
-
- def _get_dict_value_as_list(self, dictionary, key):
- if isinstance(dictionary, list):
- return dictionary
- value = dictionary.get(key)
- if not value:
- return None
- if isinstance(value, list):
- return value
- return [value]
-
-class CaribouKeyboard(Gtk.Notebook):
- __gtype_name__ = "CaribouKeyboard"
-
- def __init__(self):
- gobject.GObject.__init__(self)
- self.set_show_tabs(False)
- self.vk = Caribou.VirtualKeyboard()
- self.key_size = 30
- self.current_page = 0
- self.depressed_mods = []
- self.layouts = {}
- self._load_kb()
- self.vk.connect('group-changed', self._on_group_changed)
- grpid, group, variant = self.vk.get_current_group()
- self._on_group_changed(self.vk, grpid, group, variant)
- self._key_hold_tid = 0
-
- def _load_kb(self):
- kb_deserializer = KbLayoutDeserializer()
- groups, variants = self.vk.get_groups()
- for group, variant in zip(groups, variants):
- levels = kb_deserializer.deserialize(group, variant)
- self._add_levels('%s_%s' % (group, variant), levels)
-
- def _connect_key_signals(self, key):
- if hasattr(key, "toggle"):
- key.connect('clicked',
- self._pressed_layout_switcher_key)
- elif key.name == "Caribou_Prefs":
- key.connect('clicked', self._pressed_preferences_key)
- elif key.keyval != 0:
- if False: # We should enable this for hardware emulation
- key.connect('pressed',
- self._pressed_normal_key)
- key.connect('released',
- self._released_normal_key)
- else:
- key.connect('clicked',
- self._clicked_normal_key)
- key.connect('pressed',
- self._key_hold_start)
- key.connect('released',
- self._key_hold_end)
-
-
- def _add_levels(self, group, level_list):
- self.layouts[group] = {}
- for level in level_list:
- level.show()
- self.layouts[group][level.layout_name] = self.append_page(level, None)
- if level.mode == "default":
- self.layouts[group]["default"] = \
- self.layouts[group][level.layout_name]
- for row in level.rows:
- for key in row:
- self._connect_key_signals(key)
- for k in key.extended_keys:
- self._connect_key_signals(k)
-
- def _clear(self):
- n_pages = self.get_n_pages()
- for i in range(n_pages):
- self.remove_page(i)
-
- def _key_hold_start(self, key):
- self._key_hold_tid = gobject.timeout_add(1000, self._on_key_held, key)
-
- def _key_hold_end(self, key):
- if self._key_hold_tid != 0:
- gobject.source_remove(self._key_hold_tid)
- self._key_hold_tid = 0
-
- def _on_key_held(self, key):
- self._key_hold_tid = 0
- if key.sub_keys:
- key.sub_keys.show_subkeys(self.get_toplevel())
- return False
-
- def _clicked_normal_key(self, key):
- if self._key_hold_tid == 0:
- return
- self._pressed_normal_key(key)
- self._released_normal_key(key)
-
- def _pressed_normal_key(self, key):
- self.vk.keyval_press(key.keyval)
-
- def _released_normal_key(self, key):
- self.vk.keyval_release(key.keyval)
- layout = self.get_nth_page(self.get_current_page())
- if layout.mode == "latched":
- self._switch_to_layout()
- while True:
- try:
- mod = self.depressed_mods.pop()
- except IndexError:
- break
- mod.set_active (False)
-
- def _pressed_layout_switcher_key(self, key):
- self._switch_to_layout(level=key.toggle)
-
- def _on_group_changed(self, vk, groupid, group, variant):
- self._switch_to_layout('%s_%s' % (group, variant))
-
- def _toggled_mask_key(self, key):
- if key.get_active():
- self.vk.keyval_press(key.value)
- self.depressed_mods.append(key)
- else:
- self.vk.keyval_release(key.value)
- try:
- mod = self.depressed_mods.remove(key)
- except ValueError:
- pass
-
- def show_all_(self):
- self.set_current_page(self.current_page)
- Gtk.Notebook.show_all(self)
-
- def _pressed_preferences_key(self, key):
- p = PreferencesWindow()
- p.show_all()
- p.run()
- p.destroy()
-
- def _switch_to_fallback(self, group):
- try:
- i = min(self.layouts[group].values())
- except KeyError:
- i = 0
- self.set_current_page(i)
-
- def _switch_to_layout(self, group=None, level="default"):
- if group is None:
- _, _group, _variant = self.vk.get_current_group()
- group = '%s_%s' % (_group, _variant)
- if self.layouts.has_key(group):
- if self.layouts[group].has_key(level):
- self.set_current_page(self.layouts[group][level])
- return
- self._switch_to_fallback(group)
-
- def get_current_layout(self):
- i = self.get_current_page()
- return self.get_nth_page(i)
-
- def get_layouts(self):
- return [self.get_nth_page(i) for i in xrange(self.get_n_pages())]
-
-if __name__ == "__main__":
- import signal
- signal.signal(signal.SIGINT, signal.SIG_DFL)
-
- w = Gtk.Window()
- w.set_accept_focus(False)
-
- kb = CaribouKeyboard()
- w.add(kb)
-
- w.show_all()
-
- Gtk.main()
diff --git a/caribou/ui/main.py b/caribou/ui/main.py
deleted file mode 100644
index de2302c..0000000
--- a/caribou/ui/main.py
+++ /dev/null
@@ -1,275 +0,0 @@
-import pyatspi
-from gi.repository import Gtk
-from gi.repository import Gdk
-from gi.repository import Gio
-from string import Template
-
-from window import CaribouWindowEntry, Rectangle
-from keyboard import CaribouKeyboard
-from caribou.common.settings_manager import SettingsManager
-from caribou.ui.i18n import _
-import caribou.common.const as const
-from scan import ScanMaster
-
-debug = False
-
-CSS_TEMPLATE = """
-.caribou-keyboard-button {
-background-image: none;
-background-color: $normal_bg;
-}
-
-.caribou-keyboard-button:hover {
-background-image: none;
-background-color: $mouseover_bg;
-}
-"""
-
-SCAN_CSS_TEMPLATE = """
-.caribou-scan-key {
-background-image: none;
-background-color: $button_scan;
-}
-
-.caribou-scan-row {
-background-image: none;
-background-color: $row_scan;
-}
-
-.caribou-scan-block {
-background-image: none;
-background-color: $block_scan;
-}
-
-.caribou-scan-cancel {
-background-image: none;
-background-color: $cancel_scan;
-}
-"""
-
-class Caribou:
- def __init__(self,
- kb_factory=CaribouKeyboard,
- window_factory=CaribouWindowEntry):
- if not self._get_a11y_enabled():
- msgdialog = Gtk.MessageDialog(None,
- Gtk.DialogFlags.MODAL,
- Gtk.MessageType.QUESTION,
- Gtk.ButtonsType.YES_NO,
- _("In order to use %s, accessibility needs "
- "to be enabled. Do you want to enable "
- "it now?") % const.APP_NAME)
- resp = msgdialog.run()
- if resp == Gtk.ResponseType.NO:
- msgdialog.destroy()
- quit()
- if resp == Gtk.ResponseType.YES:
- settings = Gio.Settings('org.gnome.desktop.interface')
- atspi = settings.set_boolean("toolkit-accessibility", True)
- msgdialog2 = Gtk.MessageDialog(msgdialog,
- Gtk.DialogFlags.MODAL,
- Gtk.MessageType.INFO,
- Gtk.ButtonsType.OK,
- _("Accessibility has been enabled. "
- "Log out and back in again to use "
- "%s.") % const.APP_NAME)
- msgdialog2.run()
- msgdialog2.destroy()
- msgdialog.destroy()
- quit()
- self.__current_acc = None
- self.window_factory = window_factory
- self.kb_factory = kb_factory
- kb = kb_factory()
- self.window = window_factory(kb)
- self._register_event_listeners()
- SettingsManager.geometry.connect("value-changed",
- self._on_geometry_changed)
-
- # Scanning
- self.scan_master = ScanMaster(self.window, kb)
- SettingsManager.scan_enabled.connect("value-changed",
- self._on_scan_toggled)
- if SettingsManager.scan_enabled.value:
- self.scan_master.start()
-
- self._custom_css_provider = Gtk.CssProvider()
-
- for name in ["normal_color", "mouse_over_color", "default_colors"]:
- getattr(SettingsManager, name).connect("value-changed",
- self._colors_changed)
- self._colors_changed(None, None)
-
- self._scan_css_provider = Gtk.CssProvider()
- Gtk.StyleContext.add_provider_for_screen(
- Gdk.Screen.get_default(),
- self._scan_css_provider,
- Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
-
- for name in ["button_scanning_color",
- "row_scanning_color",
- "block_scanning_color",
- "cancel_scanning_color"]:
- getattr(SettingsManager, name).connect("value-changed",
- self._scan_colors_changed)
- self._scan_colors_changed(None, None)
-
- def _colors_changed(self, setting, value):
- if SettingsManager.default_colors.value:
- Gtk.StyleContext.remove_provider_for_screen(
- Gdk.Screen.get_default(),
- self._custom_css_provider)
- else:
- Gtk.StyleContext.add_provider_for_screen(
- Gdk.Screen.get_default(),
- self._custom_css_provider,
- Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
- self._custom_css_provider.load_from_data(
- Template(CSS_TEMPLATE).substitute(
- normal_bg=SettingsManager.normal_color.value,
- mouseover_bg=SettingsManager.mouse_over_color.value), -1)
-
- def _scan_colors_changed(self, setting, value):
- self._scan_css_provider.load_from_data(Template(SCAN_CSS_TEMPLATE).substitute(
- button_scan=SettingsManager.button_scanning_color.value,
- row_scan=SettingsManager.row_scanning_color.value,
- block_scan=SettingsManager.block_scanning_color.value,
- cancel_scan=SettingsManager.cancel_scanning_color.value), -1)
-
-
- def _register_event_listeners(self):
- pyatspi.Registry.registerEventListener(
- self.on_focus, "object:state-changed:focused")
- pyatspi.Registry.registerEventListener(self.on_focus, "focus")
- pyatspi.Registry.registerEventListener(
- self.on_text_caret_moved, "object:text-caret-moved")
-
- def _deregister_event_listeners(self):
- pyatspi.Registry.deregisterEventListener(
- self.on_focus, "object:state-changed:focused")
- pyatspi.Registry.deregisterEventListener(self.on_focus, "focus")
- pyatspi.Registry.deregisterEventListener(
- self.on_text_caret_moved, "object:text-caret-moved")
-
- def _on_scan_toggled(self, setting, val):
- if val:
- self.scan_master.start()
- else:
- self.scan_master.stop()
-
- def _on_geometry_changed(self, setting, val):
- self._deregister_event_listeners()
- self.window.destroy()
- self._update_window()
- self._register_event_listeners()
-
- def _update_window(self):
- kb = self.kb_factory()
- self.scan_master.set_keyboard(kb)
- self.window = self.window_factory(kb)
-
- def _get_a11y_enabled(self):
- try:
- try:
- settings = Gio.Settings('org.gnome.desktop.interface')
- atspi = settings.get_boolean("toolkit-accessibility")
- print "->", atspi
- return atspi
- except:
- raise
- from gi.repository import GConf
- gconfc = GConf.Client.get_default()
- atspi1 = gconfc.get_bool(
- "/desktop/gnome/interface/accessibility")
- atspi2 = gconfc.get_bool(
- "/desktop/gnome/interface/accessibility2")
- return atspi1 or atspi2
- except:
- raise
- return False
-
- def on_text_caret_moved(self, event):
- if self.__current_acc == event.source:
- self.__set_location(event.source)
- if debug == True:
- print "object:text-caret-moved in", event.host_application.name,
- print event.detail1, event.source.description
-
- def __set_text_location(self, acc):
- text = acc.queryText()
- [x, y, width, height] = text.getCharacterExtents(text.caretOffset, pyatspi.DESKTOP_COORDS)
- self.window.set_cursor_location(Rectangle(x, y, width, height))
-
- component = acc.queryComponent()
- entry_bb = component.getExtents(pyatspi.DESKTOP_COORDS)
- self.window.set_entry_location(entry_bb)
- self.window.show_all()
-
- def __set_entry_location(self, acc):
- text = acc.queryText()
- cursor_bb = Rectangle(
- *text.getCharacterExtents(text.caretOffset,
- pyatspi.DESKTOP_COORDS))
-
- component = acc.queryComponent()
- entry_bb = component.getExtents(pyatspi.DESKTOP_COORDS)
-
- if cursor_bb == Rectangle(0, 0, 0, 0):
- cursor_bb = entry_bb
-
- self.window.set_cursor_location(cursor_bb)
- self.window.set_entry_location(entry_bb)
-
- self.window.show_all()
-
- def on_focus(self, event):
- acc = event.source
- source_role = acc.getRole()
- if acc.getState().contains(pyatspi.STATE_EDITABLE) or \
- source_role == pyatspi.ROLE_TERMINAL:
- if source_role in (pyatspi.ROLE_TEXT,
- pyatspi.ROLE_PARAGRAPH,
- pyatspi.ROLE_PASSWORD_TEXT,
- pyatspi.ROLE_TERMINAL):
- if event.type.startswith("focus") or event.detail1 == 1:
- self.__set_text_location(acc)
- self.__current_acc = event.source
- self.__set_location = self.__set_text_location
- if debug == True:
- print "enter text widget in", event.host_application.name
- elif event.detail1 == 0 and acc == self.__current_acc:
- self.window.hide()
- self.__current_acc = None
- self.__set_location = None
- if debug == True:
- print "leave text widget in", event.host_application.name
-
- elif source_role == pyatspi.ROLE_ENTRY:
- if event.type.startswith("focus") or event.detail1 == 1:
- self.__set_entry_location(acc)
- self.__current_acc = event.source
- self.__set_location = self.__set_entry_location
- if debug == True:
- print "enter entry widget in", event.host_application.name
- elif event.detail1 == 0:
- self.window.hide()
- self.__current_acc = None
- self.__set_location = None
- if debug == True:
- print "leave entry widget in", event.host_application.name
- else:
- if debug == True:
- print _("WARNING - Caribou: unhandled editable widget:"), event.source
-
- # Firefox does not report leave entry widget events.
- # This could be a way to get the entry widget leave events.
- #else:
- # if event.detail1 == 1:
- # self.window.hide()
- # print "--> LEAVE EDITABLE TEXT <--"
-
- def clean_exit(self):
- self.scan_master.stop()
- self._deregister_event_listeners()
-
-
diff --git a/caribou/ui/opacity.py b/caribou/ui/opacity.py
deleted file mode 100644
index be742d8..0000000
--- a/caribou/ui/opacity.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Caribou - text entry and UI navigation application
-#
-# Copyright (C) 2009 Eitan Isaacson <eitan@monotonous.org>
-#
-# This program 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 program 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 program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-import glib
-from math import sqrt
-
-class ProximityWindowBase(object):
- def __init__(self, min_alpha=1.0, max_alpha=1.0, max_distance=100):
- if self.__class__ == ProximityWindowBase:
- raise TypeError, \
- "ProximityWindowBase is an abstract class, " \
- "must be subclassed with a Gtk.Window"
- self.connect('map-event', self.__onmapped)
- self.max_distance = max_distance
- if max_alpha < min_alpha:
- raise ValueError, "min_alpha can't be larger than max_alpha"
- self.min_alpha = min_alpha
- self.max_alpha = max_alpha
-
- def __onmapped(self, obj, event):
- if self.is_composited():
- self.set_opacity(self.max_alpha)
- if self.max_alpha != self.min_alpha:
- # Don't waste CPU if the max and min are equal.
- glib.timeout_add(80, self._proximity_check)
-
- def _proximity_check(self):
- px, py = self.get_pointer()
-
- ww = self.get_allocated_width()
- wh = self.get_allocated_height()
-
- distance = self._get_distance_to_bbox(px, py, ww, wh)
-
- opacity = (self.max_alpha - self.min_alpha) * \
- (1 - min(distance, self.max_distance)/self.max_distance)
- opacity += self.min_alpha
-
- self.set_opacity(opacity)
- return self.props.visible
-
- def _get_distance_to_bbox(self, px, py, bw, bh):
- if px < 0:
- x_distance = float(abs(px))
- elif px > bw:
- x_distance = float(px - bw)
- else:
- x_distance = 0.0
-
- if py < 0:
- y_distance = float(abs(px))
- elif py > bh:
- y_distance = float(py - bh)
- else:
- y_distance = 0.0
-
- if y_distance == 0 and x_distance == 0:
- return 0.0
- elif y_distance != 0 and x_distance == 0:
- return y_distance
- elif y_distance == 0 and x_distance != 0:
- return x_distance
- else:
- x2 = 0 if x_distance > 0 else bw
- y2 = 0 if y_distance > 0 else bh
- return sqrt((px - x2)**2 + (py - y2)**2)
diff --git a/caribou/ui/scan.py b/caribou/ui/scan.py
deleted file mode 100644
index 94aa8c2..0000000
--- a/caribou/ui/scan.py
+++ /dev/null
@@ -1,216 +0,0 @@
-import gobject
-import pyatspi
-from gi.repository import Gdk
-from gi.repository import Gtk
-import caribou.common.const as const
-from caribou.common.settings_manager import SettingsManager
-
-# Scan constants
-BUTTON = 'button'
-ROW = 'row'
-BLOCK = 'block'
-CANCEL = 'cancel'
-REVERSE = 'reverse'
-MOUSE_SWITCH_TYPE = 'mouse'
-KEYBOARD_SWITCH_TYPE = 'keyboard'
-KEYBOARD_KEY_LIST = {"Shift R" : "Shift_R",
- "Shift L" : "Shift_L",
- "Alt Gr" : "ISO_Level3_Shift",
- "Num Lock": "Num_Lock"}
-DEFAULT_KEYBOARD_KEY = 'Shift R'
-DEFAULT_MOUSE_BUTTON = '1'
-MIN_STEP_TIME = 50
-MAX_STEP_TIME = 5000
-TIME_SINGLE_INCREMENT = 1
-TIME_MULTI_INCREMENT = 10
-DEFAULT_STEP_TIME = 1000
-DEFAULT_SCANNING_TYPE = ROW
-DEFAULT_SWITCH_TYPE = KEYBOARD_SWITCH_TYPE
-
-class ScanMaster():
- def __init__(self, root_window, keyboard=None):
- self.root_window = root_window
- self._timer = 0
- self._scan_path = None
- self.started = False
-
- SettingsManager.step_time.connect("value-changed",
- self._on_step_time_changed)
- SettingsManager.scanning_type.connect("value-changed",
- self._on_scanning_type_changed)
- if keyboard:
- self.set_keyboard(keyboard)
-
- def start(self):
- if self.started: return
-
- if self._timer == 0:
- self._timer = gobject.timeout_add(
- int(SettingsManager.step_time.value*1000), self._scan)
-
- self._grab_mouse_events()
-
- pyatspi.Registry.registerKeystrokeListener(
- self._on_key_pressed, mask=0, kind=(pyatspi.KEY_PRESSED_EVENT,))
-
- def stop(self):
- if self.started: return
-
- self._ungrab_mouse_events()
-
- if self._last_block is not None:
- self._multi_map(lambda x: x.scan_highlight_clear(), self._last_block)
-
- if self._timer != 0:
- gobject.source_remove(self._timer)
- self._timer = 0
-
- pyatspi.Registry.deregisterKeystrokeListener(
- self._on_key_pressed, mask=0, kind=pyatspi.KEY_PRESSED_EVENT)
-
- def _on_scanning_type_changed(self, setting, val):
- layout = self.keyboard.get_current_layout()
- if SettingsManager.scanning_type.value == ROW:
- self._blocks = layout.get_scan_rows()
- else:
- self._blocks = layout.get_scan_blocks()
-
- def _on_step_time_changed(self, setting, val):
- if self._timer != 0:
- gobject.source_remove(self._timer)
- self._timer = gobject.timeout_add(int(1000*val), self._scan)
-
- def _on_layout_activated(self, keyboard, layout, num):
- if SettingsManager.scanning_type.value == ROW:
- self._blocks = layout.get_scan_rows()
- else:
- self._blocks = layout.get_scan_blocks()
- if SettingsManager.reverse_scanning.value:
- self._scan_path = [0]
- else:
- self._scan_path = [-1]
-
- def set_keyboard(self, keyboard):
- self._last_block = None
- keyboard.connect("switch-page", self._on_layout_activated)
- self.keyboard = keyboard
-
- def _multi_map(self, func, array):
- if isinstance(array, list):
- for item in array:
- self._multi_map(func, item)
- else:
- func(array)
-
- def _get_element_at_path(self, array, path):
- element = array
- for index in path:
- element = element[index]
- return element
-
- def _get_next_reverse_block(self):
- cancel = False
-
- if self._scan_path[-1] > 0:
- self._scan_path = self._scan_path[:-1] + [0]
-
- self._scan_path += [self._scan_path.pop() - 1]
-
- try:
- block = self._get_element_at_path(self._blocks,
- self._scan_path)
- except IndexError:
- if len(self._scan_path) == 1:
- block = self._blocks[-1]
- self._scan_path = [-1]
- else:
- block = self._get_element_at_path(
- self._blocks, self._scan_path[:-1])
- self._scan_path = self._scan_path[:-1] + [0]
- cancel = True
-
- return cancel, block
-
-
- def _get_next_block(self):
- cancel = False
- self._scan_path += [self._scan_path.pop() + 1]
- try:
- block = self._get_element_at_path(self._blocks,
- self._scan_path)
- except IndexError:
- if len(self._scan_path) == 1:
- block = self._blocks[0]
- self._scan_path = [0]
- else:
- block = self._get_element_at_path(
- self._blocks, self._scan_path[:-1])
- self._scan_path = self._scan_path[:-1] + [-1]
- cancel = True
-
- return cancel, block
-
- def _scan(self):
- if self._scan_path is None: return True
-
- if self._last_block is not None:
- self._multi_map(lambda x: x.scan_highlight_clear(), self._last_block)
-
- if SettingsManager.reverse_scanning.value:
- self._cancel, next_block = self._get_next_reverse_block()
- else:
- self._cancel, next_block = self._get_next_block()
-
- if self._cancel:
- self._multi_map(lambda x: x.scan_highlight_cancel(), next_block)
- elif isinstance(next_block, list):
- if SettingsManager.scanning_type.value == ROW:
- self._multi_map(lambda x: x.scan_highlight_row(), next_block)
- else:
- self._multi_map(lambda x: x.scan_highlight_block(), next_block)
- else:
- self._multi_map(lambda x: x.scan_highlight_key(), next_block)
-
- self._last_block = next_block
- return True
-
- def _do_switch(self):
- if self._cancel:
- assert(len(self._scan_path) > 1)
- self._scan_path.pop()
- elif isinstance(self._last_block, list):
- assert(len(self._last_block) > 0)
- if SettingsManager.reverse_scanning.value:
- self._scan_path.append(0)
- else:
- self._scan_path.append(-1)
- else:
- self._last_block.clicked()
-
- def _grab_mouse_events(self):
- Gdk.event_handler_set(self._mouse_handler, None)
-
- def _ungrab_mouse_events(self):
- Gdk.event_handler_set(Gtk.main_do_event, None)
-
- def _mouse_handler(self, event, user_data):
- if SettingsManager.switch_type.value != MOUSE_SWITCH_TYPE or \
- event.type not in (Gdk.EventType.BUTTON_PRESS,
- Gdk.EventType.ENTER_NOTIFY):
- Gtk.main_do_event(event)
- return
-
- if self.root_window.get_window().is_visible():
- if event.type == Gdk.EventType.BUTTON_PRESS and \
- str(event.button.button) == \
- SettingsManager.mouse_button.value:
- self._do_switch()
- elif event.type != Gdk.EventType.ENTER_NOTIFY:
- Gtk.main_do_event(event)
- else:
- Gtk.main_do_event(event)
-
- def _on_key_pressed(self, event):
- if SettingsManager.switch_type.value == KEYBOARD_SWITCH_TYPE and \
- SettingsManager.keyboard_key.value == event.event_string:
- self._do_switch()