349 lines
14 KiB
Python
349 lines
14 KiB
Python
#
|
|
# This file is a command-module for Dragonfly.
|
|
# (c) Copyright 2008 by Christo Butcher
|
|
# Licensed under the LGPL, see <http://www.gnu.org/licenses/>
|
|
#
|
|
|
|
"""
|
|
Command-module for **Firefox**
|
|
============================================================================
|
|
|
|
This module offers direct control of the `Firefox
|
|
<http://www.mozilla.com/en-US/firefox/>`_ web browser. It
|
|
requires the `mouseless-browsing
|
|
<https://addons.mozilla.org/en-US/firefox/addon/879>`_
|
|
(mlb) add-on for reliable access to hyperlinks.
|
|
|
|
This module includes direct searching using Firefox's
|
|
search bar and Firefox's keyword searching. It also
|
|
allows single-utterance submitting of text into form text
|
|
fields.
|
|
|
|
Installation
|
|
----------------------------------------------------------------------------
|
|
|
|
If you are using DNS and Natlink, simply place this file in you Natlink
|
|
macros directory. It will then be automatically loaded by Natlink when
|
|
you next toggle your microphone or restart Natlink.
|
|
|
|
Customization
|
|
----------------------------------------------------------------------------
|
|
|
|
Users should customize this module by editing its
|
|
configuration file. In this file they should edit the
|
|
``search.searchbar`` and ``search.keywords`` settings to
|
|
match their own personal search preferences. These
|
|
variables map *what you say* to which *search engines* to
|
|
use.
|
|
|
|
"""
|
|
|
|
from dragonfly import *
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Set up this module's configuration.
|
|
|
|
config = Config("Firefox control")
|
|
config.search = Section("Search-related section")
|
|
config.search.keywords = Item(
|
|
default={
|
|
"wikipedia": "wikipedia",
|
|
},
|
|
doc="Mapping of spoken-forms to Firefox search-keywords.",
|
|
)
|
|
config.search.searchbar = Item(
|
|
default=[
|
|
"google",
|
|
"yahoo",
|
|
"amazon",
|
|
"answers",
|
|
"creative commons",
|
|
"eBay",
|
|
"wikipedia",
|
|
],
|
|
doc="Spoken-forms of search engines in the Firefox search-bar; they must be given in the same order here as they are available in Firefox.",
|
|
)
|
|
|
|
config.lang = Section("Language section")
|
|
config.lang.new_win = Item("new (window | win)")
|
|
config.lang.new_tab = Item("new (tab | sub)")
|
|
config.lang.close_tab = Item("close (tab | sub)")
|
|
config.lang.close_tab_n = Item("close (tab | sub) <n>")
|
|
config.lang.close_n_tabs = Item("close <n> (tabs | subs)")
|
|
config.lang.address_bar = Item("address [bar]")
|
|
config.lang.copy_address = Item("copy address")
|
|
config.lang.paste_address = Item("paste address")
|
|
config.lang.search_bar = Item("search bar")
|
|
config.lang.go_home = Item("go home")
|
|
config.lang.stop_loading = Item("stop loading")
|
|
config.lang.toggle_tags = Item("toggle tags")
|
|
config.lang.fresh_tags = Item("fresh tags")
|
|
config.lang.caret_browsing = Item("(caret | carrot) browsing")
|
|
config.lang.bookmark_page = Item("bookmark [this] page")
|
|
config.lang.save_page_as = Item("save [page | file] as")
|
|
config.lang.print_page = Item("print [page | file]")
|
|
config.lang.show_tab_n = Item("show tab <n>")
|
|
config.lang.back = Item("back [<n>]")
|
|
config.lang.forward = Item("forward [<n>]")
|
|
config.lang.next_tab = Item("next tab [<n>]")
|
|
config.lang.prev_tab = Item("(previous | preev) tab [<n>]")
|
|
config.lang.normal_size = Item("normal text size")
|
|
config.lang.smaller_size = Item("smaller text size [<n>]")
|
|
config.lang.bigger_size = Item("bigger text size [<n>]")
|
|
config.lang.find = Item("find")
|
|
config.lang.find_text = Item("find <text>")
|
|
config.lang.find_next = Item("find next [<n>]")
|
|
|
|
config.lang.submit = Item("submit")
|
|
config.lang.submit_text = Item("submit <text>")
|
|
config.lang.submit_clipboard = Item("submit (clipboard | clip board)")
|
|
config.lang.link_open = Item("[link] <link> [open]")
|
|
config.lang.link_save = Item("save [link] <link> [as]")
|
|
config.lang.link_save_now = Item("save [link] <link> now now")
|
|
config.lang.link_select = Item("[link] <link> select")
|
|
config.lang.link_menu = Item("[link] <link> (menu | pop up)")
|
|
config.lang.link_force = Item("[link] <link> force")
|
|
config.lang.link_window = Item("[link] <link> [in [new]] window")
|
|
config.lang.link_tab = Item("[link] <link> [in [new]] tab")
|
|
config.lang.link_copy = Item("[link] <link> copy")
|
|
config.lang.link_copy_into_tab = Item("[link] <link> copy into tab")
|
|
config.lang.link_list = Item("[link] <link> list")
|
|
config.lang.link_submit = Item("[link] <link> submit")
|
|
config.lang.link_submit_text = Item("[link] <link> submit <text>")
|
|
config.lang.link_submit_clipboard = Item("[link] <link> submit (clipboard | clip board)")
|
|
config.lang.link_dictation_box = Item("edit [link] <link>")
|
|
config.lang.link_assign_keyword = Item("assign [a] keyword to [link] <link>")
|
|
config.lang.tabify_links = Item("tab if I <links>")
|
|
config.lang.tabify_links_sep = Item("comma")
|
|
|
|
config.lang.search_text = Item("[power] search [for] <text>")
|
|
config.lang.search_keyword_text = Item("[power] search <keyword> [for] <text>")
|
|
config.lang.search_searchbar_text = Item("[power] search <searchbar> [for] <text>")
|
|
config.lang.search_clipboard = Item("[power] search [for] (clipboard | clip board)")
|
|
config.lang.search_keyword_clipboard = Item("[power] search <keyword> [for] clipboard")
|
|
config.lang.search_searchbar_clipboard = Item("[power] search <searchbar> [for] clipboard")
|
|
|
|
#config.generate_config_file()
|
|
config.load()
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Check and prepare search-related config values.
|
|
|
|
keywords = config.search.keywords
|
|
searchbar = dict([(n,i) for i,n in enumerate(config.search.searchbar)])
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Create the rule to match mouseless-browsing link numbers.
|
|
|
|
class LinkRule(Rule):
|
|
|
|
def __init__(self):
|
|
element = Number(zero=True)
|
|
Rule.__init__(self, "link_rule", element, exported=False)
|
|
|
|
def value(self, node):
|
|
# Format and return keystrokes to select the link.
|
|
digits = str(node.children[0].value())
|
|
link_keys = "f6,s-f6," + ",".join(["numpad"+i for i in digits])
|
|
self._log.debug("Link keys: %r" % link_keys)
|
|
return link_keys
|
|
|
|
link = RuleRef(name="link", rule=LinkRule())
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Create the main command rule.
|
|
|
|
class CommandRule(MappingRule):
|
|
|
|
mapping = {
|
|
config.lang.new_win: Key("c-n"),
|
|
config.lang.new_tab: Key("c-t"),
|
|
config.lang.close_tab: Key("c-w"),
|
|
config.lang.close_tab_n: Key("0, %(n)d, enter/20, c-w"),
|
|
config.lang.close_n_tabs: Key("c-w/20:%(n)d"),
|
|
config.lang.address_bar: Key("a-d"),
|
|
config.lang.copy_address: Key("a-d, c-c"),
|
|
config.lang.paste_address: Key("a-d, c-v, enter"),
|
|
config.lang.search_bar: Key("c-k"),
|
|
config.lang.go_home: Key("a-home"),
|
|
config.lang.stop_loading: Key("escape"),
|
|
config.lang.toggle_tags: Key("f12"),
|
|
config.lang.fresh_tags: Key("f12, f12"),
|
|
config.lang.caret_browsing: Key("f7"),
|
|
config.lang.bookmark_page: Key("c-d"),
|
|
config.lang.save_page_as: Key("c-s"),
|
|
config.lang.print_page: Key("c-p"),
|
|
|
|
config.lang.show_tab_n: Key("0, %(n)d, enter"),
|
|
config.lang.back: Key("a-left/15:%(n)d"),
|
|
config.lang.forward: Key("a-right/15:%(n)d"),
|
|
config.lang.next_tab: Key("c-tab:%(n)d"),
|
|
config.lang.prev_tab: Key("cs-tab:%(n)d"),
|
|
|
|
config.lang.normal_size: Key("a-v/20, z/20, r"),
|
|
config.lang.smaller_size: Key("c-minus:%(n)d"),
|
|
config.lang.bigger_size: Key("cs-equals:%(n)d"),
|
|
|
|
config.lang.submit: Key("enter"),
|
|
config.lang.submit_text: Text("%(text)s") + Key("enter"),
|
|
config.lang.submit_clipboard: Key("c-v, enter"),
|
|
|
|
config.lang.find: Key("c-f"),
|
|
config.lang.find_text: Key("c-f") + Text("%(text)s"),
|
|
config.lang.find_next: Key("f3/10:%(n)d"),
|
|
|
|
config.lang.link_open: Key("%(link)s, enter"),
|
|
config.lang.link_save: Key("%(link)s, shift/10, apps/20, k"),
|
|
config.lang.link_save_now: Key("%(link)s, shift/10, apps/20, k")
|
|
+ WaitWindow(title="Enter name of file")
|
|
+ Pause("20") + Key("enter"),
|
|
config.lang.link_select: Key("%(link)s, shift"),
|
|
config.lang.link_menu: Key("%(link)s, shift/10, apps"),
|
|
config.lang.link_force: Key("%(link)s, shift/10, enter"),
|
|
config.lang.link_window: Key("%(link)s, shift/10, apps/20, w"),
|
|
config.lang.link_tab: Key("%(link)s, shift/10, apps/20, t"),
|
|
config.lang.link_copy: Key("%(link)s, shift/10, apps/20, a"),
|
|
config.lang.link_copy_into_tab: Key("%(link)s, shift/10, apps/20, a/10, c-t/20, c-v, enter"),
|
|
config.lang.link_list: Key("%(link)s, enter, a-down"),
|
|
config.lang.link_submit: Key("%(link)s, enter/30, enter"),
|
|
config.lang.link_submit_text: Key("%(link)s, enter/30")
|
|
+ Text("%(text)s") + Key("enter"),
|
|
config.lang.link_submit_clipboard: Key("%(link)s, enter/30, c-v, enter"),
|
|
config.lang.link_dictation_box: Key("%(link)s, enter/30, cs-d"),
|
|
config.lang.link_assign_keyword: Key("%(link)s, enter/10, apps/20, k"),
|
|
|
|
config.lang.search_text: Key("c-k")
|
|
+ Text("%(text)s") + Key("enter"),
|
|
config.lang.search_searchbar_text: Key("c-k, c-up:20, c-down:%(searchbar)d")
|
|
+ Text("%(text)s") + Key("enter"),
|
|
config.lang.search_keyword_text: Key("a-d")
|
|
+ Text("%(keyword)s %(text)s")
|
|
+ Key("enter"),
|
|
config.lang.search_clipboard: Key("c-k, c-v, enter"),
|
|
config.lang.search_searchbar_clipboard: Key("c-k, c-up:20, c-down:%(searchbar)d, c-v, enter"),
|
|
config.lang.search_keyword_clipboard: Key("a-d") + Text("%(keyword)s")
|
|
+ Key("c-v, enter"),
|
|
}
|
|
extras = [
|
|
link,
|
|
IntegerRef("n", 1, 20),
|
|
Dictation("text"),
|
|
Choice("keyword", keywords),
|
|
Choice("searchbar", searchbar),
|
|
]
|
|
defaults = {
|
|
"n": 1,
|
|
}
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Create the command rule for sliding.
|
|
|
|
slide_directions = {
|
|
"up": (0,-1),
|
|
"down": (0,+1),
|
|
}
|
|
slide_speeds = {
|
|
"1": 10,
|
|
"2": 20,
|
|
"3": 30,
|
|
"4": 40,
|
|
}
|
|
slide_default_speed = 15
|
|
slide_start_spec = "(-15,0.6)"
|
|
|
|
slide_grammar = None
|
|
|
|
def start_sliding(direction, speed):
|
|
offset_x = direction[0] * speed
|
|
offset_y = direction[1] * speed
|
|
offset_spec = "<%d,%d>" % (offset_x, offset_y)
|
|
action = Key("escape")
|
|
action.execute()
|
|
action = Mouse("%s/25, middle/25, %s" % (slide_start_spec, offset_spec))
|
|
action.execute()
|
|
|
|
global slide_grammar
|
|
if not slide_grammar:
|
|
slide_grammar = Grammar("Firefox slide grammar")
|
|
slide_grammar.add_rule(SlideControlRule())
|
|
slide_grammar.load()
|
|
slide_grammar.set_exclusive(True)
|
|
|
|
def stop_sliding():
|
|
action = Key("escape")
|
|
action.execute()
|
|
|
|
global slide_grammar
|
|
if slide_grammar:
|
|
slide_grammar.set_exclusive(False)
|
|
slide_grammar.unload()
|
|
slide_grammar = None
|
|
|
|
class SlideStartRule(MappingRule):
|
|
|
|
mapping = {
|
|
"slide <direction> [<speed>]": Function(start_sliding),
|
|
}
|
|
extras = [
|
|
Choice("direction", slide_directions),
|
|
Choice("speed", slide_speeds),
|
|
]
|
|
defaults = {
|
|
"speed": slide_default_speed,
|
|
}
|
|
|
|
|
|
class SlideControlRule(MappingRule):
|
|
|
|
mapping = {
|
|
"[slide] <direction> [<speed>]": Function(start_sliding),
|
|
"[slide] stop": Function(stop_sliding),
|
|
}
|
|
extras = [
|
|
Choice("direction", slide_directions),
|
|
Choice("speed", slide_speeds),
|
|
]
|
|
defaults = {
|
|
"speed": slide_default_speed,
|
|
}
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Create the main command rule.
|
|
|
|
class TabifyRule(CompoundRule):
|
|
|
|
spec = config.lang.tabify_links
|
|
sep_element = Compound(config.lang.tabify_links_sep)
|
|
repeat_element = Sequence([sep_element, link])
|
|
repetitions = Repetition(child=repeat_element, min=0, max=8)
|
|
extras = [Sequence(name="links", children=(link, repetitions))]
|
|
|
|
def _process_recognition(self, node, extras):
|
|
link_nodes = node.get_children_by_name("link")
|
|
for n in link_nodes:
|
|
action = Key(n.value()) + Key("shift/10, apps/20, t/20")
|
|
action.execute()
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
# Create and load this module's grammar.
|
|
|
|
context = AppContext(executable="firefox")
|
|
grammar = Grammar("firefox_general", context=context)
|
|
grammar.add_rule(CommandRule())
|
|
grammar.add_rule(SlideStartRule())
|
|
grammar.add_rule(TabifyRule())
|
|
grammar.load()
|
|
|
|
# Unload function which will be called by natlink at unload time.
|
|
def unload():
|
|
global grammar
|
|
if grammar: grammar.unload()
|
|
grammar = None
|