summaryrefslogtreecommitdiff
path: root/caribou/ui/scan.py
diff options
context:
space:
mode:
Diffstat (limited to 'caribou/ui/scan.py')
-rw-r--r--caribou/ui/scan.py515
1 files changed, 137 insertions, 378 deletions
diff --git a/caribou/ui/scan.py b/caribou/ui/scan.py
index f845903..55feabf 100644
--- a/caribou/ui/scan.py
+++ b/caribou/ui/scan.py
@@ -5,7 +5,7 @@ from gi.repository import Gtk
import caribou.common.const as const
from caribou.common.settings_manager import SettingsManager
-# Scan constans
+# Scan constants
BUTTON = 'button'
ROW = 'row'
BLOCK = 'block'
@@ -27,400 +27,159 @@ DEFAULT_STEP_TIME = 1000
DEFAULT_SCANNING_TYPE = ROW
DEFAULT_SWITCH_TYPE = KEYBOARD_SWITCH_TYPE
-class ScanService():
- def __init__(self, keyboard, root_window):
- self.keyboard = keyboard
+class ScanMaster():
+ def __init__(self, root_window, keyboard=None):
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 = []
-
- # Settings we are interested in.
- for name in ["step_time", "reverse_scanning", "scanning_type"]:
- getattr(SettingsManager, name).connect("value-changed",
- self._on_switch_changed)
-
- for name in ["switch_type", "mouse_button", "keyboard_key"]:
- getattr(SettingsManager, name).connect("value-changed",
- self._on_switch_changed)
-
- for name in ["default_colors", "normal_color", "mouse_over_color",
- "row_scanning_color", "button_scanning_color",
- "cancel_scanning_color", "block_scanning_color"]:
- getattr(SettingsManager, name).connect("value-changed",
- self._on_color_changed)
-
- SettingsManager.scan_enabled.connect("value-changed",
- self._on_scan_toggled)
-
- self._configure_scanning()
- self._set_colors()
- self._configure_switch()
-
- def destroy(self):
- self.stop()
- self.clean()
- self._deregister_events()
-
-
- def _configure_switch(self):
- self.switch_type = SettingsManager.switch_type.value
- if self.switch_type == MOUSE_SWITCH_TYPE:
- self.switch_key = SettingsManager.mouse_button.value
- elif self.switch_type == KEYBOARD_SWITCH_TYPE:
- self.switch_key = SettingsManager.keyboard_key
+ self._timer = 0
+ self._scan_path = None
+ self.started = False
- 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, settings, val):
- self._deregister_events()
- self._configure_switch()
-
- def _on_scan_toggled(self, settings, val):
- if val:
- self.start()
- else:
- self.stop()
+ SettingsManager.step_time.connect("value-changed",
+ self._on_step_time_changed)
+ SettingsManager.scanning_type.connect("value-changed",
+ self._on_scanning_type_changed)
+ if keyboard:
+ self.set_keyboard(keyboard)
- def _on_color_changed(self, settings, val):
- self._set_colors()
+ def start(self):
+ if self.started: return
- def _on_scanning_type_changed(self, settings, val):
- self._configure_scanning()
+ if self._timer == 0:
+ self._timer = gobject.timeout_add(
+ int(SettingsManager.step_time.value*1000), self._scan)
- def _configure_scanning(self):
- if not self.is_stop:
- self._stop()
- self.scanning_type = SettingsManager.scanning_type.value
- self.reverse = SettingsManager.reverse_scanning.value
- self.step_time = SettingsManager.step_time.value
+ self._grab_mouse_events()
+
+ pyatspi.Registry.registerKeystrokeListener(
+ self._on_key_pressed, mask=0, kind=(pyatspi.KEY_PRESSED_EVENT,))
- if self.scanning_type == BLOCK:
- self.selected_block = []
- else:
- self.selected_block = self.keyboard
- self.scanning = self.scanning_type
-
- def _set_colors(self):
- self.default_colors = SettingsManager.default_colors.value
- self.normal_color = SettingsManager.normal_color.value
- self.mouse_over_color = SettingsManager.mouse_over_color.value
- self.row_scanning_color = SettingsManager.row_scanning_color.value
- self.button_scanning_color = \
- SettingsManager.button_scanning_color.value
- self.cancel_scanning_color = \
- SettingsManager.cancel_scanning_color.value
- self.block_scanning_color = SettingsManager.block_scanning_color.value
-
- # public start
- def start(self, scanning=None):
- self.scanning = scanning or self.scanning_type
- self.clean()
- if self.root_window and \
- self.switch_type == MOUSE_SWITCH_TYPE:
- self._grab_mouse_events()
-
- if not self.reverse:
- self.reset(self.scanning)
-
- # public stop
def stop(self):
- if self.switch_type == MOUSE_SWITCH_TYPE:
- self._ungrab_mouse_events()
- self.clean()
- self._stop()
-
- #private start
- def _start(self, scanning=ROW):
- if self.is_stop == True and self.timerid == None:
- self.is_stop = False
- self.button_index = -1
- self.row_index = -1
- if scanning == ROW:
- self.selected_row = []
- self.timerid = gobject.timeout_add(
- int(1000*self.step_time), self._scan_row)
- elif scanning == BUTTON:
- self.selected_button = None
- self.timerid = gobject.timeout_add(
- int(1000*self.step_time), self._scan_button)
- elif scanning == BLOCK:
- self.selected_block = []
- self.selected_row = []
- self.clean()
- self.index_i = 2
- self.index_j = 1
- self.timerid = gobject.timeout_add(
- int(1000*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=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 == ROW:
- self.selected_block = keyboard
+ if self.started: return
+ self._ungrab_mouse_events()
- def _grab_mouse_events(self):
- Gdk.event_handler_set(self._mouse_handler)
+ if self._last_block is not None:
+ self._multi_map(lambda x: x.reset_color(), self._last_block)
- def _ungrab_mouse_events(self):
- Gdk.event_handler_set(Gtk.main_do_event)
+ if self._timer != 0:
+ gobject.source_remove(self._timer)
+ self._timer = 0
- def _mouse_handler(self, event):
- if self.root_window.window.is_visible():
- if event.type == Gdk.EventType.BUTTON_PRESS and \
- str(event.button) == self.switch_key.value:
- self._handle_press()
- elif event.type == Gdk.BUTTON_RELEASE and \
- str(event.button) == self.switch_key.value:
- self._handle_release()
- elif not event.type == Gdk.ENTER_NOTIFY:
- Gtk.main_do_event(event)
- else:
- Gtk.main_do_event(event)
+ pyatspi.Registry.deregisterKeystrokeListener(
+ self._on_key_pressed, mask=0, kind=pyatspi.KEY_PRESSED_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
+ def _on_scanning_type_changed(self, setting, val):
+ layout = self.keyboard.get_current_layout()
+ if SettingsManager.scanning_type.value == ROW:
+ self._blocks = layout.get_scan_rows()
else:
- self.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 = (root_x + offset_w,
- root_y + offset_h,
- width/3,
- height/2)
- empty_r = ()
- 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 = (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
+ self._blocks = layout.get_scan_blocks()
+ def _on_step_time_changed(self, setting, val):
+ if self._timer != 0:
+ gobject.source_remove(self._timer)
+ self._timer = gobject.timeout_add(int(1000*val), self._scan)
+
+ def _on_layout_activated(self, keyboard, layout, num):
+ if SettingsManager.scanning_type.value == ROW:
+ self._blocks = layout.get_scan_rows()
else:
- self.select_row(self.selected_row,
- self.scanning_type == 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 == CANCEL:
- self.scanning = BUTTON
- self.selected_button = None
- self.select_row(self.selected_row, True, self.row_scanning_color)
- return True
+ self._blocks = layout.get_scan_blocks()
+ self._scan_path = [-1]
+
+ def set_keyboard(self, keyboard):
+ self._last_block = None
+ keyboard.connect("switch-page", self._on_layout_activated)
+ self.keyboard = keyboard
+
+ def _multi_map(self, func, array):
+ if isinstance(array, list):
+ for item in array:
+ self._multi_map(func, item)
else:
- 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 = 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 = 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()
+ func(array)
+
+ def _get_element_at_path(self, array, path):
+ element = array
+ for index in path:
+ element = element[index]
+ return element
+
+ def _get_next_block(self):
+ cancel = False
+ self._scan_path += [self._scan_path.pop() + 1]
+ try:
+ block = self._get_element_at_path(self._blocks,
+ self._scan_path)
+ except IndexError:
+ if len(self._scan_path) == 1:
+ block = self._blocks[0]
+ self._scan_path = [0]
+ else:
+ block = self._get_element_at_path(
+ self._blocks, self._scan_path[:-1])
+ self._scan_path = self._scan_path[:-1] + [-1]
+ cancel = True
+
+ return cancel, block
+
+ def _scan(self):
+ if self._scan_path is None: return True
+
+ if self._last_block is not None:
+ self._multi_map(lambda x: x.reset_color(),
+ self._last_block)
+
+ self._cancel, next_block = self._get_next_block()
+
+ color = SettingsManager.button_scanning_color.value
+
+ if self._cancel:
+ color = SettingsManager.cancel_scanning_color.value
+ elif isinstance(next_block, list):
+ if SettingsManager.scanning_type.value == ROW:
+ color = SettingsManager.row_scanning_color.value
+ else:
+ color = SettingsManager.block_scanning_color.value
+
+ self._multi_map(lambda x: x.set_color(color, None), next_block)
+ self._last_block = next_block
+ return True
+
+ def _do_switch(self):
+ if self._cancel:
+ assert(len(self._scan_path) > 1)
+ self._scan_path.pop()
+ elif isinstance(self._last_block, list):
+ assert(len(self._last_block) > 0)
+ self._scan_path.append(-1)
else:
- button.set_color(self.normal_color,
- self.mouse_over_color)
+ self._last_block.clicked()
- def _on_key_pressed(self, event):
- if event.event_string == "Escape":
- SettingsManager.scan_enabled.value = False
- elif self.switch_type == KEYBOARD_SWITCH_TYPE and \
- self.switch_key.value == event.event_string:
- self._handle_press()
-
- def _on_key_released(self, event):
- if self.switch_type == KEYBOARD_SWITCH_TYPE and \
- self.switch_key.value == 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 == ROW and \
- len(self.selected_row) > 0:
- self.scanning = BUTTON
- elif self.scanning == BLOCK and \
- len(self.selected_block) > 0:
- self.scanning = ROW
- elif self.scanning == 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 == CANCEL:
- self.clean()
- self.scanning = self.scanning_type
- self._stop()
+ def _grab_mouse_events(self):
+ Gdk.event_handler_set(self._mouse_handler, None)
+
+ def _ungrab_mouse_events(self):
+ Gdk.event_handler_set(Gtk.main_do_event, None)
+
+ def _mouse_handler(self, event, user_data):
+ if SettingsManager.switch_type.value != MOUSE_SWITCH_TYPE or \
+ event.type not in (Gdk.EventType.BUTTON_PRESS,
+ Gdk.EventType.ENTER_NOTIFY):
+ Gtk.main_do_event(event)
+ return
+ if self.root_window.get_window().is_visible():
+ if event.type == Gdk.EventType.BUTTON_PRESS and \
+ str(event.button.button) == \
+ SettingsManager.mouse_button.value:
+ self._do_switch()
+ elif event.type != Gdk.EventType.ENTER_NOTIFY:
+ Gtk.main_do_event(event)
else:
- if not self.is_stop:
- if self.scanning == ROW and \
- len(self.selected_row) > 0:
- self.scanning = BUTTON
- self.reset(BUTTON)
-
- elif self.scanning == BLOCK and \
- len(self.selected_block) > 0:
- self.scanning = ROW
- self.reset(ROW)
- elif self.scanning == BUTTON and \
- self.selected_button:
- self.selected_button.clicked()
- self.scanning = 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 == CANCEL:
- self.scanning = self.scanning_type
- self.clean()
- self.reset(self.scanning_type)
-
-
+ Gtk.main_do_event(event)
+
+ def _on_key_pressed(self, event):
+ if SettingsManager.switch_type.value == KEYBOARD_SWITCH_TYPE and \
+ SettingsManager.keyboard_key.value == event.event_string:
+ self._do_switch()