[caribou] Added scanning support.



commit c35d19ab7d43328c33e9b1916f8508df944c2fc3
Author: David Pellicer <davidpellicermartin gmail com>
Date:   Tue Dec 7 09:19:56 2010 -0800

    Added scanning support.

 caribou/common/const.py |   22 +++
 caribou/ui/Makefile.am  |    1 +
 caribou/ui/__init__.py  |    2 +-
 caribou/ui/keyboard.py  |  241 ++++++++++++++++++++++++++-
 caribou/ui/main.py      |    7 +-
 caribou/ui/scan.py      |  438 +++++++++++++++++++++++++++++++++++++++++++++++
 caribou/ui/window.py    |    6 +
 data/caribou-prefs.ui   |  309 +++++++++++++++++++++++++++++++++-
 8 files changed, 1020 insertions(+), 6 deletions(-)
---
diff --git a/caribou/common/const.py b/caribou/common/const.py
index 202bc16..ecb61da 100644
--- a/caribou/common/const.py
+++ b/caribou/common/const.py
@@ -56,3 +56,25 @@ KEY_MASKS = {'shift': gtk.gdk.SHIFT_MASK,
              'button3': gtk.gdk.BUTTON3_MASK,
              'button4': gtk.gdk.BUTTON4_MASK,
              'button5': gtk.gdk.BUTTON5_MASK}
+
+# Scan constans
+BUTTON = 'button'
+ROW = '1'
+BLOCK = '0'
+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
diff --git a/caribou/ui/Makefile.am b/caribou/ui/Makefile.am
index f4a352d..27046b4 100644
--- a/caribou/ui/Makefile.am
+++ b/caribou/ui/Makefile.am
@@ -5,6 +5,7 @@ caribou_ui_PYTHON = \
 	animation.py \
         i18n.py \
 	keyboard.py \
+	scan.py     \
 	main.py \
 	opacity.py \
 	window.py 
diff --git a/caribou/ui/__init__.py b/caribou/ui/__init__.py
index 8d1c8b6..8b13789 100644
--- a/caribou/ui/__init__.py
+++ b/caribou/ui/__init__.py
@@ -1 +1 @@
- 
+
diff --git a/caribou/ui/keyboard.py b/caribou/ui/keyboard.py
index ace93b0..e395af8 100644
--- a/caribou/ui/keyboard.py
+++ b/caribou/ui/keyboard.py
@@ -23,6 +23,7 @@
 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 import caribou.common.const as const
+import caribou.ui.scan as scan
 import gconf
 import gobject
 import gtk
@@ -31,6 +32,7 @@ import sys
 import virtkey
 import os
 import traceback
+from caribou.ui.i18n import _
 try:
     import json
 except ImportError:
@@ -119,6 +121,150 @@ class KeyboardPreferences:
                                       self._on_default_font_toggled,
                                       client, key_font_button)
 
