├── .gitignore ├── package.json ├── Gulpfile.js ├── macros ├── _callback.py ├── firefox.py ├── _snore.py ├── putty.py ├── _ctrlp.py ├── _irssi.py ├── _tweetdeck.py ├── _buffergator.py ├── _less.py ├── _chrome.py ├── _password.py ├── _globals.py ├── _nerdtree.py ├── _jade.py ├── _javascript.py ├── _bash.py ├── _bringme.py ├── _multiedit.txt ├── _vim.py ├── _multiedit.py ├── _firefox.py └── _vocola_main.py ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.swp 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependicies": {}, 3 | "dependencies": { 4 | "gulp": "~0.2.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var path = require('path'); 3 | var source = path.join('.', 'macros', '**', '*'); 4 | var destination = '\\NatLink\\NatLink\\MacroSystem'; 5 | 6 | gulp.task('copy-macros', function () { 7 | console.log("copying macros"); 8 | gulp.src(source).pipe(gulp.dest(destination)); 9 | }); 10 | 11 | gulp.task('default', function () { 12 | gulp.run('copy-macros'); 13 | gulp.watch(source, function (event) { 14 | gulp.run('copy-macros'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /macros/_callback.py: -------------------------------------------------------------------------------- 1 | import natlink 2 | import natlinkmain 3 | import pprint 4 | import redis 5 | 6 | def myCallback(command, args): 7 | if command == "mic": 8 | try: 9 | print "setting natlink.mic to %s" % args 10 | r = redis.Redis() 11 | r.set('natlink.mic', args) 12 | except Exception as e: 13 | print e 14 | print "falied to connect to redis" 15 | 16 | apply(natlinkmain.changeCallback, [command, args]) 17 | 18 | natlink.setChangeCallback(myCallback) 19 | 20 | def unload(): 21 | print "unloading callback" 22 | natlink.setChangeCallback(natlinkmain.changeCallback) 23 | -------------------------------------------------------------------------------- /macros/firefox.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, AppContext, MappingRule, Dictation, Key, Text) 2 | 3 | firefox_context = AppContext(executable="firefox") 4 | grammar = Grammar("firefox", context=firefox_context) 5 | 6 | firefox_rules = MappingRule( 7 | name = "firefox", 8 | mapping = { 9 | "jump": Key("f12"), 10 | "edit": Key("cs-f4"), 11 | }, 12 | extras = [ 13 | Dictation("text") 14 | ] 15 | ) 16 | 17 | grammar.add_rule(firefox_rules) 18 | 19 | grammar.load() 20 | 21 | def unload(): 22 | global grammar 23 | if grammar: grammar.unload() 24 | grammar = None 25 | -------------------------------------------------------------------------------- /macros/_snore.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, CompoundRule, Config, Section, Item) 2 | 3 | import natlink 4 | 5 | config = Config("snore"); 6 | config.lang = Section("Language section"); 7 | config.lang.snore = Item("snore", doc="Put the microphone to sleep") 8 | 9 | class SnoreRule(CompoundRule): 10 | 11 | spec = config.lang.snore 12 | 13 | def _process_recognition(self, node, extras): 14 | self._log.debug("sleepy mic") 15 | natlink.setMicState("sleeping") 16 | 17 | grammar = Grammar("snore") 18 | grammar.add_rule(SnoreRule()) 19 | grammar.load() 20 | 21 | def unload(): 22 | global grammar 23 | if grammar: grammar.unload() 24 | grammar = None 25 | -------------------------------------------------------------------------------- /macros/putty.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, AppContext, MappingRule, Dictation, Key, Text) 2 | 3 | putty_context = AppContext(executable="putty") 4 | grammar = Grammar("putty", context=putty_context) 5 | 6 | putty_rules = MappingRule( 7 | name = "putty", 8 | mapping = { 9 | "mux north": Key('c-b, k'), 10 | "mux south": Key('c-b, j'), 11 | "mux west": Key('c-b, h'), 12 | "mux east": Key('c-b, l'), 13 | }, 14 | extras = [ 15 | Dictation("text") 16 | ] 17 | ) 18 | 19 | grammar.add_rule(putty_rules) 20 | 21 | grammar.load() 22 | 23 | def unload(): 24 | global grammar 25 | if grammar: grammar.unload() 26 | grammar = None 27 | -------------------------------------------------------------------------------- /macros/_ctrlp.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, AppContext, MappingRule, Dictation, Key, Text, Integer) 2 | ctlp = AppContext(title="ControlP") 3 | grammar = Grammar("ctlp", context=ctlp) 4 | 5 | rules = MappingRule( 6 | name = "ControlP", 7 | mapping = { 8 | "split": Key("c-s"), 9 | "vertical": Key("c-v"), 10 | "refresh": Key("f5"), 11 | }, 12 | 13 | extras = [ 14 | Dictation("text", format=False), 15 | Integer("n", 1, 20000), 16 | ], 17 | defaults = { 18 | "n" : 1 19 | } 20 | ) 21 | 22 | grammar.add_rule(rules) 23 | grammar.load() 24 | def unload(): 25 | global grammar 26 | if grammar: grammar.unload() 27 | grammar = None 28 | 29 | 30 | -------------------------------------------------------------------------------- /macros/_irssi.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, AppContext, MappingRule, Dictation, Key, Text, Integer) 2 | 3 | context = AppContext(title="irssi") 4 | grammar = Grammar("irssi", context=context) 5 | 6 | rules = MappingRule( 7 | name = "irssi", 8 | mapping = { 9 | "next when": Key("c-n"), 10 | "close": Text("/win close"), 11 | "when ": Text("/win %(n)d") + Key("enter"), 12 | "join": Text("/join "), 13 | }, 14 | extras = [ 15 | Dictation("text"), 16 | Integer("n", 0, 20000), 17 | ], 18 | defaults = { 19 | "n" : 1 20 | } 21 | ) 22 | 23 | grammar.add_rule(rules) 24 | 25 | grammar.load() 26 | 27 | def unload(): 28 | global grammar 29 | if grammar: grammar.unload() 30 | grammar = None 31 | 32 | -------------------------------------------------------------------------------- /macros/_tweetdeck.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, AppContext, MappingRule, Dictation, Key, Text, Integer) 2 | 3 | context = AppContext(title="TweetDeck") 4 | grammar = Grammar("tweetdeck", context=context) 5 | 6 | rules = MappingRule( 7 | name = "tweetdeck", 8 | mapping = { 9 | "new Tweet": Key("n"), 10 | "send": Key("c-enter"), 11 | "reply": Key("r"), 12 | "favorite": Key("f"), 13 | "retweet": Key("t"), 14 | }, 15 | extras = [ 16 | Dictation("text"), 17 | Integer("n", 0, 20000), 18 | ], 19 | defaults = { 20 | "n" : 1 21 | } 22 | ) 23 | 24 | grammar.add_rule(rules) 25 | 26 | grammar.load() 27 | 28 | def unload(): 29 | global grammar 30 | if grammar: grammar.unload() 31 | grammar = None 32 | 33 | 34 | -------------------------------------------------------------------------------- /macros/_buffergator.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, AppContext, MappingRule, Dictation, Key, Text, Integer, Mimic) 2 | context = AppContext(title="buffergator") 3 | grammar = Grammar("buffergator", context=context) 4 | noSpaceNoCaps = Mimic("\\no-caps-on") + Mimic("\\no-space-on") 5 | rules = MappingRule( 6 | name = "buffergator", 7 | mapping = { 8 | "split": Key("c-s"), 9 | "vertical": Key("c-v"), 10 | }, 11 | 12 | extras = [ 13 | Dictation("text", format=False), 14 | Integer("n", 1, 20000), 15 | ], 16 | 17 | defaults = { 18 | "text": '', 19 | "n" : 1 20 | } 21 | ) 22 | 23 | grammar.add_rule(rules) 24 | grammar.load() 25 | def unload(): 26 | global grammar 27 | if grammar: grammar.unload() 28 | grammar = None 29 | 30 | 31 | -------------------------------------------------------------------------------- /macros/_less.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, AppContext, MappingRule, Dictation, Key, Text, Integer, Mimic) 2 | 3 | context = AppContext(title = "less") 4 | grammar = Grammar("less", context=context) 5 | noSpaceNoCaps = Mimic("\\no-caps-on") + Mimic("\\no-space-on") 6 | 7 | rules = MappingRule( 8 | name = "less", 9 | mapping = { 10 | "heading []": Text("h%(n)d "), 11 | "paragraph": Text("p "), 12 | "unordered list": Text("ul "), 13 | "list item": Text("li "), 14 | "image": Text("img "), 15 | " (pixel|pixels)": Text("%(n)dpx"), 16 | }, 17 | extras = [ 18 | Dictation("text"), 19 | Integer("n", 0, 20000), 20 | ], 21 | defaults = { 22 | "n" : 1 23 | } 24 | ) 25 | 26 | grammar.add_rule(rules) 27 | 28 | grammar.load() 29 | 30 | def unload(): 31 | global grammar 32 | if grammar: grammar.unload() 33 | grammar = None 34 | 35 | 36 | -------------------------------------------------------------------------------- /macros/_chrome.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, AppContext, MappingRule, Dictation, Key, Text, Integer, Mimic) 2 | 3 | context = AppContext(executable="chrome") 4 | grammar = Grammar("chrome", context=context) 5 | noSpaceNoCaps = Mimic("\\no-caps-on") + Mimic("\\no-space-on") 6 | 7 | rules = MappingRule( 8 | name = "chrome", 9 | mapping = { 10 | "edit": Key("w-a"), 11 | "reload" : Key("f5"), 12 | "open": Key("escape, o"), 13 | "jump": Key("f"), 14 | "new tab": Key("t"), 15 | "search tabs": Key("T"), 16 | "find": Key("slash"), 17 | "console": Key("cs-j"), 18 | "close tab": Key("c-w"), 19 | "escape": Key('escape'), 20 | }, 21 | extras = [ 22 | Dictation("text"), 23 | Integer("n", 0, 20000), 24 | ], 25 | defaults = { 26 | "n" : 1 27 | } 28 | ) 29 | 30 | grammar.add_rule(rules) 31 | 32 | grammar.load() 33 | 34 | def unload(): 35 | global grammar 36 | if grammar: grammar.unload() 37 | grammar = None 38 | 39 | -------------------------------------------------------------------------------- /macros/_password.py: -------------------------------------------------------------------------------- 1 | # This macro will copy your password from "Password Safe", provided that the application 2 | # is in the 8th position on your taskbar 3 | from dragonfly import (Grammar, MappingRule, Key, Config, Section, Item, Text, Dictation) 4 | from dragonfly.windows.clipboard import Clipboard 5 | 6 | class PasswordRule(MappingRule): 7 | name = "mypassword" 8 | 9 | mapping = { 10 | "get password": Text('%(text)s'), 11 | } 12 | 13 | extras = [ 14 | Dictation("text", format=False), 15 | ] 16 | 17 | def _process_recognition(self, value, extras): 18 | getPassword = Key("w-b/10, s-tab/10, right:8/10, enter, c-f/10") + value + Key('enter/10, escape/10, c-c/10, a-tab/10') 19 | getPassword.execute() 20 | clipboard = Clipboard() 21 | clipboard.copy_from_system(clear = True) 22 | password = clipboard.get_text() 23 | action = Text(password) 24 | action.execute() 25 | 26 | grammar = Grammar("mypassword") 27 | grammar.add_rule(PasswordRule()) 28 | grammar.load() 29 | def unload(): 30 | global grammar 31 | if grammar: grammar.unload() 32 | grammar = None 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Chris Cowan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /macros/_globals.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, FocusWindow, MappingRule, Key, Config, Section, Item, Playback, Mimic) 2 | 3 | rules = MappingRule( 4 | name = "general", 5 | mapping = { 6 | "slap": Key("enter"), 7 | "Max when": Key("w-up"), 8 | "left when": Key("w-left"), 9 | "right when": Key("w-right"), 10 | "min win": Key("w-down"), 11 | "switch apps": Key("alt:down, tab"), 12 | "switch app": Key("a-tab"), 13 | "termi": Key("w-b/10, s-tab/10, enter"), 14 | "foxy": Key("w-b/10, s-tab/10, right:1/10, enter"), 15 | "foxy reload": Key("w-b/10, s-tab/10, right:1/10, enter/10, f5"), 16 | "Jimmy": Key("w-b/10, s-tab/10, right:2/10, enter"), 17 | "Heidi": Key("w-b/10, s-tab/10, right:3/10, enter"), 18 | "chrome": Key("w-b/10, s-tab/10, right:4/10, enter"), 19 | "chrome reload": Key("w-b/10, s-tab/10, right:4/10, enter/10, f5"), 20 | "bashing": Key("w-b/10, s-tab/10, right:5/10, enter"), 21 | "code mode": Mimic("\\no-caps-on") + Mimic("\\no-space-on"), 22 | } 23 | ) 24 | 25 | grammar = Grammar("general") 26 | grammar.add_rule(rules) 27 | grammar.load() 28 | 29 | def unload(): 30 | global grammar 31 | if grammar: grammar.unload() 32 | grammar = None 33 | -------------------------------------------------------------------------------- /macros/_nerdtree.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, AppContext, MappingRule, Dictation, Key, Text, Integer, Mimic) 2 | context = AppContext(title="nerdtree") 3 | grammar = Grammar("nerdtree", context=context) 4 | noSpaceNoCaps = Mimic("\\no-caps-on") + Mimic("\\no-space-on") 5 | rules = MappingRule( 6 | name = "nerdtree", 7 | mapping = { 8 | "split": Key("i"), 9 | "vertical": Key("s"), 10 | "add": noSpaceNoCaps + Key("m, a") + noSpaceNoCaps + Key('backspace'), 11 | "move": noSpaceNoCaps + Key("m, m") + noSpaceNoCaps, 12 | "copy": noSpaceNoCaps + Key("m, c") + noSpaceNoCaps, 13 | "kill": Key("m, d"), 14 | "open ": Text(":%(n)d") + Key("enter") + Key("enter"), 15 | "open vertical ": Text(":%(n)d") + Key("enter, s"), 16 | "change root": Key("C"), 17 | "level up": Key("u"), 18 | "go to parent": Key("P"), 19 | }, 20 | 21 | extras = [ 22 | Dictation("text", format=False), 23 | Integer("n", 1, 20000), 24 | ], 25 | 26 | defaults = { 27 | "text": '', 28 | "n" : 1 29 | } 30 | ) 31 | 32 | grammar.add_rule(rules) 33 | grammar.load() 34 | def unload(): 35 | global grammar 36 | if grammar: grammar.unload() 37 | grammar = None 38 | 39 | 40 | -------------------------------------------------------------------------------- /macros/_jade.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, AppContext, MappingRule, Dictation, Key, Text, Integer, Mimic) 2 | 3 | context = AppContext(title = "jade") 4 | grammar = Grammar("jade", context=context) 5 | noSpaceNoCaps = Mimic("\\no-caps-on") + Mimic("\\no-space-on") 6 | 7 | rules = MappingRule( 8 | name = "jade", 9 | mapping = { 10 | "heading []": Text("h%(n)d "), 11 | "span []": Text(".span%(n)d"), 12 | "paragraph": Text("p ") + noSpaceNoCaps, 13 | "link": Text("link") + Key("tab") + noSpaceNoCaps, 14 | "attribute": Text("attribute") + Key("tab") + noSpaceNoCaps, 15 | "Eckelberry": Text("echo_var") + Key("tab") + noSpaceNoCaps, 16 | "row fluid": Text(".row-fluid ") + noSpaceNoCaps, 17 | "row": Text(".row") + noSpaceNoCaps, 18 | "container": Text(".container") + noSpaceNoCaps, 19 | "unordered list": Text("ul") + noSpaceNoCaps, 20 | "list item": Text("li") + noSpaceNoCaps, 21 | "image": Text("image") + Key("tab") + noSpaceNoCaps, 22 | "equal": Text("=") + noSpaceNoCaps, 23 | }, 24 | extras = [ 25 | Dictation("text"), 26 | Integer("n", 0, 20000), 27 | ], 28 | defaults = { 29 | "n" : 1 30 | } 31 | ) 32 | 33 | grammar.add_rule(rules) 34 | 35 | grammar.load() 36 | 37 | def unload(): 38 | global grammar 39 | if grammar: grammar.unload() 40 | grammar = None 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | code-by-voice 2 | ============= 3 | 4 | In this repository you will find all the support files for my "code by voice" setup using Dragon Naturally Speaking and DragonFly. I was inspired to do this by Tavis Rudd (https://github.com/tavisrudd). You can see his excelent presentation from PyCon here: http://youtu.be/8SkdfdXWYaI 5 | 6 | ### !!! WORK IN PROGRESS !!! 7 | 8 | ## Requirements 9 | 10 | * Dragnon Naturally Speaking - You can use any edition (including home). The advantage of using the Premium Edition, based on talking to the rep from Nuance, is that the Premium edition will let you backup the voice traning directly from DNS. 11 | * Windows 8.1 12 | * DragonFly 13 | * Natlink / Unicode 14 | * Python 2.7 15 | * Virtualization Software (VMWare, VirtualBox, Parallels) if you are setting this up on Mac OS X or Linux 16 | 17 | ## Resources 18 | 19 | * Dragon Naturally Speaking (http://www.nuance.com/dragon/index.htm) 20 | * DragonFly (http://code.google.com/p/dragonfly/) 21 | * Unimacro (http://qh.antenna.nl/unimacro/index.html) 22 | 23 | 24 | ## Getting Started 25 | 26 | Here are the steps to get your system installed and running. 27 | 28 | !!! These are a work in progress !!! 29 | 30 | 1. Install Windows 8 (7 and XP works too) 31 | 1. Install Dragon Naturally Speaking (and go through the training) 32 | 1. Download Python 2.7 (http://sourceforge.net/projects/natlink/files/pythonfornatlink/python2.7.zip/download) 33 | 2. Extract the download 34 | 1. Install python-2.7 35 | 2. Install pywin32-218.win32-py2.7 36 | 3. Install PyXML-0.8.4.win32-py2.7 37 | 1. Install wxPython2.8-win32-ansi-2.8.12.1-py27 38 | 1. Add PATH enviroment variable for C:\Python27 and C:\Python27\Script (Right click on computer, click on Advanced system settings, click on Enviroment Variables) 39 | 1. Download easy_install.py (https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py) 40 | 1. Download Natlink (http://qh.antenna.nl/unimacro/installation/installation.html) 41 | 2. Install Natlink (Just double click the installer) 42 | 1. Download DragonFly Zip File, not the windows installer (http://code.google.com/p/dragonfly/downloads/list) 43 | 1. Exract dragonfly-0.6.5.zip 44 | 1. Install DragonFly (inside the dragonfly-0.6.5 directory run "python setup.py install") 45 | 1. Enable Natlink via GUI (using the start menu) 46 | 1. Restart Dragon NaturallySpeaking 47 | -------------------------------------------------------------------------------- /macros/_javascript.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, AppContext, MappingRule, Dictation, Key, Text, Integer, Mimic) 2 | javascript = AppContext(title="javascript") 3 | grammar = Grammar("javascript", context=(javascript)) 4 | 5 | noSpaceNoCaps = Mimic("\\no-caps-on") + Mimic("\\no-space-on") 6 | 7 | rules = MappingRule( 8 | name = "javascript", 9 | mapping = { 10 | "smear" : Text("err"), 11 | "function" : Text("fn") + Key("tab") + noSpaceNoCaps, 12 | "very equal" : Text("var_equal") + Key("tab") + noSpaceNoCaps, 13 | "very" : Text("var") + Key("tab") + noSpaceNoCaps, 14 | "require J query" : Text("req_jquery") + Key("tab"), 15 | "require" : Text("req") + Key("tab") + noSpaceNoCaps, 16 | "load" : Text("load") + Key("tab") + noSpaceNoCaps, 17 | "define model" : Text("model") + Key("tab") + noSpaceNoCaps, 18 | "define collection": Text("collection") + Key("tab") + noSpaceNoCaps, 19 | "define" : Text("def") + Key("tab") + noSpaceNoCaps, 20 | "single if" : Text("single_if") + Key("tab") + noSpaceNoCaps, 21 | "if" : Text("if") + Key("tab") + noSpaceNoCaps, 22 | "undies" : Text("undermethod") + Key("tab") + noSpaceNoCaps, 23 | "require undies" : Text("require_underscore") + Key("tab"), 24 | "require lodash" : Text("require_lodash") + Key("tab"), 25 | "chain undies" : Text("chain_undies") + Key("tab") + noSpaceNoCaps, 26 | "for each" : Text(".forEach()") + Key("escape, i") + noSpaceNoCaps, 27 | "express method" : Text("express_method") + Key("tab") + noSpaceNoCaps, 28 | "happy" : Text("app"), 29 | "return call back air" : Text("return_callback_error") + Key("tab"), 30 | "callback function" : Text("callback_function") + Key("tab") + noSpaceNoCaps, 31 | "log" : Text("cl") + Key("tab") + noSpaceNoCaps, 32 | "comment " : Text("// %(text)s"), 33 | "type of" : Text("typeof") + Key('tab') + noSpaceNoCaps, 34 | "homey" : Text("home"), 35 | "module exports" : Text("module.exports"), 36 | "regex" : Text("//") + Key("escape, i") + noSpaceNoCaps, 37 | "push task": Text("push_task") + Key("tab") + noSpaceNoCaps, 38 | }, 39 | 40 | extras = [ 41 | Dictation("text", format=False), 42 | Integer("n", 1, 20000), 43 | ], 44 | defaults = { 45 | "n" : 1 46 | } 47 | ) 48 | 49 | grammar.add_rule(rules) 50 | grammar.load() 51 | def unload(): 52 | global grammar 53 | if grammar: grammar.unload() 54 | grammar = None 55 | 56 | -------------------------------------------------------------------------------- /macros/_bash.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, AppContext, MappingRule, Dictation, Key, Text, Integer, Mimic) 2 | 3 | putty_context = AppContext(executable="putty") 4 | bash_context = AppContext(title="bash") 5 | grammar = Grammar("bash", context=(putty_context | bash_context)) 6 | noSpaceNoCaps = Mimic("\\no-caps-on") + Mimic("\\no-space-on") 7 | 8 | rules = MappingRule( 9 | name = "bash", 10 | mapping = { 11 | "term ": Key('c-b') + Text("%(n)d"), 12 | "term create": Key('c-b, c'), 13 | "term north": Key('c-b, k'), 14 | "term south": Key('c-b, j'), 15 | "term west": Key('c-b, h'), 16 | "term east": Key('c-b, l'), 17 | "term vertical": Key('c-b, v'), 18 | "term split": Key('c-b, s'), 19 | "term detach": Key('c-b, d'), 20 | "term down []": Key('c-b, colon') + Text("resize-pane -D %(n)d") + Key('enter'), 21 | "term up []": Key('c-b, colon') + Text("resize-pane -U %(n)d") + Key('enter'), 22 | "term left []": Key('c-b, colon') + Text("resize-pane -L %(n)d") + Key('enter'), 23 | "term right []": Key('c-b, colon') + Text("resize-pane -R %(n)d") + Key('enter'), 24 | "term scroll": Key("c-b, lbracket"), 25 | 26 | "watch macros": Text("cd ~/Documents/GitHub/code-by-voice && ./node_modules/.bin/gulp") + Key('enter'), 27 | 28 | "get status": Text("git status") + Key('enter'), 29 | "push": Text("git push") + Key('enter'), 30 | "push to beta": Text("git push beta develop") + Key('enter'), 31 | "push to production": Text("git push all master") + Key('enter'), 32 | "push to staging": Text("git push staging develop") + Key('enter'), 33 | "add all": Text("git add -A") + Key('enter'), 34 | "commit": Text("git commit -a") + Key('enter'), 35 | "clone": Text("git clone "), 36 | "check out": Text("git checkout ") + Key("tab"), 37 | "fetch": Text("git fetch ") + Key("tab"), 38 | "merge": Text("git merge ") + Key("tab"), 39 | "stash": Text("git stash save") + Key('enter'), 40 | "pop stash": Text("git stash pop") + Key('enter'), 41 | 42 | "change directory": Text("cd ") + Key('tab') + noSpaceNoCaps, 43 | "cancel": Key("c-c"), 44 | "exit": Key("c-d"), 45 | "up directory": Text("cd ..") + Key('enter'), 46 | "change directory to ": Text("cd %(text)s"), 47 | "list files": Text("ls -l") + Key("enter"), 48 | "go home": Text("cd ~") + Key("enter"), 49 | "attach": Text("tmux attach") + Key("enter"), 50 | "them": Text("vi") + Key("enter"), 51 | "tmux": Text("tmux") + Key("enter"), 52 | "htop": Text("htop") + Key("enter"), 53 | }, 54 | extras = [ 55 | Dictation("text"), 56 | Integer("n", 0, 20000), 57 | ], 58 | defaults = { 59 | "n" : 1 60 | } 61 | ) 62 | 63 | grammar.add_rule(rules) 64 | 65 | grammar.load() 66 | 67 | def unload(): 68 | global grammar 69 | if grammar: grammar.unload() 70 | grammar = None 71 | -------------------------------------------------------------------------------- /macros/_bringme.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is a command-module for Dragonfly. 3 | # (c) Copyright 2008 by Christo Butcher 4 | # Licensed under the LGPL, see 5 | # 6 | 7 | """ 8 | Command-module for direct opening of **user-defined targets** 9 | ============================================================================ 10 | 11 | This module offers very easy and direct opening of user- 12 | defined targets. User define their own targets in the 13 | configuration file, and then have instant access to them 14 | by saying "bring me ". The configuration file 15 | format is described below. 16 | 17 | Installation 18 | ---------------------------------------------------------------------------- 19 | 20 | If you are using DNS and Natlink, simply place this file in you Natlink 21 | macros directory. It will then be automatically loaded by Natlink when 22 | yo next toggle your microphone or restart Natlink. 23 | 24 | Commands 25 | ---------------------------------------------------------------------------- 26 | 27 | Command: **"bring me "** 28 | Open the specified target. 29 | The ** extra in this rule can be any one 30 | of the targets defined in this module's configuration file. 31 | 32 | Command: **"paste me "** 33 | Paste the address of the specified target. 34 | The ** extra is the same as for the bring me command. 35 | 36 | Customization 37 | ---------------------------------------------------------------------------- 38 | 39 | Users should customize this module by editing its configuration 40 | file. In this file they should fill the ``targets.mapping`` 41 | with their own personal targets. This target mapping maps *what 42 | you say* to *which target to open*. 43 | 44 | For example: 45 | 46 | .. code-block:: python 47 | 48 | targets.mapping = { 49 | "[my] favorite website": website("http://code.google.com/p/dragonfly"), 50 | "my local folder": folder(r"C:\"), 51 | } 52 | 53 | (Note the format of each target: ``"...": type("..."),`` 54 | And note the use of ``r"..."`` when specifying a path 55 | containing backslash characters; the *r* in front of the 56 | open-quote means that backslashes can be given literally 57 | and *don't* need to be doubled.) 58 | 59 | Using the configuration above would allow the user to say: 60 | 61 | - **"bring me my favorite website"** and the Dragonfly homepage 62 | will be opened. 63 | - **"bring me my local folder"** and a Windows Explorer 64 | will be opened showing the local ``C:\`` folder. 65 | 66 | """ 67 | 68 | try: 69 | import pkg_resources 70 | pkg_resources.require("dragonfly >= 0.6.5beta1.dev-r76") 71 | except ImportError: 72 | pass 73 | 74 | import os 75 | import os.path 76 | import webbrowser 77 | import subprocess 78 | 79 | from dragonfly import (Grammar, Choice, CompoundRule, 80 | Paste, Config, Section, Item) 81 | 82 | 83 | #--------------------------------------------------------------------------- 84 | # Bringable classes. 85 | 86 | class BringableBase(object): 87 | def __init__(self, target): 88 | self.target = target 89 | def __repr__(self): 90 | return "%s(%r)" % (self.__class__.__name__, self.target) 91 | __str__ = __repr__ 92 | def bring_it(self): 93 | pass 94 | 95 | class website(BringableBase): 96 | def bring_it(self): 97 | # subprocess.Popen([r"C:\Program Files (x86)\Mozilla Firefox\firefox.exe", self.target]) 98 | subprocess.Popen([r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe", self.target]) 99 | # webbrowser.open(self.target) 100 | 101 | class folder(BringableBase): 102 | def bring_it(self): 103 | target = self.target 104 | subprocess.Popen(["explorer", target]) 105 | 106 | class open(BringableBase): 107 | def __init__(self, target, verb="open"): 108 | self.verb = verb 109 | BringableBase.__init__(self, target) 110 | def bring_it(self): 111 | target = self.target 112 | os.startfile(target, self.verb) 113 | 114 | class ssh(BringableBase): 115 | putty_path = r"C:\Program Files (x86)\PuTTY\putty" 116 | def bring_it(self): 117 | subprocess.Popen([self.putty_path, "-load", self.target]) 118 | 119 | 120 | #--------------------------------------------------------------------------- 121 | # Set up this module's configuration. 122 | 123 | config = Config("bring me") 124 | config.targets = Section("Targets section") 125 | config.targets.mapping = Item( 126 | default={ 127 | "Google": website("http://www.google.com"), 128 | "dev": website("https://dev.plus3.ws"), 129 | "development": ssh("development"), 130 | "Trello": website("http://www.trello.com"), 131 | "Gmail": website("http://mail.google.com"), 132 | "hacker news": website("http://news.ycombinator.com"), 133 | "Reddit": website("http://www.reddit.com"), 134 | }, 135 | doc="Mapping of spoken targets to bringable targets.", 136 | namespace={ 137 | "website": website, 138 | "open": open, 139 | "folder": folder, 140 | "ssh": ssh, 141 | }, 142 | ) 143 | config.lang = Section("Language section") 144 | config.lang.bring_me = Item("bring me ", 145 | doc="Command to bring a target;" 146 | " must contain the extra.") 147 | config.lang.paste_me = Item("paste me ", 148 | doc="Command to paste the location of a target;" 149 | " must contain the extra.") 150 | config.load() 151 | 152 | 153 | #--------------------------------------------------------------------------- 154 | # Bring rule. 155 | 156 | class BringRule(CompoundRule): 157 | 158 | spec = config.lang.bring_me 159 | extras = [Choice("target", config.targets.mapping)] 160 | 161 | def _process_recognition(self, node, extras): 162 | target = extras["target"] 163 | self._log.debug("%s: bringing target %s." % (self, target)) 164 | target.bring_it() 165 | 166 | 167 | #--------------------------------------------------------------------------- 168 | # Paste rule. 169 | 170 | class PasteRule(CompoundRule): 171 | 172 | spec = config.lang.paste_me 173 | extras = [Choice("target", config.targets.mapping)] 174 | 175 | def _process_recognition(self, node, extras): 176 | target = extras["target"] 177 | self._log.debug("%s: pasting target %s." % (self, target)) 178 | Paste(target.target).execute() 179 | 180 | 181 | #--------------------------------------------------------------------------- 182 | # Create and manage this module's grammar. 183 | 184 | grammar = Grammar("bring me") 185 | grammar.add_rule(BringRule()) 186 | grammar.add_rule(PasteRule()) 187 | grammar.load() 188 | def unload(): 189 | global grammar 190 | if grammar: grammar.unload() 191 | grammar = None 192 | -------------------------------------------------------------------------------- /macros/_multiedit.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This is a config file for Dragonfly's _multiedit.py command-module. 3 | # To use this config, you must rename this file to _multiedit.txt and 4 | # place it in the same directory as the _multiedit.py file. 5 | # 6 | 7 | # Pull in all of Dragonfly's action objects so that we can use them here. 8 | 9 | from dragonfly import * 10 | 11 | 12 | #--------------------------------------------------------------------------- 13 | # Here we define the release action which releases all 14 | # modifier-keys used within this grammar. It is defined here 15 | # because this functionality is used in many different places. 16 | # Note that it is harmless to release ("...:up") a key multiple 17 | # times or when that key is not held down at all. 18 | 19 | release = Key("shift:up, ctrl:up") 20 | 21 | 22 | #--------------------------------------------------------------------------- 23 | # Here we define the single-action commands. These can be spoken 24 | # in series so as to execute multiple actions within a single utterance. 25 | 26 | cmd.map = { 27 | # Spoken-form -> -> -> Action object 28 | 29 | "up []": Key("up:%(n)d"), 30 | "down []": Key("down:%(n)d"), 31 | "left []": Key("left:%(n)d"), 32 | "right []": Key("right:%(n)d"), 33 | "page up []": Key("pgup:%(n)d"), 34 | "page down []": Key("pgdown:%(n)d"), 35 | "up (page | pages)": Key("pgup:%(n)d"), 36 | "down (page | pages)": Key("pgdown:%(n)d"), 37 | "left (word | words)": Key("c-left:%(n)d"), 38 | "right (word | words)": Key("c-right:%(n)d"), 39 | "home": Key("home"), 40 | "end": Key("end"), 41 | "doc home": Key("c-home"), 42 | "doc end": Key("c-end"), 43 | 44 | "space []": release + Key("space:%(n)d"), 45 | "enter []": release + Key("enter:%(n)d"), 46 | "tab []": Key("tab:%(n)d"), 47 | "delete []": release + Key("del:%(n)d"), 48 | "delete [ | this] (line|lines)": release + Key("home, s-down:%(n)d, del"), 49 | "backspace []": release + Key("backspace:%(n)d"), 50 | "pop up": release + Key("apps"), 51 | 52 | "paste": release + Key("c-v"), 53 | "duplicate ": release + Key("c-c, c-v:%(n)d"), 54 | "copy": release + Key("c-c"), 55 | "cut": release + Key("c-x"), 56 | "select all": release + Key("c-a"), 57 | "[hold] shift": Key("shift:down"), 58 | "release shift": Key("shift:up"), 59 | "[hold] control": Key("ctrl:down"), 60 | "release control": Key("ctrl:up"), 61 | "release [all]": release, 62 | 63 | "say ": release + Text("%(text)s"), 64 | "mimic ": release + Mimic(extra="text"), 65 | 66 | "upper adam" : Key("A"), 67 | "upper Alpha" : Key("A"), 68 | "upper bravo" : Key("B"), 69 | "upper Charlie" : Key("C"), 70 | "upper Callie" : Key("C"), 71 | "upper Delta" : Key("D"), 72 | "upper echo" : Key("E"), 73 | "upper foxtrot" : Key("F"), 74 | "upper golf" : Key("G"), 75 | "upper gamma" : Key("G"), 76 | "upper Juliet" : Key("J"), 77 | "upper hotel" : Key("H"), 78 | "upper India" : Key("I"), 79 | "upper iota" : Key("I"), 80 | "upper kilo" : Key("K"), 81 | "upper Lima" : Key("L"), 82 | "upper Mike" : Key("M"), 83 | "upper nancy" : Key("N"), 84 | "upper November" : Key("N"), 85 | "upper Oscar" : Key("O"), 86 | "upper Papa" : Key("P"), 87 | "upper Queen" : Key("Q"), 88 | "upper Romeo" : Key("R"), 89 | "upper Rico" : Key("R"), 90 | "upper soy" : Key("S"), 91 | "upper tango" : Key("T"), 92 | "upper toy" : Key("T"), 93 | "upper uniform" : Key("U"), 94 | "upper Victor" : Key("V"), 95 | "upper Van" : Key("V"), 96 | "upper whiskey" : Key("W"), 97 | "upper x-ray" : Key("X"), 98 | "upper yellow" : Key("Y"), 99 | "upper Zulu" : Key("Z"), 100 | "upper zebra" : Key("Z"), 101 | "Adam" : Key("a"), 102 | "Alpha" : Key("a"), 103 | "bravo" : Key("b"), 104 | "Charlie" : Key("c"), 105 | "Callie" : Key("c"), 106 | "Delta" : Key("d"), 107 | "echo" : Key("e"), 108 | "foxtrot" : Key("f"), 109 | "golf" : Key("g"), 110 | "gamma" : Key("g"), 111 | "Juliet" : Key("j"), 112 | "hotel" : Key("h"), 113 | "India" : Key("i"), 114 | "iota" : Key("i"), 115 | "kilo" : Key("k"), 116 | "Lima" : Key("l"), 117 | "Mike" : Key("m"), 118 | "Nancy" : Key("n"), 119 | "November" : Key("n"), 120 | "Oscar" : Key("o"), 121 | "Papa" : Key("p"), 122 | "Queen" : Key("q"), 123 | "Romeo" : Key("r"), 124 | "Rico" : Key("r"), 125 | "soy" : Key("s"), 126 | "tango" : Key("t"), 127 | "toy" : Key("t"), 128 | "uniform" : Key("u"), 129 | "Victor" : Key("v"), 130 | "Van" : Key("v"), 131 | "whiskey" : Key("w"), 132 | "x-ray" : Key("x"), 133 | "yellow" : Key("y"), 134 | "Zulu" : Key("z"), 135 | "zebra" : Key("z"), 136 | } 137 | 138 | 139 | #--------------------------------------------------------------------------- 140 | # Here we define various functions for formatting text. 141 | # Each of these functions must have a docstring which defines its 142 | # spoken-form. This docstring must include the "" extra. 143 | # See below for various examples. 144 | 145 | # Format: some_words 146 | def format_score(dictation): # Function name must start with "format_". 147 | """ score """ # Docstring defining spoken-form. 148 | text = str(dictation).lower() # Get written-form of dictated text. 149 | return "_".join(text.split(" ")) # Put underscores between words. 150 | 151 | def format_jive(dictation): # Function name must start with "format_". 152 | """ jive """ # Docstring defining spoken-form. 153 | text = str(dictation).lower() # Get written-form of dictated text. 154 | return "-".join(text.split(" ")) # Put underscores between words. 155 | 156 | def format_dotty(dictation): # Function name must start with "format_". 157 | """ Dotty """ # Docstring defining spoken-form. 158 | text = str(dictation).lower() # Get written-form of dictated text. 159 | return ".".join(text.split(" ")) # Put underscores between words. 160 | 161 | # Format: some_words() 162 | def format_under_function(dictation): 163 | """ under func """ 164 | text = str(dictation).lower() 165 | return "_".join(text.split(" ")) + "()" 166 | 167 | # Format: SomeWords 168 | def format_camel(dictation): 169 | """ camel """ 170 | text = str(dictation) 171 | words = [word.capitalize() for word in text.split(" ")] 172 | return "".join(words) 173 | 174 | # Format: somewords 175 | def format_one_word(dictation): 176 | """ [all] one word """ 177 | text = str(dictation) 178 | return "".join(text.split(" ")) 179 | 180 | # Format: SOMEWORDS 181 | def format_upper_one_word(dictation): 182 | """ one word upper """ 183 | text = str(dictation) 184 | words = [word.upper() for word in text.split(" ")] 185 | return "".join(words) 186 | 187 | # Format: SOME_WORDS 188 | def format_upper_score(dictation): 189 | """ upper score """ 190 | text = str(dictation) 191 | words = [word.upper() for word in text.split(" ")] 192 | return "_".join(words) 193 | 194 | # Format: someWords 195 | def format_studley(dictation): 196 | """ studley """ 197 | text = str(dictation) 198 | words = text.split(" ") 199 | return words[0] + "".join(w.capitalize() for w in words[1:]) 200 | 201 | -------------------------------------------------------------------------------- /macros/_vim.py: -------------------------------------------------------------------------------- 1 | from dragonfly import (Grammar, AppContext, MappingRule, Dictation, Key, Text, Integer, Mimic, Playback) 2 | vi = AppContext(title="vi") 3 | gvim = AppContext(title="GVIM") 4 | grammar = Grammar("vim", context=(vi | gvim)) 5 | 6 | noSpaceNoCaps = Mimic("\\no-caps-on") + Mimic("\\no-space-on") 7 | 8 | rules = MappingRule( 9 | name = "vim", 10 | mapping = { 11 | "north [by] []" : Key("escape, c-k:%(n)d"), 12 | "south [by] []" : Key("escape, c-j:%(n)d"), 13 | "east [by] []" : Key("escape, c-l:%(n)d"), 14 | "west [by] []" : Key("escape, c-h:%(n)d"), 15 | 16 | "split view": Key("escape, colon") + Text("split") + Key("enter"), 17 | "vertical view": Key("escape, colon") + Text("vsplit") + Key("enter"), 18 | "new split": Key("escape, colon") + Text("new") + Key("enter"), 19 | "new vertical": Key("escape, colon") + Text("vnew") + Key("enter"), 20 | 21 | "drop [by] []" : Text("%(n)d") + Key("j"), 22 | "rise [by] []" : Text("%(n)d") + Key("k"), 23 | "scroll up []" : Text("%(scroll_by)d") + Key("c-y"), 24 | "scroll down []" : Text("%(scroll_by)d") + Key("c-e"), 25 | 26 | "balance" : Key("escape, c-w, equal"), 27 | "insert" : Key("escape, i") + noSpaceNoCaps, 28 | "reflow" : Key("equals:2"), 29 | "flow" : Key("equals:2"), 30 | "indent" : Key("rangle"), 31 | "again" : Key("dot"), 32 | "out didn't" : Key("langle"), 33 | "toggle case": Key("tilde"), 34 | "after" : Key("escape, a") + noSpaceNoCaps, 35 | "big after" : Key("escape, A") + noSpaceNoCaps, 36 | "big insert" : Key("escape, I") + noSpaceNoCaps, 37 | "oh" : Key("escape, o") + noSpaceNoCaps, 38 | "bo" : Key("escape, O") + noSpaceNoCaps, 39 | "escape" : Key("escape"), 40 | "cancel" : Key("escape"), 41 | "find []" : Key("slash") + noSpaceNoCaps + Text("%(text)s"), 42 | "sip []" : Key("slash") + noSpaceNoCaps + Text("%(text)s"), 43 | "sup []" : Key("question") + noSpaceNoCaps + Text("%(text)s"), 44 | "reverse inside singles" : Key("question, squote, dot, backslash, lbrace, hyphen, rbrace, squote, enter, l"), 45 | "reverse inside doubles" : Key("question, dquote, dot, backslash, lbrace, hyphen, rbrace, dquote, enter, l"), 46 | "inside singles" : Key("slash, squote, dot, backslash, lbrace, hyphen, rbrace, squote, enter, l"), 47 | "inside doubles" : Key("slash, dquote, dot, backslash, lbrace, hyphen, rbrace, dquote, enter, l"), 48 | "transpose" : Key("x, p"), 49 | "file" : Key("c-f") + noSpaceNoCaps, 50 | 51 | # editin 52 | "diz" : Key("c, i, squote") + noSpaceNoCaps, 53 | "dib" : Key("c, i, dquote") + noSpaceNoCaps, 54 | "dip" : Key("c, i, lparen") + noSpaceNoCaps, 55 | "dice" : Key("c, i, lbrace") + noSpaceNoCaps, 56 | "dick" : Key("c, i, lbracket") + noSpaceNoCaps, 57 | 58 | "Dwight" : Key("c, w") + noSpaceNoCaps, 59 | 60 | "semi" : Key("escape, A") + Text(";") + noSpaceNoCaps, 61 | "commie" : Key("escape, A, comma") + noSpaceNoCaps, 62 | "corn hole" : Key("escape, A, colon, space") + noSpaceNoCaps, 63 | "align equals" : Key("colon") + Text("Align="), 64 | "align colon" : Key("colon") + Text("Align") + Key('colon'), 65 | 66 | # movements 67 | "bark []" : Key("b:%(n)d"), 68 | "woof []" : Key("w:%(n)d"), 69 | "eck []" : Key("e:%(n)d"), 70 | "beck []" : Text("%(n)d") + Key("g, e"), 71 | "race" : Key("question, backslash, s, backslash, S, enter"), 72 | "ace" : Key("slash, backslash, s, backslash, S, enter"), 73 | "junk" : Key("escape, slash, backslash, lparen, dquote, backslash, bar, squote, backslash, bar, lparen, backslash, bar, lbrace, backslash, bar, lbracket, backslash, bar, rparen, backslash, bar, rbrace, backslash, bar, rbracket, backslash, rparen, enter"), 74 | "leak" : Key("escape, slash, backslash, lparen, dquote, backslash, bar, squote, backslash, bar, rparen, backslash, bar, rbrace, backslash, bar, rbracket, backslash, rparen, enter, a"), 75 | "Buffer": Key("backslash, b"), 76 | "hop" : Key("space, e"), 77 | "trip" : Key("space, g, e"), 78 | "jump" : Key("space, w"), 79 | "Mark " : Key("escape, m, %(mark)s"), 80 | "jump to " : Key("escape, backtick, %(mark)s"), 81 | "next Mark": Key("rbracket, backtick"), 82 | "last mark": Key("lbracket, backtick"), 83 | "show marks": Key("escape") + Text(":marks") + Key("enter"), 84 | "squat" : Key("space, b"), 85 | "to end" : Key("dollar"), 86 | "to start" : Key("caret"), 87 | "to top" : Key("g,g"), 88 | "to bottom" : Key("G"), 89 | 90 | "viz" : Key("v, i, squote"), 91 | "vib" : Key("v, i, dquote"), 92 | "vip" : Key("v, i, lparen"), 93 | "vice" : Key("v, i, lbrace"), 94 | "vick" : Key("v, i, lbracket"), 95 | 96 | "outer single" : Key("v, a, squote"), 97 | "outer double" : Key("v, a, dquote"), 98 | "outer paren" : Key("v, a, lparen"), 99 | "outer brace" : Key("v, a, lbrace"), 100 | "outer bracket" : Key("v, a, lbracket"), 101 | 102 | "next [by] []" : Key("n:%(n)d"), 103 | "visual" : Key("escape, v"), 104 | "big visual" : Key("escape, V"), 105 | "call visual" : Key("escape, c-v"), 106 | "yank" : Key("y"), 107 | "big yank" : Key("Y"), 108 | "put" : Key("p"), 109 | "big put" : Key("P"), 110 | "duplicate line" : Key("Y, P"), 111 | "that's all" : Key("escape, g, g, V, G"), 112 | "comment out": Key("backslash, backslash"), 113 | 114 | "save" : Key("escape, colon, w, enter"), 115 | "close" : Key("escape, colon, q, enter"), 116 | "close all" : Key("escape, colon, q, a, l, l, enter"), 117 | "hard close" : Key("escape, colon, q, exclamation, enter"), 118 | "command []" : Key("escape, colon") + noSpaceNoCaps + Text("%(text)s"), 119 | "scratch" : Key("escape, u"), 120 | "oops" : Key("escape, u"), 121 | "redo" : Key("escape, c-r"), 122 | "spa" : Key("space"), 123 | "plus" : Key("space, plus, space"), 124 | "minus" : Key("space, minus, space"), 125 | "equal" : Key("space, equal, space"), 126 | "Ash" : Key("hyphen"), 127 | "kill []" : Key("x:%(n)d"), 128 | "kill line" : Key("d,d"), 129 | "kill word": Key("d,w"), 130 | "kill last word" : Key("escape, b, c, w"), 131 | "kill space" : Key("escape, m, z, question, backslash, s, enter, x, backtick, z, i"), 132 | "kill slash" : Key("escape, m, z, question, backslash, slash, enter, x, backtick, z, a"), 133 | "delete line" : Key("d,d"), 134 | "line " : Key("escape, colon") + Text("%(n)d") + Key("enter"), 135 | "nerd" : Key("escape, c-t"), 136 | "small braces" : Key("lbrace, rbrace, escape, i") + noSpaceNoCaps, 137 | "braces" : Key("lbrace, space, space, rbrace, escape, h, i") + noSpaceNoCaps, 138 | "small brackets" : Key("lbracket, rbracket, escape, i") + noSpaceNoCaps, 139 | "brackets" : Key("lbracket, space, space, rbracket, escape, h, i") + noSpaceNoCaps, 140 | "parens" : Key("lparen, rparen, escape, i"), 141 | "angles" : Key("langle, rangle, escape, i"), 142 | "doubles" : Key("dquote, dquote, escape, i"), 143 | "singles" : Key("squote, squote, escape, i"), 144 | "inc." : Key("c-a"), 145 | "deinc" : Key("c-x"), 146 | "bang": Key("exclamation"), 147 | "double bar": Text(" || "), 148 | "double or": Text(" || "), 149 | "double ampersand": Text(" && "), 150 | "double and": Text(" && "), 151 | "fat arrow": Text(" =>") + noSpaceNoCaps, 152 | "skinny arrow": Text(" ->") + noSpaceNoCaps, 153 | "triple equals" : Text(" === "), 154 | "triple not equals" : Text(" !== "), 155 | "jog" : Key("c-j"), 156 | "mysql split": Key("colon") + Text("ConqueTermSplit mysql -u root") + Key("enter"), 157 | "node split": Key("colon") + Text("ConqueTermSplit node") + Key("enter"), 158 | "file type JavaScript": Key("colon") + Text("set ft=javascript"), 159 | }, 160 | 161 | extras = [ 162 | Dictation("text", format=False), 163 | Dictation("mark", format=False), 164 | Integer("n", 1, 20000), 165 | Integer("scroll_by", 1, 20000), 166 | ], 167 | defaults = { 168 | "text" : "", 169 | "mark": "a", 170 | "n" : 1, 171 | "scroll_by" : 10 172 | } 173 | ) 174 | 175 | grammar.add_rule(rules) 176 | grammar.load() 177 | def unload(): 178 | global grammar 179 | if grammar: grammar.unload() 180 | grammar = None 181 | -------------------------------------------------------------------------------- /macros/_multiedit.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is a command-module for Dragonfly. 3 | # (c) Copyright 2008 by Christo Butcher 4 | # Licensed under the LGPL, see 5 | # 6 | 7 | """ 8 | Command-module for cursor movement and **editing** 9 | ============================================================================ 10 | 11 | This module allows the user to control the cursor and 12 | efficiently perform multiple text editing actions within a 13 | single phrase. 14 | 15 | 16 | Example commands 17 | ---------------------------------------------------------------------------- 18 | 19 | *Note the "/" characters in the examples below are simply 20 | to help the reader see the different parts of each voice 21 | command. They are not present in the actual command and 22 | should not be spoken.* 23 | 24 | Example: **"up 4 / down 1 page / home / space 2"** 25 | This command will move the cursor up 4 lines, down 1 page, 26 | move to the beginning of the line, and then insert 2 spaces. 27 | 28 | Example: **"left 7 words / backspace 3 / insert hello Cap world"** 29 | This command will move the cursor left 7 words, then delete 30 | the 3 characters before the cursor, and finally insert 31 | the text "hello World". 32 | 33 | Example: **"home / space 4 / down / 43 times"** 34 | This command will insert 4 spaces at the beginning of 35 | of this and the next 42 lines. The final "43 times" 36 | repeats everything in front of it that many times. 37 | 38 | 39 | Discussion of this module 40 | ---------------------------------------------------------------------------- 41 | 42 | This command-module creates a powerful voice command for 43 | editing and cursor movement. This command's structure can 44 | be represented by the following simplified language model: 45 | 46 | - *CommandRule* -- top-level rule which the user can say 47 | - *repetition* -- sequence of actions (name = "sequence") 48 | - *KeystrokeRule* -- rule that maps a single 49 | spoken-form to an action 50 | - *optional* -- optional specification of repeat count 51 | - *integer* -- repeat count (name = "n") 52 | - *literal* -- "times" 53 | 54 | The top-level command rule has a callback method which is 55 | called when this voice command is recognized. The logic 56 | within this callback is very simple: 57 | 58 | 1. Retrieve the sequence of actions from the element with 59 | the name "sequence". 60 | 2. Retrieve the repeat count from the element with the name 61 | "n". 62 | 3. Execute the actions the specified number of times. 63 | 64 | """ 65 | 66 | try: 67 | import pkg_resources 68 | pkg_resources.require("dragonfly >= 0.6.5beta1.dev-r99") 69 | except ImportError: 70 | pass 71 | 72 | from dragonfly import * 73 | 74 | 75 | #--------------------------------------------------------------------------- 76 | # Here we globally defined the release action which releases all 77 | # modifier-keys used within this grammar. It is defined here 78 | # because this functionality is used in many different places. 79 | # Note that it is harmless to release ("...:up") a key multiple 80 | # times or when that key is not held down at all. 81 | 82 | release = Key("shift:up, ctrl:up, alt:up") 83 | 84 | 85 | #--------------------------------------------------------------------------- 86 | # Set up this module's configuration. 87 | 88 | config = Config("multi edit") 89 | config.cmd = Section("Language section") 90 | config.cmd.map = Item( 91 | # Here we define the *default* command map. If you would like to 92 | # modify it to your personal taste, please *do not* make changes 93 | # here. Instead change the *config file* called "_multiedit.txt". 94 | { 95 | # Spoken-form -> -> -> Action object 96 | "up []": Key("up:%(n)d"), 97 | "down []": Key("down:%(n)d"), 98 | "left []": Key("left:%(n)d"), 99 | "right []": Key("right:%(n)d"), 100 | "page up []": Key("pgup:%(n)d"), 101 | "page down []": Key("pgdown:%(n)d"), 102 | "up (page | pages)": Key("pgup:%(n)d"), 103 | "down (page | pages)": Key("pgdown:%(n)d"), 104 | "left (word | words)": Key("c-left:%(n)d"), 105 | "right (word | words)": Key("c-right:%(n)d"), 106 | # "home": Key("home"), 107 | # "end": Key("end"), 108 | # "doc home": Key("c-home"), 109 | # "doc end": Key("c-end"), 110 | 111 | "space []": release + Key("space:%(n)d"), 112 | "enter []": release + Key("enter:%(n)d"), 113 | "tab []": Key("tab:%(n)d"), 114 | "delete []": release + Key("del:%(n)d"), 115 | "delete [ | this] (line|lines)": release + Key("home, s-down:%(n)d, del"), 116 | "backspace []": release + Key("backspace:%(n)d"), 117 | "pop up": release + Key("apps"), 118 | 119 | "paste": release + Key("c-v"), 120 | "duplicate ": release + Key("c-c, c-v:%(n)d"), 121 | "copy": release + Key("c-c"), 122 | "cut": release + Key("c-x"), 123 | # "select all": release + Key("c-a"), 124 | "[hold] shift": Key("shift:down"), 125 | "release shift": Key("shift:up"), 126 | "[hold] control": Key("ctrl:down"), 127 | "release control": Key("ctrl:up"), 128 | "release [all]": release, 129 | 130 | "say ": release + Text("%(text)s"), 131 | "mimic ": release + Mimic(extra="text"), 132 | }, 133 | namespace={ 134 | "Key": Key, 135 | "Text": Text, 136 | } 137 | ) 138 | namespace = config.load() 139 | #--------------------------------------------------------------------------- 140 | # Here we prepare the list of formatting functions from the config file. 141 | 142 | # Retrieve text-formatting functions from this module's config file. 143 | # Each of these functions must have a name that starts with "format_". 144 | format_functions = {} 145 | if namespace: 146 | for name, function in namespace.items(): 147 | if name.startswith("format_") and callable(function): 148 | spoken_form = function.__doc__.strip() 149 | 150 | # We wrap generation of the Function action in a function so 151 | # that its *function* variable will be local. Otherwise it 152 | # would change during the next iteration of the namespace loop. 153 | def wrap_function(function): 154 | def _function(dictation): 155 | formatted_text = function(dictation) 156 | Text(formatted_text).execute() 157 | return Function(_function) 158 | 159 | action = wrap_function(function) 160 | format_functions[spoken_form] = action 161 | 162 | 163 | # Here we define the text formatting rule. 164 | # The contents of this rule were built up from the "format_*" 165 | # functions in this module's config file. 166 | if format_functions: 167 | class FormatRule(MappingRule): 168 | 169 | mapping = format_functions 170 | extras = [Dictation("dictation")] 171 | 172 | else: 173 | FormatRule = None 174 | 175 | 176 | #--------------------------------------------------------------------------- 177 | # Here we define the keystroke rule. 178 | 179 | # This rule maps spoken-forms to actions. Some of these 180 | # include special elements like the number with name "n" 181 | # or the dictation with name "text". This rule is not 182 | # exported, but is referenced by other elements later on. 183 | # It is derived from MappingRule, so that its "value" when 184 | # processing a recognition will be the right side of the 185 | # mapping: an action. 186 | # Note that this rule does not execute these actions, it 187 | # simply returns them when it's value() method is called. 188 | # For example "up 4" will give the value Key("up:4"). 189 | # More information about Key() actions can be found here: 190 | # http://dragonfly.googlecode.com/svn/trunk/dragonfly/documentation/actionkey.html 191 | class KeystrokeRule(MappingRule): 192 | 193 | exported = False 194 | 195 | mapping = config.cmd.map 196 | extras = [ 197 | IntegerRef("n", 1, 100), 198 | Dictation("text"), 199 | Dictation("text2"), 200 | ] 201 | defaults = { 202 | "n": 1, 203 | } 204 | # Note: when processing a recognition, the *value* of 205 | # this rule will be an action object from the right side 206 | # of the mapping given above. This is default behavior 207 | # of the MappingRule class' value() method. It also 208 | # substitutes any "%(...)." within the action spec 209 | # with the appropriate spoken values. 210 | 211 | 212 | #--------------------------------------------------------------------------- 213 | # Here we create an element which is the sequence of keystrokes. 214 | 215 | # First we create an element that references the keystroke rule. 216 | # Note: when processing a recognition, the *value* of this element 217 | # will be the value of the referenced rule: an action. 218 | alternatives = [] 219 | alternatives.append(RuleRef(rule=KeystrokeRule())) 220 | if FormatRule: 221 | alternatives.append(RuleRef(rule=FormatRule())) 222 | single_action = Alternative(alternatives) 223 | 224 | # Second we create a repetition of keystroke elements. 225 | # This element will match anywhere between 1 and 16 repetitions 226 | # of the keystroke elements. Note that we give this element 227 | # the name "sequence" so that it can be used as an extra in 228 | # the rule definition below. 229 | # Note: when processing a recognition, the *value* of this element 230 | # will be a sequence of the contained elements: a sequence of 231 | # actions. 232 | sequence = Repetition(single_action, min=1, max=16, name="sequence") 233 | 234 | 235 | #--------------------------------------------------------------------------- 236 | # Here we define the top-level rule which the user can say. 237 | 238 | # This is the rule that actually handles recognitions. 239 | # When a recognition occurs, it's _process_recognition() 240 | # method will be called. It receives information about the 241 | # recognition in the "extras" argument: the sequence of 242 | # actions and the number of times to repeat them. 243 | class RepeatRule(CompoundRule): 244 | 245 | # Here we define this rule's spoken-form and special elements. 246 | spec = " [[[and] repeat [that]] times]" 247 | extras = [ 248 | sequence, # Sequence of actions defined above. 249 | IntegerRef("n", 1, 100), # Times to repeat the sequence. 250 | ] 251 | defaults = { 252 | "n": 1, # Default repeat count. 253 | } 254 | 255 | # This method gets called when this rule is recognized. 256 | # Arguments: 257 | # - node -- root node of the recognition parse tree. 258 | # - extras -- dict of the "extras" special elements: 259 | # . extras["sequence"] gives the sequence of actions. 260 | # . extras["n"] gives the repeat count. 261 | def _process_recognition(self, node, extras): 262 | sequence = extras["sequence"] # A sequence of actions. 263 | count = extras["n"] # An integer repeat count. 264 | for i in range(count): 265 | for action in sequence: 266 | action.execute() 267 | release.execute() 268 | 269 | 270 | #--------------------------------------------------------------------------- 271 | # Create and load this module's grammar. 272 | 273 | grammar = Grammar("multi edit") # Create this module's grammar. 274 | grammar.add_rule(RepeatRule()) # Add the top-level rule. 275 | grammar.load() # Load the grammar. 276 | 277 | # Unload function which will be called at unload time. 278 | def unload(): 279 | global grammar 280 | if grammar: grammar.unload() 281 | grammar = None 282 | -------------------------------------------------------------------------------- /macros/_firefox.py: -------------------------------------------------------------------------------- 1 | # 2 | # This file is a command-module for Dragonfly. 3 | # (c) Copyright 2008 by Christo Butcher 4 | # Licensed under the LGPL, see 5 | # 6 | 7 | """ 8 | Command-module for **Firefox** 9 | ============================================================================ 10 | 11 | This module offers direct control of the `Firefox 12 | `_ web browser. It 13 | requires the `mouseless-browsing 14 | `_ 15 | (mlb) add-on for reliable access to hyperlinks. 16 | 17 | This module includes direct searching using Firefox's 18 | search bar and Firefox's keyword searching. It also 19 | allows single-utterance submitting of text into form text 20 | fields. 21 | 22 | Installation 23 | ---------------------------------------------------------------------------- 24 | 25 | If you are using DNS and Natlink, simply place this file in you Natlink 26 | macros directory. It will then be automatically loaded by Natlink when 27 | you next toggle your microphone or restart Natlink. 28 | 29 | Customization 30 | ---------------------------------------------------------------------------- 31 | 32 | Users should customize this module by editing its 33 | configuration file. In this file they should edit the 34 | ``search.searchbar`` and ``search.keywords`` settings to 35 | match their own personal search preferences. These 36 | variables map *what you say* to which *search engines* to 37 | use. 38 | 39 | """ 40 | 41 | try: 42 | import pkg_resources 43 | pkg_resources.require("dragonfly >= 0.6.5beta1.dev-r76") 44 | except ImportError: 45 | pass 46 | 47 | from dragonfly import * 48 | 49 | 50 | #--------------------------------------------------------------------------- 51 | # Set up this module's configuration. 52 | 53 | config = Config("Firefox control") 54 | config.search = Section("Search-related section") 55 | config.search.keywords = Item( 56 | default={ 57 | "wikipedia": "wikipedia", 58 | }, 59 | doc="Mapping of spoken-forms to Firefox search-keywords.", 60 | ) 61 | config.search.searchbar = Item( 62 | default=[ 63 | "google", 64 | "yahoo", 65 | "amazon", 66 | "answers", 67 | "creative commons", 68 | "eBay", 69 | "wikipedia", 70 | ], 71 | 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.", 72 | ) 73 | 74 | config.lang = Section("Language section") 75 | config.lang.new_win = Item("new (window | win)") 76 | config.lang.new_tab = Item("new (tab | sub)") 77 | config.lang.close_tab = Item("close (tab | sub)") 78 | config.lang.close_tab_n = Item("close (tab | sub) ") 79 | config.lang.close_n_tabs = Item("close (tabs | subs)") 80 | config.lang.address_bar = Item("address [bar]") 81 | config.lang.copy_address = Item("copy address") 82 | config.lang.paste_address = Item("paste address") 83 | config.lang.search_bar = Item("search bar") 84 | config.lang.go_home = Item("go home") 85 | config.lang.stop_loading = Item("stop loading") 86 | config.lang.toggle_tags = Item("toggle tags") 87 | config.lang.fresh_tags = Item("fresh tags") 88 | config.lang.caret_browsing = Item("(caret | carrot) browsing") 89 | config.lang.bookmark_page = Item("bookmark [this] page") 90 | config.lang.save_page_as = Item("save [page | file] as") 91 | config.lang.print_page = Item("print [page | file]") 92 | config.lang.show_tab_n = Item("show tab ") 93 | config.lang.back = Item("back []") 94 | config.lang.forward = Item("forward []") 95 | config.lang.next_tab = Item("next tab []") 96 | config.lang.prev_tab = Item("(previous | preev) tab []") 97 | config.lang.normal_size = Item("normal text size") 98 | config.lang.smaller_size = Item("smaller text size []") 99 | config.lang.bigger_size = Item("bigger text size []") 100 | config.lang.find = Item("find") 101 | config.lang.find_text = Item("find ") 102 | config.lang.find_next = Item("find next []") 103 | 104 | config.lang.submit = Item("submit") 105 | config.lang.submit_text = Item("submit ") 106 | config.lang.submit_clipboard = Item("submit (clipboard | clip board)") 107 | config.lang.link_open = Item("[link] [open]") 108 | config.lang.link_save = Item("save [link] [as]") 109 | config.lang.link_save_now = Item("save [link] now now") 110 | config.lang.link_select = Item("[link] select") 111 | config.lang.link_menu = Item("[link] (menu | pop up)") 112 | config.lang.link_force = Item("[link] force") 113 | config.lang.link_window = Item("[link] [in [new]] window") 114 | config.lang.link_tab = Item("[link] [in [new]] tab") 115 | config.lang.link_copy = Item("[link] copy") 116 | config.lang.link_copy_into_tab = Item("[link] copy into tab") 117 | config.lang.link_list = Item("[link] list") 118 | config.lang.link_submit = Item("[link] submit") 119 | config.lang.link_submit_text = Item("[link] submit ") 120 | config.lang.link_submit_clipboard = Item("[link] submit (clipboard | clip board)") 121 | config.lang.link_dictation_box = Item("edit [link] ") 122 | config.lang.link_assign_keyword = Item("assign [a] keyword to [link] ") 123 | config.lang.tabify_links = Item("tab if I ") 124 | config.lang.tabify_links_sep = Item("comma") 125 | 126 | config.lang.search_text = Item("[power] search [for] ") 127 | config.lang.search_keyword_text = Item("[power] search [for] ") 128 | config.lang.search_searchbar_text = Item("[power] search [for] ") 129 | config.lang.search_clipboard = Item("[power] search [for] (clipboard | clip board)") 130 | config.lang.search_keyword_clipboard = Item("[power] search [for] clipboard") 131 | config.lang.search_searchbar_clipboard = Item("[power] search [for] clipboard") 132 | 133 | #config.generate_config_file() 134 | config.load() 135 | 136 | 137 | #--------------------------------------------------------------------------- 138 | # Check and prepare search-related config values. 139 | 140 | keywords = config.search.keywords 141 | searchbar = dict([(n,i) for i,n in enumerate(config.search.searchbar)]) 142 | 143 | 144 | #--------------------------------------------------------------------------- 145 | # Create the rule to match mouseless-browsing link numbers. 146 | 147 | class LinkRule(Rule): 148 | 149 | def __init__(self): 150 | element = Number(zero=True) 151 | Rule.__init__(self, "link_rule", element, exported=False) 152 | 153 | def value(self, node): 154 | # Format and return keystrokes to select the link. 155 | digits = str(node.children[0].value()) 156 | link_keys = "f6,s-f6," + ",".join(["numpad"+i for i in digits]) 157 | self._log.debug("Link keys: %r" % link_keys) 158 | return link_keys 159 | 160 | link = RuleRef(name="link", rule=LinkRule()) 161 | 162 | 163 | #--------------------------------------------------------------------------- 164 | # Create the main command rule. 165 | 166 | class CommandRule(MappingRule): 167 | 168 | mapping = { 169 | config.lang.new_win: Key("c-n"), 170 | config.lang.new_tab: Key("c-t"), 171 | config.lang.close_tab: Key("c-w"), 172 | config.lang.close_tab_n: Key("0, %(n)d, enter/20, c-w"), 173 | config.lang.close_n_tabs: Key("c-w/20:%(n)d"), 174 | config.lang.address_bar: Key("a-d"), 175 | config.lang.copy_address: Key("a-d, c-c"), 176 | config.lang.paste_address: Key("a-d, c-v, enter"), 177 | config.lang.search_bar: Key("c-k"), 178 | config.lang.go_home: Key("a-home"), 179 | config.lang.stop_loading: Key("escape"), 180 | config.lang.toggle_tags: Key("f12"), 181 | config.lang.fresh_tags: Key("f12, f12"), 182 | config.lang.caret_browsing: Key("f7"), 183 | config.lang.bookmark_page: Key("c-d"), 184 | config.lang.save_page_as: Key("c-s"), 185 | config.lang.print_page: Key("c-p"), 186 | 187 | config.lang.show_tab_n: Key("0, %(n)d, enter"), 188 | config.lang.back: Key("a-left/15:%(n)d"), 189 | config.lang.forward: Key("a-right/15:%(n)d"), 190 | config.lang.next_tab: Key("c-tab:%(n)d"), 191 | config.lang.prev_tab: Key("cs-tab:%(n)d"), 192 | 193 | config.lang.normal_size: Key("a-v/20, z/20, r"), 194 | config.lang.smaller_size: Key("c-minus:%(n)d"), 195 | config.lang.bigger_size: Key("cs-equals:%(n)d"), 196 | 197 | config.lang.submit: Key("enter"), 198 | config.lang.submit_text: Text("%(text)s") + Key("enter"), 199 | config.lang.submit_clipboard: Key("c-v, enter"), 200 | 201 | config.lang.find: Key("c-f"), 202 | config.lang.find_text: Key("c-f") + Text("%(text)s"), 203 | config.lang.find_next: Key("f3/10:%(n)d"), 204 | 205 | config.lang.link_open: Key("%(link)s, enter"), 206 | config.lang.link_save: Key("%(link)s, shift/10, apps/20, k"), 207 | config.lang.link_save_now: Key("%(link)s, shift/10, apps/20, k") 208 | + WaitWindow(title="Enter name of file") 209 | + Pause("20") + Key("enter"), 210 | config.lang.link_select: Key("%(link)s, shift"), 211 | config.lang.link_menu: Key("%(link)s, shift/10, apps"), 212 | config.lang.link_force: Key("%(link)s, shift/10, enter"), 213 | config.lang.link_window: Key("%(link)s, shift/10, apps/20, w"), 214 | config.lang.link_tab: Key("%(link)s, shift/10, apps/20, t"), 215 | config.lang.link_copy: Key("%(link)s, shift/10, apps/20, a"), 216 | config.lang.link_copy_into_tab: Key("%(link)s, shift/10, apps/20, a/10, c-t/20, c-v, enter"), 217 | config.lang.link_list: Key("%(link)s, enter, a-down"), 218 | config.lang.link_submit: Key("%(link)s, enter/30, enter"), 219 | config.lang.link_submit_text: Key("%(link)s, enter/30") 220 | + Text("%(text)s") + Key("enter"), 221 | config.lang.link_submit_clipboard: Key("%(link)s, enter/30, c-v, enter"), 222 | config.lang.link_dictation_box: Key("%(link)s, enter/30, cs-d"), 223 | config.lang.link_assign_keyword: Key("%(link)s, enter/10, apps/20, k"), 224 | 225 | config.lang.search_text: Key("c-k") 226 | + Text("%(text)s") + Key("enter"), 227 | config.lang.search_searchbar_text: Key("c-k, c-up:20, c-down:%(searchbar)d") 228 | + Text("%(text)s") + Key("enter"), 229 | config.lang.search_keyword_text: Key("a-d") 230 | + Text("%(keyword)s %(text)s") 231 | + Key("enter"), 232 | config.lang.search_clipboard: Key("c-k, c-v, enter"), 233 | config.lang.search_searchbar_clipboard: Key("c-k, c-up:20, c-down:%(searchbar)d, c-v, enter"), 234 | config.lang.search_keyword_clipboard: Key("a-d") + Text("%(keyword)s") 235 | + Key("c-v, enter"), 236 | } 237 | extras = [ 238 | link, 239 | IntegerRef("n", 1, 20), 240 | Dictation("text"), 241 | Choice("keyword", keywords), 242 | Choice("searchbar", searchbar), 243 | ] 244 | defaults = { 245 | "n": 1, 246 | } 247 | 248 | 249 | #--------------------------------------------------------------------------- 250 | # Create the command rule for sliding. 251 | 252 | slide_directions = { 253 | "up": (0,-1), 254 | "down": (0,+1), 255 | } 256 | slide_speeds = { 257 | "1": 10, 258 | "2": 20, 259 | "3": 30, 260 | "4": 40, 261 | } 262 | slide_default_speed = 15 263 | slide_start_spec = "(-15,0.6)" 264 | 265 | slide_grammar = None 266 | 267 | def start_sliding(direction, speed): 268 | offset_x = direction[0] * speed 269 | offset_y = direction[1] * speed 270 | offset_spec = "<%d,%d>" % (offset_x, offset_y) 271 | action = Key("escape") 272 | action.execute() 273 | action = Mouse("%s/25, middle/25, %s" % (slide_start_spec, offset_spec)) 274 | action.execute() 275 | 276 | global slide_grammar 277 | if not slide_grammar: 278 | slide_grammar = Grammar("Firefox slide grammar") 279 | slide_grammar.add_rule(SlideControlRule()) 280 | slide_grammar.load() 281 | slide_grammar.set_exclusive(True) 282 | 283 | def stop_sliding(): 284 | action = Key("escape") 285 | action.execute() 286 | 287 | global slide_grammar 288 | if slide_grammar: 289 | slide_grammar.set_exclusive(False) 290 | slide_grammar.unload() 291 | slide_grammar = None 292 | 293 | class SlideStartRule(MappingRule): 294 | 295 | mapping = { 296 | "slide []": Function(start_sliding), 297 | } 298 | extras = [ 299 | Choice("direction", slide_directions), 300 | Choice("speed", slide_speeds), 301 | ] 302 | defaults = { 303 | "speed": slide_default_speed, 304 | } 305 | 306 | 307 | class SlideControlRule(MappingRule): 308 | 309 | mapping = { 310 | "[slide] []": Function(start_sliding), 311 | "[slide] stop": Function(stop_sliding), 312 | } 313 | extras = [ 314 | Choice("direction", slide_directions), 315 | Choice("speed", slide_speeds), 316 | ] 317 | defaults = { 318 | "speed": slide_default_speed, 319 | } 320 | 321 | 322 | #--------------------------------------------------------------------------- 323 | # Create the main command rule. 324 | 325 | class TabifyRule(CompoundRule): 326 | 327 | spec = config.lang.tabify_links 328 | sep_element = Compound(config.lang.tabify_links_sep) 329 | repeat_element = Sequence([sep_element, link]) 330 | repetitions = Repetition(child=repeat_element, min=0, max=8) 331 | extras = [Sequence(name="links", children=(link, repetitions))] 332 | 333 | def _process_recognition(self, node, extras): 334 | link_nodes = node.get_children_by_name("link") 335 | for n in link_nodes: 336 | action = Key(n.value()) + Key("shift/10, apps/20, t/20") 337 | action.execute() 338 | 339 | 340 | #--------------------------------------------------------------------------- 341 | # Create and load this module's grammar. 342 | 343 | context = AppContext(executable="firefox") 344 | grammar = Grammar("firefox_general", context=context) 345 | grammar.add_rule(CommandRule()) 346 | grammar.add_rule(SlideStartRule()) 347 | grammar.add_rule(TabifyRule()) 348 | grammar.load() 349 | 350 | # Unload function which will be called by natlink at unload time. 351 | def unload(): 352 | global grammar 353 | if grammar: grammar.unload() 354 | grammar = None 355 | -------------------------------------------------------------------------------- /macros/_vocola_main.py: -------------------------------------------------------------------------------- 1 | # _vocola_main.py - NatLink support for Vocola 2 | # -*- coding: latin-1 -*- 3 | # 4 | # Contains: 5 | # - "Built-in" voice commands 6 | # - Autoloading of changed command files 7 | # 8 | # 9 | # Copyright (c) 2002-2012 by Rick Mohr. 10 | # 11 | # Portions Copyright (c) 2012-2013 by Hewlett-Packard Development Company, L.P. 12 | # 13 | # Permission is hereby granted, free of charge, to any person 14 | # obtaining a copy of this software and associated documentation files 15 | # (the "Software"), to deal in the Software without restriction, 16 | # including without limitation the rights to use, copy, modify, merge, 17 | # publish, distribute, sublicense, and/or sell copies of the Software, 18 | # and to permit persons to whom the Software is furnished to do so, 19 | # subject to the following conditions: 20 | # 21 | # The above copyright notice and this permission notice shall be 22 | # included in all copies or substantial portions of the Software. 23 | # 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 28 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 29 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 30 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | # SOFTWARE. 32 | 33 | 34 | # 35 | # WARNING: This version of _vocola_main.py has been modified to work 36 | # with Quintijn's installer/version of NatLink and has a 37 | # number of unofficial changes/improvements. The code has 38 | # been organized to minimize the diff's with the official 39 | # version. 40 | # 41 | 42 | import string 43 | import sys 44 | import os # access to file information 45 | import os.path # to parse filenames 46 | import time # print time in messages 47 | from stat import * # file statistics 48 | import re 49 | import natlink 50 | from natlinkutils import * 51 | 52 | 53 | 54 | ########################################################################### 55 | # # 56 | # Configuration # 57 | # # 58 | ########################################################################### 59 | 60 | # The Vocola translator is a perl program. By default we use the precompiled 61 | # executable vcl2py.exe, which doesn't require installing perl. 62 | # To instead use perl and vcl2py.pl, set the following variable to 1: 63 | usePerl = 0 64 | 65 | 66 | try: 67 | import natlinkstatus 68 | Quintijn_installer = True 69 | status = natlinkstatus.NatlinkStatus() 70 | VocolaEnabled = not not status.getVocolaUserDirectory() 71 | language = status.getLanguage() 72 | except ImportError: 73 | Quintijn_installer = False 74 | VocolaEnabled = True 75 | language = 'enx' 76 | 77 | 78 | # get location of MacroSystem folder: 79 | NatLinkFolder = os.path.split( 80 | sys.modules['natlinkmain'].__dict__['__file__'])[0] 81 | # (originally, natlinkmain lived in MacroSystem, not MacroSystem\core) 82 | NatLinkFolder = re.sub(r'\core$', "", NatLinkFolder) 83 | 84 | 85 | VocolaFolder = os.path.normpath(os.path.join(NatLinkFolder, '..', 'Vocola')) 86 | ExecFolder = os.path.normpath(os.path.join(NatLinkFolder, '..', 'Vocola', 'exec')) 87 | # C module "simpscrp" defines Exec(), which runs a program in a minimized 88 | # window and waits for completion. Since such modules need to be compiled 89 | # separately for each python version we need this careful import: 90 | pydFolder = os.path.normpath(os.path.join(NatLinkFolder, '..', 'Vocola', 'exec', sys.version[0:3])) 91 | ExtensionsFolder = os.path.normpath(os.path.join(NatLinkFolder, '..', 'Vocola', 'extensions')) 92 | 93 | NatLinkFolder = os.path.abspath(NatLinkFolder) 94 | 95 | if VocolaEnabled: 96 | sys.path.append(pydFolder) 97 | sys.path.append(ExecFolder) 98 | sys.path.append(ExtensionsFolder) 99 | 100 | 101 | def get_command_folder(): 102 | configured = None 103 | try: 104 | import natlinkstatus 105 | # Quintijn's's installer: 106 | configured = natlinkstatus.NatlinkStatus().getVocolaUserDirectory() 107 | except ImportError: 108 | try: 109 | import RegistryDict 110 | import win32con 111 | # Scott's installer: 112 | r = RegistryDict.RegistryDict(win32con.HKEY_CURRENT_USER, 113 | "Software\NatLink") 114 | if r: 115 | configured = r["VocolaUserDirectory"] 116 | except ImportError: 117 | pass 118 | if os.path.isdir(configured): 119 | return configured 120 | 121 | systemCommandFolder = os.path.join(VocolaFolder, 'Commands') 122 | if os.path.isdir(systemCommandFolder): 123 | return systemCommandFolder 124 | 125 | return None 126 | 127 | commandFolder = get_command_folder() 128 | if VocolaEnabled and not commandFolder: 129 | print >> sys.stderr, "Warning: no Vocola command folder found!" 130 | 131 | 132 | ## 133 | ## Quintijn's unofficial multiple language kludge: 134 | ## 135 | 136 | def copyVclFileLanguageVersion(Input, Output): 137 | """copy to another location, keeping the include files one directory above 138 | """ 139 | # let include lines to relative paths point to the folder above ..\ 140 | # so you can take the same include file for the alternate language. 141 | reInclude = re.compile(r'(include\s+)\w') 142 | Input = os.path.normpath(Input) 143 | Output = os.path.normpath(Output) 144 | input = open(Input, 'r').read() 145 | output = open(Output, 'w') 146 | output.write("# vocola file for alternate language: %s\n"% language) 147 | lines = map(string.strip, str(input).split('\n')) 148 | for line in lines: 149 | if reInclude.match(line): 150 | line = 'include ..\\' + line[8:] 151 | output.write(line + '\n') 152 | output.close() 153 | 154 | def copyToNewSubDirectory(trunk, subdirectory): 155 | for f in os.listdir(trunk): 156 | if f.endswith('.vcl'): 157 | copyVclFileLanguageVersion(os.path.join(trunk, f), 158 | os.path.join(subdirectory, f)) 159 | 160 | if VocolaEnabled and status.getVocolaTakesLanguages(): 161 | print '_vocola_main started with language: %s' % language 162 | if language != 'enx' and commandFolder: 163 | uDir = commandFolder 164 | uDir2 = os.path.join(uDir, language) 165 | if not os.path.isdir(uDir2): 166 | print 'creating userCommandFolder for language %s' % language 167 | os.mkdir(uDir2) 168 | copyToNewSubDirectory(uDir, uDir2) 169 | commandFolder = uDir2 170 | 171 | 172 | 173 | ########################################################################### 174 | # # 175 | # The built-in commands # 176 | # # 177 | ########################################################################### 178 | 179 | class ThisGrammar(GrammarBase): 180 | 181 | gramSpec = """ 182 | exported = [Show] (NatLink|Vocola) Window; 183 | exported = Edit [Voice] Commands; 184 | exported = Edit Global [Voice] Commands; 185 | exported = Edit Machine [Voice] Commands; 186 | exported = Edit Global Machine [Voice] Commands; 187 | exported = Load All [Voice] Commands; 188 | exported = Load [Voice] Commands; 189 | exported = Load Global [Voice] Commands; 190 | exported = Load [Voice] Extensions; 191 | exported = Discard Old [Voice] Commands; 192 | """ 193 | 194 | if language == 'nld': 195 | gramSpec = """ 196 | exported = Toon (NatLink|Vocola) venster; 197 | exported = (Eddit|Bewerk|Sjoo|Toon)(Commandoos|Commands) | 198 | (Eddit|Bewerk|Sjoo|Toon)(stem|vojs)(Commandoos|Commands); 199 | exported = (Eddit|Bewerk|Sjoo|Toon) (Global|globale) [stem|vojs] (Commandoos|Commands); 200 | exported = (Eddit|Bewerk|Sjoo|Toon) Machine [stem|vojs] (Commandoos|Commands); 201 | exported = (Eddit|Bewerk|Sjoo|Toon) (Global|globale) Machine [stem|vojs] (Commandoos|Commands); 202 | exported = (Laad|Lood) alle [stem|vojs] (Commandoos|Commands); 203 | exported = (Laad|Lood) [stem|vojs] (Commandoos|Commands); 204 | exported = (Laad|Lood) globale [stem|vojs] (Commandoos|Commands); 205 | exported = Laad [stem] extensies; 206 | exported = (Discard|Verwijder) (oude|oold) [stem|vojs] (Commandoos|Commands); 207 | """ 208 | elif language == 'fra': 209 | gramSpec = """ 210 | exported = [Afficher] Fenetre (NatLink|Vocola); 211 | exported = Editer Commandes [Vocales]; 212 | exported = Editer Commandes [Vocales] Globales; 213 | exported = Editer Commandes [Vocales] Machine; 214 | exported = Editer Commandes [Vocales] Globales Machine; 215 | exported = Charger Toutes Les Commandes [Vocales]; 216 | exported = Charger Commandes [Vocales]; 217 | exported = Charger Commandes [Vocales] Globales; 218 | exported = Charger Extensions [Vocales]; 219 | exported = Effacer Commandes [Vocales] Precedentes; 220 | """ 221 | elif language == 'deu': 222 | gramSpec = """ 223 | exported = [Zeige] (NatLink|Vocola) Fenster; 224 | exported = Bearbeite [Sprach] Befehle; 225 | exported = Bearbeite globale [Sprach] Befehle; 226 | exported = Bearbeite Maschinen [Sprach] Befehle; 227 | exported = Bearbeite globale Maschinen [Sprach] Befehle; 228 | exported = Lade alle [Sprach] Befehle; 229 | exported = Lade [Sprach] Befehle; 230 | exported = Lade globale [Sprach] Befehle; 231 | exported = Lade [Sprach] Extensions; 232 | exported = Verwerfe alte [Sprach] Befehle; 233 | """ 234 | elif language == 'ita': 235 | gramSpec = """ 236 | exported = [Mostra] Finestra Di (NatLink|Vocola); 237 | exported = Modifica Comandi [Vocali]; 238 | exported = Modifica Comandi [Vocali] Globali; 239 | exported = Modifica Comandi [Vocali] [del] Computer; 240 | exported = Modifica Comandi [Vocali] Globali [del] Computer; 241 | exported = Carica Tutti I Comandi [Vocali]; 242 | exported = Carica I Comandi [Vocali]; 243 | exported = Carica Comandi [Vocali] Gliobali; 244 | exported = Carica Estensioni [Vocali]; 245 | exported = Annulla Vecchi Comandi [Vocali]; 246 | """ 247 | elif language == 'esp': 248 | gramSpec = """ 249 | exported = [Mostrar] Ventana de (NatLink|Vocola) ; 250 | exported = (Modificar|Editar) Comandos [de voz]; 251 | exported = (Modificar|Editar) Comandos [de voz] Globales ; 252 | exported = (Modificar|Editar) Comandos [de voz] de (este ordenador|la Computadora); 253 | exported = (Modificar|Editar) Comandos [de voz] Globales de (este ordenador|la Computadora); 254 | exported = (Recargar|Cargar) Todos Los Comandos [de voz]; 255 | exported = (Recargar|Cargar) Comandos [de voz]; 256 | exported = (Recargar|Cargar) Comandos [de voz] Globales; 257 | exported = (Recargar|Cargar) Extensiones [de voz]; 258 | exported = Descartar Comandos [de voz] Viejos; 259 | """ 260 | elif language != 'enx': 261 | print >> sys.stderr, """\n\n 262 | Vocola Warning: no language "%s" translations for the built-in Vocola 263 | commands (e.g., commands to load voice commands) are currently 264 | available; consider helping translate them -- inquire on 265 | http://www.speechcomputing.com. For now the English versions, like "Edit 266 | Commands" and "Edit Global Commands" are activated. 267 | """ % language 268 | 269 | 270 | def initialize(self): 271 | self.updateUnimacroHeaderIfNeeded() 272 | 273 | if os.environ.has_key('COMPUTERNAME'): 274 | self.machine = string.lower(os.environ['COMPUTERNAME']) 275 | else: self.machine = 'local' 276 | 277 | self.load_extensions() 278 | self.loadAllFiles(False) 279 | 280 | self.load(self.gramSpec) 281 | self.activateAll() 282 | 283 | def gotBegin(self,moduleInfo): 284 | self.currentModule = moduleInfo 285 | # delay enabling until now to avoid NatLink clobbering our callback: 286 | enable_callback() 287 | 288 | 289 | # Get app name by stripping folder and extension from currentModule name 290 | def getCurrentApplicationName(self): 291 | return string.lower(os.path.splitext(os.path.split(self.currentModule[0]) [1]) [0]) 292 | 293 | 294 | ### Miscellaneous commands 295 | 296 | # "Show NatLink Window" -- print to output window so it appears 297 | def gotResults_NatLinkWindow(self, words, fullResults): 298 | print "This is the NatLink/Vocola output window" 299 | 300 | # "Load Extensions" -- scan for new/changed extensions: 301 | def gotResults_loadExtensions(self, words, fullResults): 302 | self.load_extensions(True) 303 | for module in sys.modules.keys(): 304 | if module.startswith("vocola_ext_"): 305 | del sys.modules[module] 306 | 307 | def load_extensions(self, verbose=False): 308 | #if sys.modules.has_key("scan_extensions"): 309 | # del sys.modules["scan_extensions"] 310 | import scan_extensions 311 | arguments = ["scan_extensions", ExtensionsFolder] 312 | if verbose: 313 | arguments.insert(1, "-v") 314 | scan_extensions.main(arguments) 315 | 316 | 317 | ### Loading Vocola Commands 318 | 319 | # "Load All Commands" -- translate all Vocola files 320 | def gotResults_loadAll(self, words, fullResults): 321 | self.loadAllFiles(True) 322 | 323 | # "Load Commands" -- translate Vocola files for current application 324 | def gotResults_loadCurrent(self, words, fullResults): 325 | self.loadSpecificFiles(self.getCurrentApplicationName()) 326 | 327 | # "Load Global Commands" -- translate global Vocola files 328 | def gotResults_loadGlobal(self, words, fullResults): 329 | self.loadSpecificFiles('') 330 | 331 | # "Discard Old [Voice] Commands" -- purge output then translate all files 332 | def gotResults_discardOld(self, words, fullResults): 333 | purgeOutput() 334 | self.loadAllFiles(True) 335 | 336 | # Load all command files 337 | def loadAllFiles(self, force): 338 | if commandFolder: 339 | compile_Vocola(commandFolder, force) 340 | 341 | # Load command files for specific application 342 | def loadSpecificFiles(self, module): 343 | special = re.compile(r'([][()^$.+*?{\\])') 344 | pattern = "^" + special.sub(r'\\\1', module) 345 | pattern += "(_[^@]*)?(@" + special.sub(r'\\\1', self.machine) 346 | pattern += ")?\.vcl$" 347 | p = re.compile(pattern) 348 | 349 | targets = [] 350 | if commandFolder: 351 | targets += [os.path.join(commandFolder,f) 352 | for f in os.listdir(commandFolder) if p.search(f)] 353 | if len(targets) > 0: 354 | for target in targets: 355 | self.loadFile(target) 356 | else: 357 | print >> sys.stderr 358 | if module == "": 359 | print >> sys.stderr, "Found no Vocola global command files [for machine '" + self.machine + "']" 360 | else: 361 | print >> sys.stderr, "Found no Vocola command files for application '" + module + "' [for machine '" + self.machine + "']" 362 | 363 | # Load a specific command file, returning false if not present 364 | def loadFile(self, file): 365 | try: 366 | os.stat(file) 367 | compile_Vocola(file, False) 368 | return True 369 | except OSError: 370 | return False # file not found 371 | 372 | 373 | ### Editing Vocola Command Files 374 | 375 | # "Edit Commands" -- open command file for current application 376 | def gotResults_edit(self, words, fullResults): 377 | app = self.getCurrentApplicationName() 378 | file = app + '.vcl' 379 | comment = 'Voice commands for ' + app 380 | self.openCommandFile(file, comment) 381 | 382 | # "Edit Machine Commands" -- open command file for current app & machine 383 | def gotResults_editMachine(self, words, fullResults): 384 | app = self.getCurrentApplicationName() 385 | file = app + '@' + self.machine + '.vcl' 386 | comment = 'Voice commands for ' + app + ' on ' + self.machine 387 | self.openCommandFile(file, comment) 388 | 389 | # "Edit Global Commands" -- open global command file 390 | def gotResults_editGlobal(self, words, fullResults): 391 | file = '_vocola.vcl' 392 | comment = 'Global voice commands' 393 | self.openCommandFile(file, comment) 394 | 395 | # "Edit Global Machine Commands" -- open global command file for machine 396 | def gotResults_editGlobalMachine(self, words, fullResults): 397 | file = '_vocola@' + self.machine + '.vcl' 398 | comment = 'Global voice commands on ' + self.machine 399 | self.openCommandFile(file, comment) 400 | 401 | 402 | def FindExistingCommandFile(self, file): 403 | if commandFolder: 404 | f = commandFolder + '\\' + file 405 | if os.path.isfile(f): return f 406 | 407 | return "" 408 | 409 | # Open a Vocola command file (using the application associated with ".vcl") 410 | def openCommandFile(self, file, comment): 411 | if not commandFolder: 412 | print >> sys.stderr, "Error: Unable to create command file because no Vocola command folder found." 413 | return 414 | 415 | path = self.FindExistingCommandFile(file) 416 | if not path: 417 | path = commandFolder + '\\' + file 418 | 419 | new = open(path, 'w') 420 | new.write('# ' + comment + '\n\n') 421 | # insert include line to Unimacro.vch: 422 | if status.getVocolaTakesUnimacroActions(): 423 | if language == 'enx' or not status.getVocolaTakesLanguages(): 424 | includeLine = 'include Unimacro.vch;\n\n' 425 | else: 426 | includeLine = 'include ..\\Unimacro.vch;\n\n' 427 | new.write(includeLine) 428 | new.close() 429 | 430 | wantedPath = os.path.join(commandFolder, file) 431 | if path and path != wantedPath: 432 | # copy from other location 433 | if wantedPath.startswith(path) and len(wantedPath) - len(path) == 3: 434 | print 'copying enx version to language version %s'% language 435 | copyVclFileLanguageVersion(path, wantedPath) 436 | else: 437 | print 'copying from other location' 438 | self.copyVclFile(path, wantedPath) 439 | path = wantedPath 440 | 441 | # 442 | # NatLink/DNS bug causes os.startfile or wpi32api.ShellExecute 443 | # to crash DNS if allResults is on in *any* grammer (e.g., Unimacro) 444 | # 445 | # Accordingly, use AppBringUp instead: 446 | # 447 | 448 | #try: 449 | # os.startfile(path) 450 | #except WindowsError, e: 451 | # print 452 | # print "Unable to open voice command file with associated editor: " + str(e) 453 | # print "Trying to open it with notepad instead." 454 | # prog = os.path.join(os.getenv('WINDIR'), 'notepad.exe') 455 | # os.spawnv(os.P_NOWAIT, prog, [prog, path]) 456 | natlink.execScript("AppBringUp \"" + path + "\", \"" + path + "\"") 457 | 458 | def copyVclFile(self, Input, Output): 459 | """copy to another location 460 | """ 461 | # QH, febr, 5, 2008 462 | Input = os.path.normpath(Input) 463 | Output = os.path.normpath(Output) 464 | 465 | input = open(Input, 'r').read() 466 | output = open(Output, 'w') 467 | output.write("# vocola file from a sample directory %s\n"% Input) 468 | lines = map(string.strip, str(input).split('\n')) 469 | for line in lines: 470 | output.write(line + '\n') 471 | output.close() 472 | 473 | def updateUnimacroHeaderIfNeeded(self): 474 | import shutil 475 | if not status.getVocolaTakesUnimacroActions(): 476 | return 477 | 478 | destDir = status.getVocolaUserDirectory() 479 | sourceDir = os.path.join(status.getUserDirectory(), 'vocola_compatibility') 480 | destPath = os.path.join(destDir, 'Unimacro.vch') 481 | sourcePath = os.path.join(sourceDir, 'Unimacro.vch') 482 | sourceTime, destTime = vocolaGetModTime(sourcePath), vocolaGetModTime(destPath) 483 | 484 | if not (sourceTime or destTime): 485 | print >> sys.stderr, """\n 486 | Error: The option "Vocola Takes Unimacro Actions" is switched on, but 487 | no file "Unimacro.vch" is found. 488 | 489 | Please fix the configuration of NatLink/Vocola/Unimacro and restart 490 | Dragon. Either ensure the source file is at: 491 | "%s", 492 | or switch off the option "Vocola Takes Unimacro Actions". 493 | """% sourceDir 494 | return 495 | 496 | if destTime < sourceTime: 497 | try: 498 | shutil.copyfile(sourcePath, destPath) 499 | except IOError: 500 | print >> sys.stderr, """\n 501 | Warning: Could not copy example "Unimacro.vch" to: 502 | "%s". 503 | 504 | There is a valid "Unimacro.vch" available, but a newer file is 505 | available at: "%s". 506 | 507 | Please fix the configuration of NatLink/Vocola/Unimacro and restart 508 | Dragon, if you want to use the updated version of this file."""% (destDir, sourceDir) 509 | else: 510 | print 'Succesfully copied "Unimacro.vch" from\n\t"%s" to\n\t"%s".'% (sourceDir, destDir) 511 | 512 | 513 | 514 | 515 | ########################################################################### 516 | # # 517 | # Compiling Vocola files # 518 | # # 519 | ########################################################################### 520 | 521 | may_have_compiled = False # has the compiler been called? 522 | compile_error = False # has a compiler error occurred? 523 | 524 | # Run Vocola compiler, converting command files from "inputFileOrFolder" 525 | # and writing output to NatLink/MacroSystem 526 | def compile_Vocola(inputFileOrFolder, force): 527 | global may_have_compiled, compiler_error 528 | 529 | may_have_compiled = True 530 | 531 | # below line currently needed because kludge changes the the folder: 532 | VocolaFolder = os.path.normpath(os.path.join(NatLinkFolder, '..', 'Vocola')) 533 | if usePerl: 534 | executable = "perl" 535 | arguments = [VocolaFolder + r'\exec\vcl2py.pl'] 536 | else: 537 | executable = VocolaFolder + r'\exec\vcl2py.exe' 538 | arguments = [] 539 | executable = sys.prefix + r'\python.exe' 540 | arguments = [VocolaFolder + r'\exec\vcl2py.py'] 541 | 542 | arguments += ['-extensions', ExtensionsFolder + r'\extensions.csv'] 543 | if language == "enx": 544 | arguments += ['-numbers', 545 | 'zero,one,two,three,four,five,six,seven,eight,nine'] 546 | 547 | arguments += ["-suffix", "_vcl" ] 548 | if force: arguments += ["-f"] 549 | 550 | arguments += [inputFileOrFolder, NatLinkFolder] 551 | hidden_call(executable, arguments) 552 | 553 | logName = commandFolder + r'\vcl2py_log.txt' 554 | if os.path.isfile(logName): 555 | try: 556 | log = open(logName, 'r') 557 | compiler_error = True 558 | print >> sys.stderr, log.read() 559 | log.close() 560 | os.remove(logName) 561 | except IOError: # no log file means no Vocola errors 562 | pass 563 | 564 | # Unload all commands, including those of files no longer existing 565 | def purgeOutput(): 566 | pattern = re.compile("_vcl\d*\.pyc?$") 567 | [os.remove(os.path.join(NatLinkFolder,f)) for f 568 | in os.listdir(NatLinkFolder) if pattern.search(f)] 569 | 570 | # 571 | # Run program with path executable and arguments arguments. Waits for 572 | # the program to finish. Runs the program in a hidden window. 573 | # 574 | def hidden_call(executable, arguments): 575 | args = [executable] + arguments 576 | try: 577 | # Using simpscrp is depreciated; remove '_disabled' below to use: 578 | import simpscrp_disabled 579 | args = ['"' + str(x) + '"' for x in args] 580 | call = ' '.join(args) 581 | simpscrp.Exec(call, 1) 582 | except ImportError: 583 | try: 584 | import subprocess 585 | si = subprocess.STARTUPINFO() 586 | # Location of below constants seems to vary from Python 587 | # version to version so hardcode them: 588 | si.dwFlags = 1 # subprocess.STARTF_USESHOWWINDOW 589 | si.wShowWindow = 0 # subprocess.SW_HIDE 590 | return subprocess.call(args, startupinfo=si) 591 | except ImportError: 592 | pid = os.spawnv(os.P_NOWAIT, executable, args) 593 | pid, exit_code = os.waitpid(pid, 0) 594 | exit_code = exit_code >> 8 595 | return exit_code 596 | 597 | 598 | lastVocolaFileTime = 0 599 | lastCommandFolderTime = 0 600 | 601 | def compile_changed(): 602 | global lastVocolaFileTime, lastCommandFolderTime 603 | global compiler_error 604 | 605 | current = getLastVocolaFileModTime() 606 | if current > lastVocolaFileTime: 607 | compiler_error = False 608 | thisGrammar.loadAllFiles(False) 609 | if not compiler_error: 610 | lastVocolaFileTime = current 611 | 612 | #source_changed = False 613 | #if commandFolder: 614 | # if vocolaGetModTime(commandFolder) > lastCommandFolderTime: 615 | # lastCommandFolderTime = vocolaGetModTime(commandFolder) 616 | # source_changed = True 617 | #if source_changed: 618 | # deleteOrphanFiles() 619 | 620 | # Returns the newest modified time of any Vocola command folder file or 621 | # 0 if none: 622 | def getLastVocolaFileModTime(): 623 | last = 0 624 | if commandFolder: 625 | last = max([last] + 626 | [vocolaGetModTime(os.path.join(commandFolder,f)) 627 | for f in os.listdir(commandFolder)]) 628 | return last 629 | 630 | # Returns the modification time of a file or 0 if the file does not exist: 631 | def vocolaGetModTime(file): 632 | try: return os.stat(file)[ST_MTIME] 633 | except OSError: return 0 # file not found 634 | 635 | 636 | def deleteOrphanFiles(): 637 | print "checking for orphans..." 638 | for f in os.listdir(NatLinkFolder): 639 | if not re.search("_vcl.pyc?$", f): continue 640 | 641 | s = getSourceFilename(f) 642 | if s: 643 | if vocolaGetModTime(s)>0: continue 644 | 645 | f = os.path.join(NatLinkFolder, f) 646 | print "Deleting: " + f 647 | os.remove(f) 648 | 649 | def getSourceFilename(output_filename): 650 | m = re.match("^(.*)_vcl.pyc?$", output_filename) 651 | if not m: return None # Not a Vocola file 652 | name = m.group(1) 653 | if not commandFolder: return None 654 | 655 | marker = "e_s_c_a_p_e_d__" 656 | m = re.match("^(.*)" + marker + "(.*)$", name) # rightmost marker! 657 | if m: 658 | name = m.group(1) 659 | tail = m.group(2) 660 | tail = re.sub("__a_t__", "@", tail) 661 | tail = re.sub("___", "_", tail) 662 | name += tail 663 | 664 | name = re.sub("_@", "@", name) 665 | return commandFolder + "\\" + name + ".vcl" 666 | 667 | 668 | lastNatLinkModTime = 0 669 | 670 | # Check for changes to our output .py files and report status relative 671 | # to last time this routine was called; return code means: 672 | # 0: no changes 673 | # 1: 1 or more existing .py files were modified, but no new .py files created 674 | # 2: one or more new .py files may have been created, plus maybe existing changed 675 | def output_changes(): 676 | global lastNatLinkModTime, may_have_compiled 677 | 678 | old_may_have_compiled = may_have_compiled 679 | may_have_compiled = False 680 | 681 | current = vocolaGetModTime(NatLinkFolder) 682 | if current > lastNatLinkModTime: 683 | lastNatLinkModTime = current 684 | return 2 685 | 686 | if old_may_have_compiled: 687 | return 1 688 | else: 689 | return 0 690 | 691 | 692 | # When speech is heard this function will be called before any others. 693 | # 694 | # Must return result of output_changes() so we can tell NatLink when 695 | # files need to be loaded. 696 | def utterance_start_callback(moduleInfo): 697 | compile_changed() 698 | return output_changes() 699 | 700 | 701 | 702 | ########################################################################### 703 | # # 704 | # Callback handling # 705 | # # 706 | ########################################################################### 707 | 708 | # 709 | # With Quintijn's installer as of February 4, 2008: 710 | # 711 | # _vocola_main is loaded before any other NatLink modules 712 | # vocolaBeginCallback is called directly by natlinkmain before any 713 | # other grammer's gotBegin method 714 | # natlinkmain now guarantees we are not called with CallbackDepth>1 715 | # we return the result of output_changes() directly rather than 716 | # massaging NatLink to deal with new .py files 717 | # 718 | 719 | 720 | callback_enabled = False 721 | 722 | def enable_callback(): 723 | global callback_enabled 724 | if not callback_enabled: 725 | callback_enabled = True 726 | if not Quintijn_installer: 727 | # Replace NatLink's "begin" callback function with ours: 728 | natlink.setBeginCallback(vocolaBeginCallback) 729 | 730 | def disable_callback(): 731 | global callback_enabled 732 | callback_enabled = False 733 | if not Quintijn_installer: 734 | natlink.setBeginCallback(beginCallback) 735 | 736 | 737 | def vocolaBeginCallback(moduleInfo): 738 | if not callback_enabled: 739 | return 0 740 | 741 | changes = 0 742 | if Quintijn_installer or getCallbackDepth()<2: 743 | changes = utterance_start_callback(moduleInfo) 744 | 745 | if Quintijn_installer: 746 | return changes 747 | else: 748 | if changes > 1: 749 | # make sure NatLink sees any new .py files: 750 | natlinkmain.findAndLoadFiles() 751 | natlinkmain.loadModSpecific(moduleInfo) 752 | natlinkmain.beginCallback(moduleInfo) 753 | 754 | 755 | 756 | ########################################################################### 757 | # # 758 | # Startup/shutdown # 759 | # # 760 | ########################################################################### 761 | 762 | thisGrammar = None 763 | 764 | # remove previous Vocola/Python compilation output as it may be out of 765 | # date (e.g., new compiler, source file deleted, partially written due 766 | # to crash, new machine name, etc.): 767 | purgeOutput() 768 | 769 | if not VocolaEnabled: 770 | print "Vocola not active" 771 | else: 772 | print "Vocola version 2.8I starting..." 773 | thisGrammar = ThisGrammar() 774 | thisGrammar.initialize() 775 | 776 | 777 | def unload(): 778 | global thisGrammar 779 | disable_callback() 780 | if thisGrammar: thisGrammar.unload() 781 | thisGrammar = None 782 | --------------------------------------------------------------------------------