[caribou/geometry] Major re-work of Python modules:
- From: Eitan Isaacson <eitani src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [caribou/geometry] Major re-work of Python modules:
- Date: Sat, 30 Apr 2011 07:39:02 +0000 (UTC)
commit 7740764049d02a575b7b51a2a1081667b5eb5ff6
Author: Eitan Isaacson <eitan monotonous org>
Date: Sun Apr 24 16:57:28 2011 -0700
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.
.gitignore | 3 +-
bin/Makefile.am | 2 +-
bin/caribou-preferences.in | 32 ++
bin/caribou.in | 33 +-
caribou/Makefile.am | 10 +-
caribou/__init__.py | 3 +-
caribou/antler/Makefile.am | 10 +
caribou/antler/keyboard_view.py | 164 +++++++
caribou/antler/main.py | 30 ++
caribou/{ui => antler}/window.py | 147 ++++--
caribou/common/Makefile.am | 11 -
caribou/common/__init__.py | 1 -
caribou/common/const.py | 43 --
caribou/common/settings.py | 161 -------
caribou/daemon/Makefile.am | 8 +
caribou/daemon/__init__.py | 1 +
caribou/daemon/main.py | 150 ++++++
caribou/{ui => }/i18n.py.in | 0
caribou/settings/Makefile.am | 11 +
caribou/settings/__init__.py | 5 +
caribou/settings/caribou_settings.py | 64 +++
caribou/{ui => settings}/preferences_window.py | 70 ++--
caribou/{common => settings}/setting_types.py | 10 +
caribou/{common => settings}/settings_manager.py | 9 +-
caribou/ui/Makefile.am | 16 -
caribou/ui/__init__.py | 1 -
caribou/ui/keyboard.py | 535 ----------------------
caribou/ui/main.py | 275 -----------
caribou/ui/opacity.py | 83 ----
caribou/ui/scan.py | 216 ---------
configure.ac | 10 +-
data/Makefile.am | 4 +-
data/layouts/Makefile.am | 2 +-
data/layouts/natural/Makefile.am | 8 -
data/layouts/touch/Makefile.am | 8 +
data/layouts/{natural => touch}/ara.json | 0
data/layouts/{natural => touch}/il.json | 0
data/layouts/{natural => touch}/us.json | 0
po/POTFILES.in | 7 +-
tools/make_schema.py | 84 ++++
40 files changed, 751 insertions(+), 1476 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index a0ba962..de388bc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,10 +9,11 @@ aclocal.m4
autom4te.cache
*.make
bin/caribou
+bin/caribou-preferences
install-sh
missing
py-compile
-caribou/ui/i18n.py
+caribou/i18n.py
mkinstalldirs
po/Makefile.in.in
po/POTFILES
diff --git a/bin/Makefile.am b/bin/Makefile.am
index cae01c9..e7797ec 100644
--- a/bin/Makefile.am
+++ b/bin/Makefile.am
@@ -1,4 +1,4 @@
-bin_SCRIPTS = caribou
+bin_SCRIPTS = caribou caribou-preferences
CLEANFILES = $(bin_SCRIPTS)
diff --git a/bin/caribou-preferences.in b/bin/caribou-preferences.in
new file mode 100644
index 0000000..a745bab
--- /dev/null
+++ b/bin/caribou-preferences.in
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+import sys, os
+
+libs = os.path.join('@prefix@', 'lib', 'python PYTHON_VERSION@', 'site-packages')
+
+# This might be run from the build dir.
+_dirname = os.path.dirname(__file__)
+if _dirname != "@prefix@/bin":
+ libs = os.path.normpath(os.path.join(_dirname, '..'))
+
+sys.path.insert(1, libs)
+
+from gi.repository import Gtk
+from caribou.settings.settings_manager import SettingsManager
+from caribou.settings import CaribouSettings
+from caribou.settings.preferences_window import PreferencesWindow
+
+import signal
+signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+w = PreferencesWindow(CaribouSettings())
+
+w.connect("delete-event", lambda x, y: Gtk.main_quit())
+
+w.show_all()
+
+try:
+ Gtk.main()
+except KeyboardInterrupt:
+ Gtk.main_quit()
+
diff --git a/bin/caribou.in b/bin/caribou.in
index 824c68d..ac1debb 100644
--- a/bin/caribou.in
+++ b/bin/caribou.in
@@ -30,32 +30,16 @@ import sys
import pyatspi
import os
-# We can't rely on prefix if we're installed by relocated RPM. Instead, we
-# use __file__ and for now hope that lib is relative to bin.
-sys.prefix = os.path.normpath(os.path.join(os.path.dirname(__file__), '..'))
-libs = os.path.join(sys.prefix, 'lib',
- 'python PYTHON_VERSION@', 'site-packages')
-# point to the proper site-packages path
-sys.path.insert(1, libs)
+libs = os.path.join('@prefix@', 'lib', 'python PYTHON_VERSION@', 'site-packages')
# This might be run from the build dir.
_dirname = os.path.dirname(__file__)
-if os.path.dirname(__file__) != "@prefix@/bin":
- srcdir = os.path.normpath(os.path.join(_dirname, '..'))
- sys.path.insert(1, srcdir)
- import caribou.common
- import caribou.ui
- caribou.data_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
- "@top_srcdir@",
- "data"))
-else:
- import caribou.common
- import caribou.ui
- caribou.data_path = os.path.join("@prefix@", "share", "caribou")
+if _dirname != "@prefix@/bin":
+ libs = os.path.normpath(os.path.join(_dirname, '..'))
+
+sys.path.insert(1, libs)
-import caribou.ui.window as window
-import caribou.ui.keyboard as keyboard
-import caribou.ui.main as main
+from caribou.daemon import CaribouDaemon
_ = gettext.gettext
@@ -71,10 +55,9 @@ if __name__ == "__main__":
help="print debug messages on stdout")
(options, args) = parser.parse_args()
- main.debug = options.debug
+ #main.debug = options.debug
- caribou = main.Caribou()
- caribou.window.hide()
+ caribou = CaribouDaemon()
try:
pyatspi.Registry.start()
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
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
similarity index 70%
rename from caribou/ui/window.py
rename to 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/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
similarity index 100%
rename from caribou/ui/i18n.py.in
rename to 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
similarity index 91%
rename from caribou/ui/preferences_window.py
rename to 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
similarity index 91%
rename from caribou/common/setting_types.py
rename to 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
similarity index 93%
rename from caribou/common/settings_manager.py
rename to 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/configure.ac b/configure.ac
index 8108c43..f1d0895 100644
--- a/configure.ac
+++ b/configure.ac
@@ -72,13 +72,15 @@ AC_OUTPUT([
Makefile
po/Makefile.in
caribou/Makefile
-caribou/common/Makefile
-caribou/ui/i18n.py
-caribou/ui/Makefile
+caribou/i18n.py
+caribou/antler/Makefile
+caribou/settings/Makefile
+caribou/daemon/Makefile
bin/Makefile
bin/caribou
+bin/caribou-preferences
data/Makefile
data/layouts/Makefile
-data/layouts/natural/Makefile
+data/layouts/touch/Makefile
libcaribou/Makefile
])
diff --git a/data/Makefile.am b/data/Makefile.am
index 98da3b2..745443b 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -17,8 +17,8 @@ autostart_DATA = $(autostart_in_files:.desktop.in=.desktop)
EXTRA_DIST = $(desktop_in_files) $(autostart_in_files)
-org.gnome.caribou.gschema.xml.in: $(top_srcdir)/caribou/common/settings.py
- PYTHONPATH=${PYTHONPATH}:$(top_srcdir) $(PYTHON) $< > $@
+org.gnome.caribou.gschema.xml.in: $(top_srcdir)/tools/make_schema.py
+ $< org.gnome.caribou
CLEANFILES = $(desktop_DATA) \
$(autostart_DATA) \
diff --git a/data/layouts/Makefile.am b/data/layouts/Makefile.am
index 40457f3..9d82efe 100644
--- a/data/layouts/Makefile.am
+++ b/data/layouts/Makefile.am
@@ -1 +1 @@
-SUBDIRS = natural
+SUBDIRS = touch
diff --git a/data/layouts/touch/Makefile.am b/data/layouts/touch/Makefile.am
new file mode 100644
index 0000000..f18d728
--- /dev/null
+++ b/data/layouts/touch/Makefile.am
@@ -0,0 +1,8 @@
+touchlayoutsdir = $(datadir)/caribou/layouts/touch
+
+touchlayouts_DATA = \
+ ara.json \
+ il.json \
+ us.json
+
+EXTRA_DIST = $(touchlayouts_DATA)
diff --git a/data/layouts/natural/ara.json b/data/layouts/touch/ara.json
similarity index 100%
rename from data/layouts/natural/ara.json
rename to data/layouts/touch/ara.json
diff --git a/data/layouts/natural/il.json b/data/layouts/touch/il.json
similarity index 100%
rename from data/layouts/natural/il.json
rename to data/layouts/touch/il.json
diff --git a/data/layouts/natural/us.json b/data/layouts/touch/us.json
similarity index 100%
rename from data/layouts/natural/us.json
rename to data/layouts/touch/us.json
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 0a869dd..5f44bd0 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,5 +1,4 @@
[encoding: UTF-8]
-caribou/ui/main.py
-caribou/ui/keyboard.py
-caribou/common/settings.py
-caribou/ui/preferences_window.py
+caribou/settings/caribou_settings.py
+caribou/__init__.py
+caribou/daemon/main.py
diff --git a/tools/make_schema.py b/tools/make_schema.py
new file mode 100755
index 0000000..c9ee361
--- /dev/null
+++ b/tools/make_schema.py
@@ -0,0 +1,84 @@
+#!/usr/bin/python
+
+from gi.repository import GLib
+import xml.dom.minidom
+
+import os,sys
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+from caribou.settings import caribou_settings
+
+class SchemasMaker:
+ def __init__(self, settings):
+ self.settings = settings
+
+ def create_schemas(self):
+ doc = xml.dom.minidom.Document()
+ schemafile = doc.createElement('schemalist')
+ schema = doc.createElement('schema')
+ schema.setAttribute("id", self.settings.schema_id)
+ schemafile.appendChild(schema)
+ self._create_schema(self.settings, doc, schema)
+
+ fp = open("%s.gschema.xml.in" % self.settings.schema_id, 'w')
+ self._pretty_xml(fp, schemafile)
+ fp.close()
+
+ 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, fp, e, indent=0):
+ if not e.childNodes or \
+ (len(e.childNodes) == 1 and \
+ e.firstChild.nodeType == e.TEXT_NODE):
+ fp.write('%s%s\n' % (' '*indent*2, e.toxml().strip()))
+ else:
+ fp.write('%s<%s%s>\n' % (' '*indent*2, e.tagName, self._attribs(e)))
+ for c in e.childNodes:
+ self._pretty_xml(fp, c, indent + 1)
+ fp.write('%s</%s>\n' % (' '*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, 'path'):
+ schemalist.setAttribute("path", setting.path)
+ 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)
+
+if __name__ == "__main__":
+ from caribou.settings import AllSettings
+
+ if (len(sys.argv) != 2):
+ print "usage: %s <schema id>" % sys.argv[0]
+ sys.exit(1)
+
+ avail_settings = dict([(s.schema_id, s) for s in AllSettings])
+
+ try:
+ settings = avail_settings[sys.argv[-1]]
+ except KeyError:
+ print "Schema '%s' not available", sys.argv[-1]
+ sys.exit(1)
+
+ maker = SchemasMaker(settings)
+ maker.create_schemas()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]