+        reverse_scanning_checkbox = builder.get_object(
+                                    "reverse_scanning_checkbox")
+
+        reverse_scanning = client.get_bool(const.CARIBOU_GCONF + 
+                                            "/reverse_scanning")
+        if reverse_scanning is None:
+            reverse_scanning is False
+        reverse_scanning_checkbox.set_active(reverse_scanning)
+        reverse_scanning_checkbox.connect('toggled', 
+                                        self._on_reverse_scanning_toggled,
+                                        client)
+
+        block_scanning_color_button = builder.get_object("block_scanning_color_button")
+        block_scanning_color_string = client.get_string(const.CARIBOU_GCONF +
+                                                "/block_scanning_color") or "purple"
+        block_scanning_color = gtk.gdk.Color(block_scanning_color_string)
+        block_scanning_color_button.set_color(block_scanning_color)
+        block_scanning_color_button.connect("color-set",
+                                    self._on_block_scanning_color_set,
+                                    client)
+
+        row_scanning_color_button = builder.get_object("row_scanning_color_button")
+        row_scanning_color_string = client.get_string(const.CARIBOU_GCONF +
+                                                "/row_scanning_color") or "blue"
+        row_scanning_color = gtk.gdk.Color(row_scanning_color_string)
+        row_scanning_color_button.set_color(row_scanning_color)
+        row_scanning_color_button.connect("color-set",
+                                    self._on_row_scanning_color_set,
+                                    client)
+
+        button_scanning_color_button = builder.get_object("button_scanning_color_button")
+        button_scanning_color_string = client.get_string(const.CARIBOU_GCONF +
+                                                "/button_scanning_color") or "green"
+        button_scanning_color = gtk.gdk.Color(button_scanning_color_string)
+        button_scanning_color_button.set_color(button_scanning_color)
+        button_scanning_color_button.connect("color-set",
+                                    self._on_button_scanning_color_set,
+                                    client)
+
+        cancel_scanning_color_button = builder.get_object(
+                                        "cancel_scanning_color_button")
+        cancel_scanning_color_string = client.get_string(const.CARIBOU_GCONF +
+                                            "/cancel_scanning_color") or "red"
+        cancel_scanning_color = gtk.gdk.Color(cancel_scanning_color_string)
+        cancel_scanning_color_button.set_color(cancel_scanning_color)
+        cancel_scanning_color_button.connect("color-set",
+                                    self._on_cancel_scanning_color_set,
+                                    client)
+        scanning_type_combo = builder.get_object("scanning_type_combo")
+        scanning_type_combo.connect("changed", 
+                                    self._on_scanning_type_combo_changed, 
+                                    client)
+        types = [_("block"), _("row")]
+        for type in types:
+            scanning_type_combo.append_text(type)
+        type = client.get_string(const.CARIBOU_GCONF + "/scanning_type")
+        try:
+            scanning_type_combo.set_active(int(type))
+        except:
+            scanning_type_combo.set_active(0)
+
+
+        mouse_button_combo = builder.get_object("mouse_button_combo")
+        mouse_button_combo.connect("changed", 
+                                   self._on_mouse_button_combo_changed,
+                                   client)
+        mouse_buttons = [_("Left"), _("Center"), _("Right")]
+        for button in mouse_buttons:
+            mouse_button_combo.append_text(button)
+        button = client.get_string(const.CARIBOU_GCONF + "/mouse_button")
+        try:
+            mouse_button_combo.set_active(int(button)-1)
+        except:
+            mouse_button_combo.set_active(0)
+        
+
+        keyboard_key_combo = builder.get_object("keyboard_key_combo")
+        keyboard_keys = const.KEYBOARD_KEY_LIST.keys()
+        keyboard_values = const.KEYBOARD_KEY_LIST.values()
+        for key in keyboard_keys:
+            keyboard_key_combo.append_text(key)
+        key = client.get_string(const.CARIBOU_GCONF + "/keyboard_key")
+        try:
+            keyboard_key_combo.set_active(keyboard_values.index(key))
+        except:
+            keyboard_key_combo.set_active(0)
+
+        keyboard_key_combo.connect("changed", 
+                                   self._on_keyboard_key_combo_changed,
+                                   client)
+
+        mouse_switch_radio = builder.get_object("mouse_switch_radio")
+        keyboard_switch_radio = builder.get_object("keyboard_switch_radio")
+        mouse_switch_radio.connect("toggled", self._on_switch_radio_toggled, 
+                                    client, mouse_button_combo,
+                                    keyboard_key_combo)
+        
+        switch_type = client.get_string(const.CARIBOU_GCONF + "/switch_type")
+        if switch_type == const.KEYBOARD_SWITCH_TYPE:
+            keyboard_switch_radio.set_active(True)
+        else:
+            mouse_switch_radio.set_active(True)
+
+        step_time_spin = builder.get_object("step_time_spin")
+        step_time_spin.set_value(client.get_int(const.CARIBOU_GCONF + 
+                                 "/step_time"))
+        step_time_spin.connect("value_changed", 
+                               self._on_step_time_spin_changed, client)
+
+        scan_enabled_checkbox = builder.get_object("scan_enabled_checkbox")
+        scan_enabled = client.get_bool(const.CARIBOU_GCONF + "/scan_enabled")
+        if scan_enabled is None:
+            scan_enabled is False
+
+        scan_enabled_checkbox.set_active(scan_enabled)
+
+        self._on_scan_enabled_toggled(scan_enabled_checkbox, 
+                                      client, reverse_scanning_checkbox, 
+                                      row_scanning_color_button,
+                                      button_scanning_color_button,
+                                      block_scanning_color_button,
+                                      cancel_scanning_color_button,
+                                      mouse_switch_radio,
+                                      keyboard_switch_radio,
+                                      mouse_button_combo,
+                                      keyboard_key_combo,
+                                      scanning_type_combo,
+                                      step_time_spin)
+                                      
+        scan_enabled_checkbox.connect('toggled', 
+                                    self._on_scan_enabled_toggled, client, 
+                                    reverse_scanning_checkbox,
+                                    row_scanning_color_button,
+                                    block_scanning_color_button,
+                                    button_scanning_color_button,
+                                    cancel_scanning_color_button,
+                                    mouse_switch_radio,
+                                    keyboard_switch_radio,
+                                    mouse_button_combo,
+                                    keyboard_key_combo,
+                                    scanning_type_combo,
+                                    step_time_spin)
+
+
         kbds = self._fetch_keyboards()
         for kbddef in kbds:
             layout_combo.append_text(kbddef)
@@ -149,6 +295,36 @@ class KeyboardPreferences:
         normal_color_button.set_sensitive(not use_defaults)
         mouse_over_color_button.set_sensitive(not use_defaults)
 
+    def _on_scan_enabled_toggled(self, scan_enabled_checkbox, 
+                                 gconf_client, *args):
+        scan_enabled = scan_enabled_checkbox.get_active()
+        gconf_client.set_bool(const.CARIBOU_GCONF + "/scan_enabled", scan_enabled)
+        for arg in args:
+            arg.set_sensitive(scan_enabled)
+
+    def _on_reverse_scanning_toggled(self, reverse_scanning_checkbox,
+                                     gconf_client):
+        reverse_scanning = reverse_scanning_checkbox.get_active()
+        gconf_client.set_bool(const.CARIBOU_GCONF + "/reverse_scanning", 
+                              reverse_scanning)
+    def _on_switch_radio_toggled(self, mouse_switch_radio, 
+                                       gconf_client, mouse_button_combo, 
+                                       keyboard_key_combo):
+        mouse_switch = mouse_switch_radio.get_active()
+        if mouse_switch:
+            gconf_client.set_string(const.CARIBOU_GCONF + "/switch_type", 
+                                  const.MOUSE_SWITCH_TYPE)
+        else:
+            gconf_client.set_string(const.CARIBOU_GCONF + "/switch_type", 
+                                  const.KEYBOARD_SWITCH_TYPE)
+        mouse_button_combo.set_sensitive(mouse_switch)
+        keyboard_key_combo.set_sensitive(not mouse_switch)
+
+    def _on_step_time_spin_changed(self, step_time_spin, gconf_client):
+        gconf_client.set_int(const.CARIBOU_GCONF + "/step_time", 
+                             step_time_spin.get_value_as_int())
+
+
     def destroy(self, widget, data = None):
         self.window.destroy()
 
@@ -167,13 +343,46 @@ class KeyboardPreferences:
 
     def _on_layout_changed(self, combobox, client):
         kbdname = combobox.get_active_text()
-        if kbdname:
+        actual_layout = client.get_string("/apps/caribou/osk/layout")
+        if kbdname and kbdname != actual_layout:
             client.set_string("/apps/caribou/osk/layout", kbdname)
 
+    def _on_scanning_type_combo_changed(self, scanning_type_combo, gconf_client):
+        value = str(scanning_type_combo.get_active())
+        if value:
+            gconf_client.set_string(const.CARIBOU_GCONF + "/scanning_type", value)
+
+    def _on_mouse_button_combo_changed(self, mouse_button_combo, client):
+        value = mouse_button_combo.get_active() + 1
+        if value:
+            client.set_string(const.CARIBOU_GCONF + "/mouse_button", str(value))
+
+    def _on_keyboard_key_combo_changed(self, keyboard_key_combo, client):
+        value = keyboard_key_combo.get_active_text()
+        if value:
+            client.set_string(const.CARIBOU_GCONF + "/keyboard_key", 
+                              const.KEYBOARD_KEY_LIST[value])
+
     def _on_normal_state_color_set(self, colorbutton, client):
         color = colorbutton.get_color().to_string()
         client.set_string(const.CARIBOU_GCONF + "/normal_color", color)
 
+    def _on_block_scanning_color_set(self, colorbutton, client):
+        color = colorbutton.get_color().to_string()
+        client.set_string(const.CARIBOU_GCONF + "/block_scanning_color", color)
+
+    def _on_row_scanning_color_set(self, colorbutton, client):
+        color = colorbutton.get_color().to_string()
+        client.set_string(const.CARIBOU_GCONF + "/row_scanning_color", color)
+
+    def _on_button_scanning_color_set(self, colorbutton, client):
+        color = colorbutton.get_color().to_string()
+        client.set_string(const.CARIBOU_GCONF + "/button_scanning_color", color)
+
+    def _on_cancel_scanning_color_set(self, colorbutton, client):
+        color = colorbutton.get_color().to_string()
+        client.set_string(const.CARIBOU_GCONF + "/cancel_scanning_color", color)
+
     def _on_mouse_over_color_set(self, colorbutton, client):
         color = colorbutton.get_color().to_string()
         client.set_string(const.CARIBOU_GCONF + "/mouse_over_color", color)
@@ -433,10 +642,14 @@ class CaribouKeyboard(gtk.Notebook):
         self._gconf_connections.append(self.client.notify_add(
                             const.CARIBOU_GCONF + "/key_font",
                             self._key_font_changed))
+        self._gconf_connections.append(self.client.notify_add(
+                            const.CARIBOU_GCONF + "/scan_enabled", 
+                            self._scan_enabled))
 
         self.connect('size-allocate', self._on_size_allocate)
 
         self.row_height = -1
+        self.scan_enabled = False
         self.keyboard_preferences = KeyboardPreferences()
 
     def reset_row_height(self):
@@ -466,6 +679,7 @@ class CaribouKeyboard(gtk.Notebook):
         layouts = kb_deserializer.deserialize(kb_location)
         self._set_layouts(layouts)
         self._update_key_style()
+        self._enable_scanning()
 
     def _set_layouts(self, layout_list):
         self._clear()
@@ -486,6 +700,9 @@ class CaribouKeyboard(gtk.Notebook):
                         key.connect('clicked',
                                     self._pressed_normal_key)
 
+    def _scan_enabled(self, client, connection_id, entry, args):
+        self._enable_scanning()
+
     def _colors_changed(self, client, connection_id, entry, args):
         self._update_key_style()
 
@@ -543,6 +760,8 @@ class CaribouKeyboard(gtk.Notebook):
     def show_all(self):
         self.set_current_page(self.current_page)
         gtk.Notebook.show_all(self)
+        if self.scan_enabled:
+            self.scan_service.start()
 
     def is_preferences_open(self):
         if self.keyboard_preferences.window.window and \
@@ -554,7 +773,24 @@ class CaribouKeyboard(gtk.Notebook):
     def _pressed_preferences_key(self, key):
         self.keyboard_preferences.window.show_all()
 
+    def _enable_scanning(self):
+        enable = self.client.get_bool(const.CARIBOU_GCONF + '/scan_enabled')
+        if enable:
+            current_layout = self.get_nth_page(self.current_page)
+            if current_layout and not self.scan_enabled:
+                self.scan_service = scan.Scan_service(current_layout.rows, 
+                                    self.get_parent().get_parent())
+                self.scan_enabled = True
+        elif self.scan_enabled:
+            self.scan_service.destroy()
+            self.scan_service = None
+            self.scan_enabled = False
+        else: 
+            self.scan_enabled = False
+
     def destroy(self):
+        if self.scan_enabled:
+            self.scan_service.destroy()
         for id in self._gconf_connections:
             self.client.notify_remove(id)
         super(gtk.Notebook, self).destroy()
@@ -565,4 +801,7 @@ class CaribouKeyboard(gtk.Notebook):
             if self.get_nth_page(i).layout_name == name:
                 self.set_current_page(i)
                 self.current_page = i
+                if self.scan_enabled:
+                    self.scan_service.change_keyboard(
+                            self.get_nth_page(i).rows)
                 break
diff --git a/caribou/ui/main.py b/caribou/ui/main.py
index 0f99398..013cabf 100644
--- a/caribou/ui/main.py
+++ b/caribou/ui/main.py
@@ -131,9 +131,10 @@ class Caribou:
                     if debug == True:
                         print "leave entry widget in", event.host_application.name
             else:
-                print _("WARNING - Caribou: unhandled editable widget:"), event.source         
+                if debug == True:
+                    print _("WARNING - Caribou: unhandled editable widget:"), event.source         
 
-        # Firefox does report leave entry widget events.
+        # 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:
@@ -180,3 +181,5 @@ class Caribou:
         if debug == True:
             print "deregisterKeystrokeListener"
         gtk.main_quit()
+        
+        
diff --git a/caribou/ui/scan.py b/caribou/ui/scan.py
new file mode 100644
index 0000000..0964af1
--- /dev/null
+++ b/caribou/ui/scan.py
@@ -0,0 +1,438 @@
+import gobject
+import pyatspi
+import gtk
+import caribou.common.const as const
+import gconf
+
+class Scan_service():
+    def __init__(self, keyboard, root_window):
+        self.keyboard = keyboard
+        self.root_window = root_window
+        self.selected_row = None
+        self.selected_button = None
+        self.button_index = 0
+        self.row_index = 0
+        self.index_i = 0
+        self.index_j = 0
+        self.is_stop = True
+        self.timerid = None
+        self.reverse = False
+        self.selected_block = []
+        self.client = gconf.client_get_default()
+        self._gconf_connections = []
+        self._gconf_connections.append(self.client.notify_add(
+                            const.CARIBOU_GCONF + "/scanning_type", 
+                            self._on_scanning_type_changed))
+        self._gconf_connections.append(self.client.notify_add(
+                            const.CARIBOU_GCONF + "/step_time", 
+                            self._on_scanning_type_changed))
+        self._gconf_connections.append(self.client.notify_add(
+                            const.CARIBOU_GCONF + "/reverse_scanning", 
+                            self._on_scanning_type_changed))
+        self._gconf_connections.append(self.client.notify_add(
+                            const.CARIBOU_GCONF + "/switch_type",
+                            self._on_switch_changed))
+        self._gconf_connections.append(self.client.notify_add(
+                            const.CARIBOU_GCONF + "/mouse_button",
+                            self._on_switch_changed))
+        self._gconf_connections.append(self.client.notify_add(
+                            const.CARIBOU_GCONF + "/keyboard_key", 
+                            self._on_switch_changed))
+
+        self._gconf_connections.append(self.client.notify_add(
+                            const.CARIBOU_GCONF + "/default_colors",
+                            self._on_color_changed))
+        self._gconf_connections.append(self.client.notify_add(
+                            const.CARIBOU_GCONF + "/normal_color", 
+                            self._on_color_changed))
+        self._gconf_connections.append(self.client.notify_add(
+                            const.CARIBOU_GCONF + "/mouse_over_color", 
+                            self._on_color_changed))
+        self._gconf_connections.append(self.client.notify_add(
+                            const.CARIBOU_GCONF + "/row_scanning_color", 
+                            self._on_color_changed))
+        self._gconf_connections.append(self.client.notify_add(
+                            const.CARIBOU_GCONF + "/button_scanning_color", 
+                            self._on_color_changed))
+        self._gconf_connections.append(self.client.notify_add(
+                            const.CARIBOU_GCONF + "/cancel_scanning_color", 
+                            self._on_color_changed))
+        self._gconf_connections.append(self.client.notify_add(
+                            const.CARIBOU_GCONF + "/block_scanning_color", 
+                            self._on_color_changed))
+        self._configure_scanning()
+        self._set_colors()
+        self._configure_switch()
+
+    def destroy(self):
+        self.stop()
+        self.clean()
+        self._deregister_events()
+        for id in self._gconf_connections:
+            self.client.notify_remove(id)
+
+
+
+    def _configure_switch(self):
+        self.switch_type = self.client.get_string(const.CARIBOU_GCONF + 
+                                                  "/switch_type") or "mouse"
+        if self.switch_type == const.MOUSE_SWITCH_TYPE:
+            self.switch_key = self.client.get_string(const.CARIBOU_GCONF + 
+                                                     "/mouse_button") or "2"
+        elif self.switch_type == const.KEYBOARD_SWITCH_TYPE:
+            self.switch_key = self.client.get_string(const.CARIBOU_GCONF +
+                                                     "/keyboard_key") or "Shift_L"
+        try:
+            pyatspi.Registry.registerKeystrokeListener(self._on_key_pressed, 
+                                mask=None, kind=(pyatspi.KEY_PRESSED_EVENT,))
+            pyatspi.Registry.registerKeystrokeListener(self._on_key_released, 
+                                mask=None, kind=(pyatspi.KEY_RELEASED_EVENT,))
+        except:
+            print "Error while registering keyboard events in scan.py"
+
+    def _deregister_events(self):
+        try:
+            pyatspi.Registry.deregisterKeystrokeListener(self._on_key_pressed, 
+                                mask=None, kind=(pyatspi.KEY_PRESSED_EVENT,))
+            pyatspi.Registry.deregisterKeystrokeListener(
+                                self._on_key_released, 
+                                mask=None, kind=(pyatspi.KEY_RELEASED_EVENT,))
+        except:
+            print "Error while deregistering keyboard events in scan.py"
+
+    def _on_switch_changed(self, client, connection_id, entry, args):
+        self._deregister_events()
+        self._configure_switch()
+
+    def _on_color_changed(self, client, connection_id, entry, args):
+        self._set_colors()
+
+    def _on_scanning_type_changed(self, client, connection_id, entry, args):
+        self._configure_scanning()
+
+    def _configure_scanning(self):
+        if not self.is_stop:
+            self._stop()
+        self.scanning_type = self.client.get_string(const.CARIBOU_GCONF +
+                                                    "/scanning_type") or "0"
+        self.reverse = self.client.get_bool(const.CARIBOU_GCONF +
+                                                    "/reverse_scanning") or False
+        self.step_time = self.client.get_int(const.CARIBOU_GCONF + 
+                                                    "/step_time") or 1000
+        if self.scanning_type == const.BLOCK:
+            self.selected_block = []
+        else:
+            self.selected_block = self.keyboard
+        self.scanning = self.scanning_type
+
+    def _set_colors(self):
+        self.default_colors = self.client.get_bool(
+                                const.CARIBOU_GCONF + "/default_colors") \
+                                or True
+        self.normal_color = self.client.get_string(
+                                const.CARIBOU_GCONF + "/normal_color") \
+                                or "gray80"
+        self.mouse_over_color = self.client.get_string(
+                                const.CARIBOU_GCONF + "/mouse_over_color") \
+                                or "yellow"
+
+        self.row_scanning_color = self.client.get_string(
+                                const.CARIBOU_GCONF + "/row_scanning_color") \
+                                or "green"
+        self.button_scanning_color = self.client.get_string(
+                            const.CARIBOU_GCONF + "/button_scanning_color") \
+                            or "cyan"
+        self.cancel_scanning_color = self.client.get_string(
+                            const.CARIBOU_GCONF + "/cancel_scanning_color") \
+                            or "red"
+        self.block_scanning_color = self.client.get_string(
+                            const.CARIBOU_GCONF + "/block_scanning_color") \
+                            or "purple" 
+
+
+    # public start
+    def start(self, scanning=None):
+        self.scanning = scanning or self.scanning_type
+        self.clean()
+        if self.root_window and \
+            self.switch_type == const.MOUSE_SWITCH_TYPE:
+            self._grab_mouse_events()
+
+        if not self.reverse:
+            self.reset(self.scanning)
+
+    # public stop
+    def stop(self):
+        if self.switch_type == const.MOUSE_SWITCH_TYPE:
+            self._ungrab_mouse_events()
+        self.clean()
+        self._stop()
+
+    #private start
+    def _start(self, scanning=const.ROW):
+        if self.is_stop == True and self.timerid == None:
+            self.is_stop = False
+            self.button_index = -1
+            self.row_index = -1
+            if scanning == const.ROW:
+                self.selected_row = []
+                self.timerid = gobject.timeout_add(self.step_time, self._scan_row)
+            elif scanning == const.BUTTON:
+                self.selected_button = None
+                self.timerid = gobject.timeout_add(self.step_time, self._scan_button)
+            elif scanning == const.BLOCK:
+                self.selected_block = []
+                self.selected_row = []
+                self.clean()
+                self.index_i = 2
+                self.index_j = 1
+                self.timerid = gobject.timeout_add(self.step_time, self._scan_block)
+
+    # private stop
+    def _stop(self):
+        self.is_stop = True
+        if self.timerid:
+            gobject.source_remove(self.timerid)
+            self.timerid = None
+
+    def reset(self, scanning=const.ROW):
+        self._stop()
+        self._start(scanning)
+
+    def clean(self):
+        for row in self.keyboard:
+            for button in row:
+                self.select_button(button, False)
+
+
+    def change_keyboard(self, keyboard):
+        if not self.is_stop:
+            self._stop()
+        self.keyboard = keyboard
+        self.scanning = self.scanning_type
+        self.start(self.scanning_type)
+        if self.scanning == const.ROW:
+            self.selected_block = keyboard
+
+
+    def _grab_mouse_events(self):
+        gtk.gdk.event_handler_set(self._mouse_handler)
+
+    def _ungrab_mouse_events(self):
+        gtk.gdk.event_handler_set(gtk.main_do_event)
+
+    def _mouse_handler(self, event):
+        if self.root_window.window.is_visible():
+            if event.type == gtk.gdk.BUTTON_PRESS and \
+                str(event.button) == self.switch_key:
+                self._handle_press()
+            elif event.type == gtk.gdk.BUTTON_RELEASE and \
+                str(event.button) == self.switch_key:
+                self._handle_release()
+            elif not event.type == gtk.gdk.ENTER_NOTIFY:
+                gtk.main_do_event(event)
+        else:
+            gtk.main_do_event(event)
+
+    def _scan_block(self):
+        if self.is_stop:
+            return False
+        # Clean the previous block
+        self.select_block(self.selected_block, False)
+        # Update indexes, three horizontal blocks, and two vertical
+        if self.index_j < 2:
+            self.index_j += 1
+        elif self.index_i < 1:
+            self.index_i += 1
+            self.index_j = 0
+        else:
+            self.index_j = 0
+            self.index_i = 0
+
+        self.selected_block = []
+        width = self.root_window.size_request()[0]
+        height = self.root_window.size_request()[1]
+        root_x = self.root_window.get_position()[0]
+        root_y = self.root_window.get_position()[1]
+        offset_w = self.index_j*(width/3)
+        offset_h = self.index_i*(height/2)
+
+        block_window = gtk.gdk.Rectangle(root_x + offset_w, 
+                                         root_y + offset_h, 
+                                         width/3, 
+                                         height/2)
+        empty_r = gtk.gdk.Rectangle()
+        try:
+            for row in self.keyboard:
+                line = []
+                for button in row:
+                    abs_b_x = button.get_allocation()[0] + \
+                              button.window.get_position()[0]
+                    abs_b_y = button.get_allocation()[1] + \
+                              button.window.get_position()[1]
+                    abs_b_r = gtk.gdk.Rectangle(abs_b_x, 
+                                               abs_b_y,
+                                               button.size_request()[0],
+                                               button.size_request()[1])
+
+            # If button rectangle is inside the block:
+                    intersect = block_window.intersect(abs_b_r)
+                # If the intersected rectangle != empty
+                    if intersect != empty_r:
+                        # If the witdth of intersection is bigger than half 
+                        # of button width and height, we append button to line
+                        if (intersect.width > (abs_b_r.width / 2)) and \
+                           (intersect.height > (abs_b_r.height / 2)):
+                           line.append(button)
+
+
+                if len(line) > 0:
+                    self.selected_block.append(line)
+                    
+        except Exception as e:
+            self.is_stop = True
+            return False
+            
+        self.select_block(self.selected_block, True,
+                           self.block_scanning_color)
+        return True       
+                                               
+
+
+    def _scan_row(self):
+        if self.is_stop:
+            return False
+
+        else:
+            self.select_row(self.selected_row, 
+                            self.scanning_type == const.BLOCK, 
+                            self.block_scanning_color)
+            self.row_index += 1
+            if self.row_index >= len(self.selected_block):
+                self.row_index = 0
+            self.selected_row = self.selected_block[self.row_index]
+            self.select_row(self.selected_row, True, self.row_scanning_color)
+            return True
+
+    def _scan_button(self):
+        if self.scanning == const.CANCEL:
+            self.scanning = const.BUTTON
+            self.selected_button = None
+            self.select_row(self.selected_row, True, self.row_scanning_color)
+            return True
+        else:
+            if self.selected_button and self.selected_button in self.selected_row:
+                self.select_button(self.selected_button, True, self.row_scanning_color)
+
+            if self.is_stop:
+                return False
+
+            self.button_index += 1
+            if self.button_index >= len(self.selected_row):
+                self.select_row(self.selected_row, True, self.cancel_scanning_color)
+                self.button_index = -1
+                self.scanning = const.CANCEL
+                return True
+
+            self.selected_button = self.selected_row[self.button_index]
+            while self.selected_button.key_type == const.DUMMY_KEY_TYPE:
+                self.button_index += 1
+                if self.button_index >= len(self.selected_row):
+                    self.select_row(self.selected_row, True, self.cancel_scanning_color)
+                    self.button_index = -1
+                    self.scanning = const.CANCEL
+                    return True
+
+                self.selected_button = self.selected_row[self.button_index]
+            self.select_button(self.selected_button, True, self.button_scanning_color)
+            return True
+
+    def select_block(self, block, state, color=None):
+        for row in block:
+            self.select_row(row, state, color)
+
+    def select_row(self, row, state, color=None):
+        for button in row:
+            self.select_button(button, state, color)
+
+    def select_button(self, button, state, color=None):
+        if state:
+            button.set_color(color, self.mouse_over_color)
+        elif self.default_colors:
+            button.reset_color()
+        else:
+            button.set_color(self.normal_color, self.mouse_over_color)
+
+
+    def _on_key_pressed(self, event):
+        if event.event_string == "Escape":
+            self.stop()
+        elif self.switch_type == const.KEYBOARD_SWITCH_TYPE and \
+             self.switch_key == event.event_string:
+            self._handle_press()
+
+    def _on_key_released(self, event):
+            if self.switch_type == const.KEYBOARD_SWITCH_TYPE and \
+                 self.switch_key == event.event_string:
+                self._handle_release()
+            elif event.event_string != "Escape": 
+                self._stop()
+                self.start()
+
+    def _handle_press(self):
+        if self.reverse:
+            self._start(self.scanning)
+
+    def _handle_release(self):
+        if self.reverse:
+            if not self.is_stop:
+                if self.scanning == const.ROW and \
+                        len(self.selected_row) > 0:
+                    self.scanning = const.BUTTON
+                elif self.scanning == const.BLOCK and \
+                        len(self.selected_block) > 0:
+                    self.scanning = const.ROW
+                elif self.scanning == const.BUTTON and \
+                        self.selected_button:
+                    self.clean()
+                    if self.selected_button.key_type == const.PREFERENCES_KEY_TYPE:
+                        self.stop()
+                    self.selected_button.clicked()
+                    self.selected_button = None
+                    self.scanning = self.scanning_type
+                    self.reset()
+
+                elif self.scanning == const.CANCEL:
+                    self.clean()
+                    self.scanning = self.scanning_type
+                self._stop()
+
+        else:
+            if not self.is_stop:
+                if self.scanning == const.ROW and \
+                        len(self.selected_row) > 0:
+                    self.scanning = const.BUTTON
+                    self.reset(const.BUTTON)
+
+                elif self.scanning == const.BLOCK and \
+                        len(self.selected_block) > 0:
+                    self.scanning = const.ROW
+                    self.reset(const.ROW)
+                elif self.scanning == const.BUTTON and \
+                        self.selected_button:
+                    self.selected_button.clicked()
+                    self.scanning = const.ROW
+                    if self.selected_button.key_type \
+                            == const.PREFERENCES_KEY_TYPE:
+                        self.selected_button = None
+                        self.stop()
+                    else: 
+                        self.selected_button = None
+                        self.scanning = self.scanning_type
+
+                elif self.scanning == const.CANCEL:
+                    self.scanning = self.scanning_type
+                    self.clean()
+                    self.reset(self.scanning_type)
+
+               
diff --git a/caribou/ui/window.py b/caribou/ui/window.py
index 08d3e2f..0ed5c90 100644
--- a/caribou/ui/window.py
+++ b/caribou/ui/window.py
@@ -59,6 +59,11 @@ class CaribouWindow(gtk.Window):
 
         self.connect('show', self._on_window_show)
 
+    def destroy(self):
+        self.keyboard.destroy()
+        super(gtk.Window, self).destroy()
+
+
     def set_cursor_location(self, cursor_location):
         self._cursor_location = cursor_location
         self._update_position()
@@ -225,6 +230,7 @@ class CaribouWindowEntry(CaribouWindow):
         CaribouWindow.__init__(self, text_entry_mech, placement, min_alpha=0.075,
                                max_alpha=0.8)
 
+
     def _calculate_axis(self, axis_placement, root_bbox):
         offset = CaribouWindow._calculate_axis(self, axis_placement, root_bbox)
 
diff --git a/data/caribou-prefs.ui b/data/caribou-prefs.ui
index 97fcec0..bdc75e9 100644
--- a/data/caribou-prefs.ui
+++ b/data/caribou-prefs.ui
@@ -322,10 +322,290 @@
               </packing>
             </child>
             <child>
-              <placeholder/>
+              <object class="GtkHBox" id="hbox4">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkTable" id="table3">
+                    <property name="visible">True</property>
+                    <property name="n_rows">10</property>
+                    <property name="n_columns">2</property>
+                    <child>
+                      <object class="GtkCheckButton" id="scan_enabled_checkbox">
+                        <property name="label" translatable="yes">_Scan enabled</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="right_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton" id="reverse_scanning_checkbox">
+                        <property name="label" translatable="yes">Re_verse scanning</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label11">
+                        <property name="visible">True</property>
+                        <property name="xalign">0.05000000074505806</property>
+                        <property name="label" translatable="yes">Ste_p time:</property>
+                        <property name="use_underline">True</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">9</property>
+                        <property name="bottom_attach">10</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSpinButton" id="step_time_spin">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="invisible_char">&#x25CF;</property>
+                        <property name="adjustment">adjustment1</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">9</property>
+                        <property name="bottom_attach">10</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="keyboard_switch_radio">
+                        <property name="label" translatable="yes">_Keyboard switch</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">mouse_switch_radio</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">8</property>
+                        <property name="bottom_attach">9</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkComboBox" id="keyboard_key_combo">
+                        <property name="visible">True</property>
+                        <property name="model">liststore4</property>
+                        <child>
+                          <object class="GtkCellRendererCombo" id="cellrenderertext4"/>
+                          <attributes>
+                            <attribute name="text">0</attribute>
+                          </attributes>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">8</property>
+                        <property name="bottom_attach">9</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkComboBox" id="mouse_button_combo">
+                        <property name="visible">True</property>
+                        <property name="model">liststore3</property>
+                        <child>
+                          <object class="GtkCellRendererCombo" id="cellrenderertext3"/>
+                          <attributes>
+                            <attribute name="text">0</attribute>
+                          </attributes>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">7</property>
+                        <property name="bottom_attach">8</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="mouse_switch_radio">
+                        <property name="label" translatable="yes">_Mouse switch</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">7</property>
+                        <property name="bottom_attach">8</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkComboBox" id="scanning_type_combo">
+                        <property name="visible">True</property>
+                        <property name="model">liststore2</property>
+                        <child>
+                          <object class="GtkCellRendererCombo" id="cellrenderertext2"/>
+                          <attributes>
+                            <attribute name="text">0</attribute>
+                          </attributes>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">6</property>
+                        <property name="bottom_attach">7</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label10">
+                        <property name="visible">True</property>
+                        <property name="xalign">0.05000000074505806</property>
+                        <property name="label" translatable="yes">Scanning _type: </property>
+                        <property name="use_underline">True</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">6</property>
+                        <property name="bottom_attach">7</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkColorButton" id="cancel_scanning_color_button">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="color">#000000000000</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">5</property>
+                        <property name="bottom_attach">6</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label9">
+                        <property name="visible">True</property>
+                        <property name="xalign">0.05000000074505806</property>
+                        <property name="label" translatable="yes">_Cancel color: </property>
+                        <property name="use_underline">True</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">5</property>
+                        <property name="bottom_attach">6</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkColorButton" id="button_scanning_color_button">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="color">#000000000000</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">4</property>
+                        <property name="bottom_attach">5</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label8">
+                        <property name="visible">True</property>
+                        <property name="xalign">0.05000000074505806</property>
+                        <property name="label" translatable="yes">_Button color: </property>
+                        <property name="use_underline">True</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">4</property>
+                        <property name="bottom_attach">5</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkColorButton" id="row_scanning_color_button">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="color">#000000000000</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">3</property>
+                        <property name="bottom_attach">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label6">
+                        <property name="visible">True</property>
+                        <property name="xalign">0.05000000074505806</property>
+                        <property name="label" translatable="yes">_Row color: </property>
+                        <property name="use_underline">True</property>
+                        <property name="ellipsize">start</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">3</property>
+                        <property name="bottom_attach">4</property>
+                        <property name="x_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label12">
+                        <property name="visible">True</property>
+                        <property name="xalign">0.05000000074505806</property>
+                        <property name="label" translatable="yes">B_lock color: </property>
+                        <property name="use_underline">True</property>
+                        <property name="ellipsize">start</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                        <property name="x_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkColorButton" id="block_scanning_color_button">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="color">#000000000000</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">3</property>
+              </packing>
             </child>
             <child type="tab">
-              <placeholder/>
+              <object class="GtkLabel" id="label5">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Scanning</property>
+              </object>
+              <packing>
+                <property name="position">3</property>
+                <property name="tab_fill">False</property>
+              </packing>
             </child>
           </object>
           <packing>
@@ -368,4 +648,29 @@
       <action-widget response="0">button_close</action-widget>
     </action-widgets>
   </object>
+  <object class="GtkListStore" id="liststore2">
+    <columns>
+      <!-- column-name gchararray1 -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="liststore3">
+    <columns>
+      <!-- column-name gchararray1 -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="liststore4">
+    <columns>
+      <!-- column-name gchararray1 -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkAdjustment" id="adjustment1">
+    <property name="value">1000</property>
+    <property name="lower">100</property>
+    <property name="upper">10000</property>
+    <property name="step_increment">10</property>
+    <property name="page_increment">100</property>
+  </object>
 </interface>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]