rum 0.0.1-x86-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +3 -0
 - data/README +30 -0
 - data/Rakefile +134 -0
 - data/bin/rum-client +124 -0
 - data/doc/basic.rb +10 -0
 - data/doc/doc.html +602 -0
 - data/doc/example.rb +59 -0
 - data/doc/reference.rb +415 -0
 - data/doc/resources/bg.png +0 -0
 - data/doc/resources/bottom.png +0 -0
 - data/doc/resources/build.rb +235 -0
 - data/doc/resources/doc.haml +167 -0
 - data/doc/resources/emacs-auto-completion.png +0 -0
 - data/doc/resources/flash.png +0 -0
 - data/doc/resources/highlight.css +94 -0
 - data/doc/resources/intro.rb +17 -0
 - data/doc/resources/left.png +0 -0
 - data/doc/resources/logo.png +0 -0
 - data/doc/resources/screen.css +420 -0
 - data/doc/resources/screenshot.png +0 -0
 - data/doc/resources/top.png +0 -0
 - data/ext/mac/keyboard_hook/English.lproj/InfoPlist.strings +0 -0
 - data/ext/mac/keyboard_hook/Event.h +17 -0
 - data/ext/mac/keyboard_hook/Event.m +18 -0
 - data/ext/mac/keyboard_hook/EventTap.h +11 -0
 - data/ext/mac/keyboard_hook/EventTap.m +77 -0
 - data/ext/mac/keyboard_hook/Info.plist +26 -0
 - data/ext/mac/keyboard_hook/KeyboardHook.xcodeproj/TemplateIcon.icns +0 -0
 - data/ext/mac/keyboard_hook/KeyboardHook.xcodeproj/project.pbxproj +323 -0
 - data/ext/mac/keyboard_hook/KeyboardHook_Prefix.pch +7 -0
 - data/ext/mac/keyboard_hook/version.plist +16 -0
 - data/ext/windows/keyboard_hook/extconf.rb +2 -0
 - data/ext/windows/keyboard_hook/keyboard_hook.c +126 -0
 - data/ext/windows/system/autohotkey_stuff.c +255 -0
 - data/ext/windows/system/autohotkey_stuff.h +2 -0
 - data/ext/windows/system/clipboard_watcher.c +58 -0
 - data/ext/windows/system/clipboard_watcher.h +2 -0
 - data/ext/windows/system/extconf.rb +3 -0
 - data/ext/windows/system/input_box.c +239 -0
 - data/ext/windows/system/input_box.h +4 -0
 - data/ext/windows/system/system.c +273 -0
 - data/lib/rum.rb +4 -0
 - data/lib/rum/apps.rb +4 -0
 - data/lib/rum/barrel.rb +157 -0
 - data/lib/rum/barrel/emacs.rb +44 -0
 - data/lib/rum/barrel/emacs_client.rb +74 -0
 - data/lib/rum/core.rb +125 -0
 - data/lib/rum/dsl.rb +109 -0
 - data/lib/rum/gui.rb +93 -0
 - data/lib/rum/help.rb +128 -0
 - data/lib/rum/hotkey_core.rb +479 -0
 - data/lib/rum/mac.rb +18 -0
 - data/lib/rum/mac/app.rb +4 -0
 - data/lib/rum/mac/apps.rb +19 -0
 - data/lib/rum/mac/gui.rb +26 -0
 - data/lib/rum/mac/gui/growl.rb +54 -0
 - data/lib/rum/mac/irb/completion.rb +207 -0
 - data/lib/rum/mac/keyboard_hook.rb +73 -0
 - data/lib/rum/mac/layouts.rb +146 -0
 - data/lib/rum/mac/system.rb +45 -0
 - data/lib/rum/remote.rb +48 -0
 - data/lib/rum/server.rb +92 -0
 - data/lib/rum/windows.rb +23 -0
 - data/lib/rum/windows/app.rb +72 -0
 - data/lib/rum/windows/apps.rb +25 -0
 - data/lib/rum/windows/gui.rb +116 -0
 - data/lib/rum/windows/keyboard.rb +80 -0
 - data/lib/rum/windows/keyboard_hook.rb +20 -0
 - data/lib/rum/windows/keyboard_hook.so +0 -0
 - data/lib/rum/windows/layouts.rb +232 -0
 - data/lib/rum/windows/system.rb +310 -0
 - data/lib/rum/windows/system.so +0 -0
 - data/lib/rum/windows/system_foreign_functions.rb +129 -0
 - data/rum.gemspec +14 -0
 - metadata +156 -0
 
    
        data/lib/rum/gui.rb
    ADDED
    
    | 
         @@ -0,0 +1,93 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Rum
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Gui
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend self
         
     | 
| 
      
 4 
     | 
    
         
            +
                
         
     | 
| 
      
 5 
     | 
    
         
            +
                def self.use gui_module, *methods
         
     | 
| 
      
 6 
     | 
    
         
            +
                  if methods.empty?
         
     | 
| 
      
 7 
     | 
    
         
            +
                    include gui_module
         
     | 
| 
      
 8 
     | 
    
         
            +
                    extend self
         
     | 
| 
      
 9 
     | 
    
         
            +
                    gui_module.auto_setup if gui_module.respond_to? :auto_setup
         
     | 
| 
      
 10 
     | 
    
         
            +
                  else
         
     | 
| 
      
 11 
     | 
    
         
            +
                    methods.each do |method|
         
     | 
| 
      
 12 
     | 
    
         
            +
                      method = gui_module.method(method)
         
     | 
| 
      
 13 
     | 
    
         
            +
                      define_method(method.name) do |*args, &block|
         
     | 
| 
      
 14 
     | 
    
         
            +
                        method.call(*args, &block)
         
     | 
| 
      
 15 
     | 
    
         
            +
                      end
         
     | 
| 
      
 16 
     | 
    
         
            +
                    end
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def message text, *args, &callback
         
     | 
| 
      
 21 
     | 
    
         
            +
                  sticky = !!args.delete(:sticky)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  title  = args.first
         
     | 
| 
      
 23 
     | 
    
         
            +
                  message_backend(text, title, sticky, callback)
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def alert text, title=nil
         
     | 
| 
      
 27 
     | 
    
         
            +
                  Rum.switch_worker_thread
         
     | 
| 
      
 28 
     | 
    
         
            +
                  title ||= 'Rum'
         
     | 
| 
      
 29 
     | 
    
         
            +
                  alert_backend(text, title)
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def read(text='', args=nil)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  Rum.switch_worker_thread
         
     | 
| 
      
 34 
     | 
    
         
            +
                  if text.is_a? Hash
         
     | 
| 
      
 35 
     | 
    
         
            +
                    args = text
         
     | 
| 
      
 36 
     | 
    
         
            +
                    text = nil
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
                  if args
         
     | 
| 
      
 39 
     | 
    
         
            +
                    default = args[:default]
         
     | 
| 
      
 40 
     | 
    
         
            +
                    title   = args[:title]
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  read_backend(text, (title or 'Rum'), default)
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                def choose prompt=nil, choices
         
     | 
| 
      
 46 
     | 
    
         
            +
                  Rum.switch_worker_thread
         
     | 
| 
      
 47 
     | 
    
         
            +
                  choose_backend(prompt, choices)
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def browse url
         
     | 
| 
      
 51 
     | 
    
         
            +
                  url = 'http://' + url unless url =~ /^\w+:\/\//
         
     | 
| 
      
 52 
     | 
    
         
            +
                  browse_backend url
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                module CocoaDialog
         
     | 
| 
      
 56 
     | 
    
         
            +
                  private
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  def alert_backend prompt, title
         
     | 
| 
      
 59 
     | 
    
         
            +
                    result = dialog('ok-msgbox', '--text', prompt.to_s, '--title', title.to_s) 
         
     | 
| 
      
 60 
     | 
    
         
            +
                    result.first == '1'
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  def message_backend text, title, sticky, callback
         
     | 
| 
      
 64 
     | 
    
         
            +
                    alert text, title
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  def read_backend text, title, default
         
     | 
| 
      
 68 
     | 
    
         
            +
                    text    = ['--informative-text', text.to_s] if text
         
     | 
| 
      
 69 
     | 
    
         
            +
                    title   = ['--title', title.to_s]   if title
         
     | 
| 
      
 70 
     | 
    
         
            +
                    default = ['--text',  default.to_s] if default
         
     | 
| 
      
 71 
     | 
    
         
            +
                    result  = dialog('standard-inputbox', *text, *default, *title)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    result.shift == '1' ? result.join(' ') : ''
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  def choose_backend prompt, choices
         
     | 
| 
      
 76 
     | 
    
         
            +
                    prompt = ['--text', prompt.to_s] if prompt
         
     | 
| 
      
 77 
     | 
    
         
            +
                    result = dialog('standard-dropdown', '--items', *choices.map(&:to_s), *prompt)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    choices[result[1].to_i] if result.first == '1'
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
                  
         
     | 
| 
      
 81 
     | 
    
         
            +
                  def dialog *args
         
     | 
| 
      
 82 
     | 
    
         
            +
                    IO.popen([@@binary, *args]) { |p| p.read }.split
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  def self.setup binary=nil
         
     | 
| 
      
 86 
     | 
    
         
            +
                    unless binary
         
     | 
| 
      
 87 
     | 
    
         
            +
                      raise ArgumentError, 'Please provide a path to the CocoaDialog binary.'
         
     | 
| 
      
 88 
     | 
    
         
            +
                    end
         
     | 
| 
      
 89 
     | 
    
         
            +
                    @@binary = binary
         
     | 
| 
      
 90 
     | 
    
         
            +
                  end
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
              end
         
     | 
| 
      
 93 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/rum/help.rb
    ADDED
    
    | 
         @@ -0,0 +1,128 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Rum
         
     | 
| 
      
 2 
     | 
    
         
            +
              def self.reference
         
     | 
| 
      
 3 
     | 
    
         
            +
                reference = File.join(File.dirname(__FILE__), '..', '..',
         
     | 
| 
      
 4 
     | 
    
         
            +
                                      'doc', 'reference.rb')
         
     | 
| 
      
 5 
     | 
    
         
            +
                Gui.open_file reference
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
              
         
     | 
| 
      
 8 
     | 
    
         
            +
              def self.read_key &proc
         
     | 
| 
      
 9 
     | 
    
         
            +
                KeyReader.start(hotkey_processor, proc)
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              def self.show_hotkey
         
     | 
| 
      
 13 
     | 
    
         
            +
                read_key { |hotkey| Gui.message "'#{hotkey}' not active" }
         
     | 
| 
      
 14 
     | 
    
         
            +
                Action.hook = lambda do |action|
         
     | 
| 
      
 15 
     | 
    
         
            +
                  KeyReader.remove_hooks
         
     | 
| 
      
 16 
     | 
    
         
            +
                  action.show_definition or Gui.message "Definition location unkown."
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # Must return true for the key that triggered the action to be
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # retained by the processor.
         
     | 
| 
      
 19 
     | 
    
         
            +
                  true
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              def self.print_next_hotkey
         
     | 
| 
      
 24 
     | 
    
         
            +
                read_key { |hotkey| Gui.message hotkey }
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              def self.snippet
         
     | 
| 
      
 28 
     | 
    
         
            +
                Gui.message 'Enter hotkey!'
         
     | 
| 
      
 29 
     | 
    
         
            +
                read_key do |hotkey|
         
     | 
| 
      
 30 
     | 
    
         
            +
                  snippet = "'#{hotkey}'.do {  }"
         
     | 
| 
      
 31 
     | 
    
         
            +
                  Keyboard.type! snippet
         
     | 
| 
      
 32 
     | 
    
         
            +
                  Keyboard.type '(left)(left)'
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              module KeyReader
         
     | 
| 
      
 37 
     | 
    
         
            +
                extend self
         
     | 
| 
      
 38 
     | 
    
         
            +
                attr_accessor :pressed_modifiers
         
     | 
| 
      
 39 
     | 
    
         
            +
                
         
     | 
| 
      
 40 
     | 
    
         
            +
                Hook = proc do
         
     | 
| 
      
 41 
     | 
    
         
            +
                  @pass_key = false
         
     | 
| 
      
 42 
     | 
    
         
            +
                  if @key.modifier?
         
     | 
| 
      
 43 
     | 
    
         
            +
                    if @down
         
     | 
| 
      
 44 
     | 
    
         
            +
                      KeyReader.pressed_modifiers << @key
         
     | 
| 
      
 45 
     | 
    
         
            +
                    elsif seen_key = KeyReader.pressed_modifiers.delete(@key)
         
     | 
| 
      
 46 
     | 
    
         
            +
                      if @key == @last_key
         
     | 
| 
      
 47 
     | 
    
         
            +
                        KeyReader.stop(@key, @pressed_modifiers) 
         
     | 
| 
      
 48 
     | 
    
         
            +
                      elsif @pressed_modifiers.values.compact.empty?
         
     | 
| 
      
 49 
     | 
    
         
            +
                        KeyReader.stop
         
     | 
| 
      
 50 
     | 
    
         
            +
                      end
         
     | 
| 
      
 51 
     | 
    
         
            +
                    else
         
     | 
| 
      
 52 
     | 
    
         
            +
                      @pass_key = true
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
                  elsif @down and not @was_executed[@key]
         
     | 
| 
      
 55 
     | 
    
         
            +
                    KeyReader.stop(@key, @pressed_modifiers)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
                
         
     | 
| 
      
 59 
     | 
    
         
            +
                def start(hotkey_processor, proc)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  @hotkey_processor = hotkey_processor
         
     | 
| 
      
 61 
     | 
    
         
            +
                  @proc = proc
         
     | 
| 
      
 62 
     | 
    
         
            +
                  @pressed_modifiers = []
         
     | 
| 
      
 63 
     | 
    
         
            +
                  add_hooks
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                def stop key=nil, modifiers=nil
         
     | 
| 
      
 67 
     | 
    
         
            +
                  if key
         
     | 
| 
      
 68 
     | 
    
         
            +
                    modifiers = modifiers.keys.select { |key| modifiers[key] }
         
     | 
| 
      
 69 
     | 
    
         
            +
                    key = Hotkey.new(key, modifiers)
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
                  remove_hooks
         
     | 
| 
      
 72 
     | 
    
         
            +
                  @proc.call(key)
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                def add_hooks
         
     | 
| 
      
 76 
     | 
    
         
            +
                  @hotkey_processor.add_hook Hook
         
     | 
| 
      
 77 
     | 
    
         
            +
                  Action.hook = lambda { |action| } # Ignore Actions.
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                def remove_hooks
         
     | 
| 
      
 81 
     | 
    
         
            +
                  @hotkey_processor.remove_hook Hook
         
     | 
| 
      
 82 
     | 
    
         
            +
                  Action.hook = nil
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
              end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
              module WindowInfo
         
     | 
| 
      
 87 
     | 
    
         
            +
                module_function
         
     | 
| 
      
 88 
     | 
    
         
            +
                
         
     | 
| 
      
 89 
     | 
    
         
            +
                def start
         
     | 
| 
      
 90 
     | 
    
         
            +
                  return if @thread
         
     | 
| 
      
 91 
     | 
    
         
            +
                  register_stop_key
         
     | 
| 
      
 92 
     | 
    
         
            +
                  @stop = false
         
     | 
| 
      
 93 
     | 
    
         
            +
                  @thread = Thread.new do
         
     | 
| 
      
 94 
     | 
    
         
            +
                    new = old = nil
         
     | 
| 
      
 95 
     | 
    
         
            +
                    loop do
         
     | 
| 
      
 96 
     | 
    
         
            +
                      break if @stop
         
     | 
| 
      
 97 
     | 
    
         
            +
                      new = active_window
         
     | 
| 
      
 98 
     | 
    
         
            +
                      if new != old
         
     | 
| 
      
 99 
     | 
    
         
            +
                        Gui.message new.report
         
     | 
| 
      
 100 
     | 
    
         
            +
                        old = new
         
     | 
| 
      
 101 
     | 
    
         
            +
                      end
         
     | 
| 
      
 102 
     | 
    
         
            +
                      sleep 0.1
         
     | 
| 
      
 103 
     | 
    
         
            +
                    end
         
     | 
| 
      
 104 
     | 
    
         
            +
                    Clipboard.set new.report
         
     | 
| 
      
 105 
     | 
    
         
            +
                    Gui.message 'Window Info stopped.'
         
     | 
| 
      
 106 
     | 
    
         
            +
                    @thread = nil
         
     | 
| 
      
 107 
     | 
    
         
            +
                  end
         
     | 
| 
      
 108 
     | 
    
         
            +
                end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                StopKey = 'escape'
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                def register_stop_key
         
     | 
| 
      
 113 
     | 
    
         
            +
                  @old_action = StopKey.unregister
         
     | 
| 
      
 114 
     | 
    
         
            +
                  StopKey.do { WindowInfo.stop }
         
     | 
| 
      
 115 
     | 
    
         
            +
                  Gui.message "Press '#{StopKey}' to stop WindowInfo."
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                def stop
         
     | 
| 
      
 119 
     | 
    
         
            +
                  return unless @thread
         
     | 
| 
      
 120 
     | 
    
         
            +
                  @stop = true
         
     | 
| 
      
 121 
     | 
    
         
            +
                  if @old_action
         
     | 
| 
      
 122 
     | 
    
         
            +
                    @old_action.register
         
     | 
| 
      
 123 
     | 
    
         
            +
                  else
         
     | 
| 
      
 124 
     | 
    
         
            +
                    StopKey.unregister
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
      
 126 
     | 
    
         
            +
                end
         
     | 
| 
      
 127 
     | 
    
         
            +
              end
         
     | 
| 
      
 128 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,479 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Rum
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Key
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_accessor :name, :id, :aliases, :modifier
         
     | 
| 
      
 4 
     | 
    
         
            +
                
         
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize name, aliases, id
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @name = name
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @id = id
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @aliases = aliases
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @modifier = false
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
                
         
     | 
| 
      
 12 
     | 
    
         
            +
                def modifier?
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @modifier
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def inspect
         
     | 
| 
      
 17 
     | 
    
         
            +
                  "#<Key:#{name}>"
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              class Layout
         
     | 
| 
      
 22 
     | 
    
         
            +
                attr_accessor :ids, :names, :aliases, :modifiers, :core_modifiers, \
         
     | 
| 
      
 23 
     | 
    
         
            +
                              :action_modifiers, :translations
         
     | 
| 
      
 24 
     | 
    
         
            +
                
         
     | 
| 
      
 25 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @ids = {}
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @names = {}
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @aliases = {}
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @modifiers = []
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @core_modifiers = {}
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # Windows-specific: Modifiers that trigger actions when pressed and released.
         
     | 
| 
      
 32 
     | 
    
         
            +
                  @action_modifiers = {}
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @translations = {}
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def keys
         
     | 
| 
      
 37 
     | 
    
         
            +
                  @ids.values.uniq
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def lookup_id id
         
     | 
| 
      
 41 
     | 
    
         
            +
                  @ids[id]
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def [] attribute
         
     | 
| 
      
 45 
     | 
    
         
            +
                  @names[attribute] or @aliases[attribute] or @ids[attribute]
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                # Modifiers can have the names of other keys as aliases.
         
     | 
| 
      
 49 
     | 
    
         
            +
                # This allows for abbreviating 'ctrl shift enter'.do to 'c s enter'.do
         
     | 
| 
      
 50 
     | 
    
         
            +
                # Lookup the alias first to avoid matching normal keys.
         
     | 
| 
      
 51 
     | 
    
         
            +
                def modifier_lookup attribute
         
     | 
| 
      
 52 
     | 
    
         
            +
                  if (key = @aliases[attribute] || @names[attribute]) and key.modifier?
         
     | 
| 
      
 53 
     | 
    
         
            +
                    key
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                def add *args, id
         
     | 
| 
      
 58 
     | 
    
         
            +
                  id += 2**9 if args.delete :extended # Windows-specific magic value.
         
     | 
| 
      
 59 
     | 
    
         
            +
                                                      # An explicit implementation might follow
         
     | 
| 
      
 60 
     | 
    
         
            +
                  name = args.shift
         
     | 
| 
      
 61 
     | 
    
         
            +
                  aliases = args
         
     | 
| 
      
 62 
     | 
    
         
            +
                  key = Key.new(name, aliases, id)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  
         
     | 
| 
      
 64 
     | 
    
         
            +
                  @ids[id] = key
         
     | 
| 
      
 65 
     | 
    
         
            +
                  @names[name] = key
         
     | 
| 
      
 66 
     | 
    
         
            +
                  aliases.each { |key_alias| @aliases[key_alias] = key }
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                def alias key, key_alias
         
     | 
| 
      
 70 
     | 
    
         
            +
                  key = self[key]
         
     | 
| 
      
 71 
     | 
    
         
            +
                  @aliases[key_alias] = key
         
     | 
| 
      
 72 
     | 
    
         
            +
                  key.aliases << key_alias
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                def rename from, to
         
     | 
| 
      
 76 
     | 
    
         
            +
                  key = self[from]
         
     | 
| 
      
 77 
     | 
    
         
            +
                  @names.delete from
         
     | 
| 
      
 78 
     | 
    
         
            +
                  key.name = to
         
     | 
| 
      
 79 
     | 
    
         
            +
                  @names[to] = key
         
     | 
| 
      
 80 
     | 
    
         
            +
                end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                def remap *from, to
         
     | 
| 
      
 83 
     | 
    
         
            +
                  to = self[to]
         
     | 
| 
      
 84 
     | 
    
         
            +
                  from.each { |key| @ids[self[key].id] = to }
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                def modifier key
         
     | 
| 
      
 88 
     | 
    
         
            +
                  key = self[key]
         
     | 
| 
      
 89 
     | 
    
         
            +
                  key.modifier = true
         
     | 
| 
      
 90 
     | 
    
         
            +
                  @modifiers << key unless @modifiers.include? key
         
     | 
| 
      
 91 
     | 
    
         
            +
                  key
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                def core_modifier key
         
     | 
| 
      
 95 
     | 
    
         
            +
                  @core_modifiers[modifier(key)] = true
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                def action_modifier key
         
     | 
| 
      
 99 
     | 
    
         
            +
                  @action_modifiers[modifier(key)] = true
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
              end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
              module Layouts
         
     | 
| 
      
 104 
     | 
    
         
            +
                def self.list
         
     | 
| 
      
 105 
     | 
    
         
            +
                  layouts = methods(nil) - [:default_layout, :core, :basic, :list]
         
     | 
| 
      
 106 
     | 
    
         
            +
                  layouts - Object.methods # Needed for MacRuby, methods(nil) doesn't work here.
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
              end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
              class Hotkey
         
     | 
| 
      
 111 
     | 
    
         
            +
                attr_accessor :key, :modifiers, :actions, :direction # todo: unveränderlich machen
         
     | 
| 
      
 112 
     | 
    
         
            +
                
         
     | 
| 
      
 113 
     | 
    
         
            +
                def initialize key, modifiers=[], fuzzy=false, actions=[]
         
     | 
| 
      
 114 
     | 
    
         
            +
                  @key = key
         
     | 
| 
      
 115 
     | 
    
         
            +
                  @modifiers = modifiers
         
     | 
| 
      
 116 
     | 
    
         
            +
                  @fuzzy = fuzzy
         
     | 
| 
      
 117 
     | 
    
         
            +
                  @actions = actions
         
     | 
| 
      
 118 
     | 
    
         
            +
                  @direction = nil
         
     | 
| 
      
 119 
     | 
    
         
            +
                end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                def fuzzy?
         
     | 
| 
      
 122 
     | 
    
         
            +
                  @fuzzy
         
     | 
| 
      
 123 
     | 
    
         
            +
                end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                def execute repeated
         
     | 
| 
      
 126 
     | 
    
         
            +
                  @actions.each { |action| return true if action.execute(repeated) }
         
     | 
| 
      
 127 
     | 
    
         
            +
                  false
         
     | 
| 
      
 128 
     | 
    
         
            +
                end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                def to_s
         
     | 
| 
      
 131 
     | 
    
         
            +
                  (modifiers.dup << key).map(&:name).join ' '
         
     | 
| 
      
 132 
     | 
    
         
            +
                end
         
     | 
| 
      
 133 
     | 
    
         
            +
              end
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
              class Action
         
     | 
| 
      
 136 
     | 
    
         
            +
                attr_accessor :action, :condition, :hotkey, :location
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                @@hook = nil
         
     | 
| 
      
 139 
     | 
    
         
            +
                
         
     | 
| 
      
 140 
     | 
    
         
            +
                def self.work_queue= queue
         
     | 
| 
      
 141 
     | 
    
         
            +
                  @@work_queue = queue
         
     | 
| 
      
 142 
     | 
    
         
            +
                end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                def self.hook= proc
         
     | 
| 
      
 145 
     | 
    
         
            +
                  @@hook = proc
         
     | 
| 
      
 146 
     | 
    
         
            +
                end
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                def self.hook
         
     | 
| 
      
 149 
     | 
    
         
            +
                  @@hook
         
     | 
| 
      
 150 
     | 
    
         
            +
                end
         
     | 
| 
      
 151 
     | 
    
         
            +
                
         
     | 
| 
      
 152 
     | 
    
         
            +
                def initialize(action, condition, repeated, location)
         
     | 
| 
      
 153 
     | 
    
         
            +
                  @action = action
         
     | 
| 
      
 154 
     | 
    
         
            +
                  @condition = condition
         
     | 
| 
      
 155 
     | 
    
         
            +
                  @repeated = repeated
         
     | 
| 
      
 156 
     | 
    
         
            +
                  @location = location
         
     | 
| 
      
 157 
     | 
    
         
            +
                end
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                def execute repeated
         
     | 
| 
      
 160 
     | 
    
         
            +
                  return false if not @action or repeated and not @repeated
         
     | 
| 
      
 161 
     | 
    
         
            +
                  if not @condition or @condition.call
         
     | 
| 
      
 162 
     | 
    
         
            +
                    if @@hook
         
     | 
| 
      
 163 
     | 
    
         
            +
                      @@hook.call(self)
         
     | 
| 
      
 164 
     | 
    
         
            +
                    else
         
     | 
| 
      
 165 
     | 
    
         
            +
                      @@work_queue.enq @action
         
     | 
| 
      
 166 
     | 
    
         
            +
                      true
         
     | 
| 
      
 167 
     | 
    
         
            +
                    end
         
     | 
| 
      
 168 
     | 
    
         
            +
                  end
         
     | 
| 
      
 169 
     | 
    
         
            +
                end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                def show_definition
         
     | 
| 
      
 172 
     | 
    
         
            +
                  @location.show if @location
         
     | 
| 
      
 173 
     | 
    
         
            +
                end
         
     | 
| 
      
 174 
     | 
    
         
            +
              end
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
              # TODO: Needs refactoring.
         
     | 
| 
      
 177 
     | 
    
         
            +
              class HotkeySet
         
     | 
| 
      
 178 
     | 
    
         
            +
                attr_accessor :layout, :hotkeys, :modifiers, :down, :up, \
         
     | 
| 
      
 179 
     | 
    
         
            +
                              :up_fuzzy, :down_fuzzy, :modifier_hotkeys
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                def initialize layout
         
     | 
| 
      
 182 
     | 
    
         
            +
                  @layout = layout
         
     | 
| 
      
 183 
     | 
    
         
            +
                  # All hotkeys that consist entirely of modifiers
         
     | 
| 
      
 184 
     | 
    
         
            +
                  @modifier_hotkeys = {}
         
     | 
| 
      
 185 
     | 
    
         
            +
                  @modifiers = layout.modifiers.sort_by &:id
         
     | 
| 
      
 186 
     | 
    
         
            +
                  # All modifier combinations of all registered hotkeys
         
     | 
| 
      
 187 
     | 
    
         
            +
                  @modifier_combinations = Hash.new(0)
         
     | 
| 
      
 188 
     | 
    
         
            +
                  @up = {}
         
     | 
| 
      
 189 
     | 
    
         
            +
                  @down = {}
         
     | 
| 
      
 190 
     | 
    
         
            +
                  @up_fuzzy = {}
         
     | 
| 
      
 191 
     | 
    
         
            +
                  @down_fuzzy = {}
         
     | 
| 
      
 192 
     | 
    
         
            +
                end
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                def add_hotkey(string, action, condition, repeated, location)
         
     | 
| 
      
 195 
     | 
    
         
            +
                  action = Action.new(action, condition, repeated, location)
         
     | 
| 
      
 196 
     | 
    
         
            +
                  action.hotkey = hotkey_from_string(string)
         
     | 
| 
      
 197 
     | 
    
         
            +
                  register(action)
         
     | 
| 
      
 198 
     | 
    
         
            +
                end
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
      
 200 
     | 
    
         
            +
                def remove_hotkey string
         
     | 
| 
      
 201 
     | 
    
         
            +
                  hotkey = hotkey_from_string(string)
         
     | 
| 
      
 202 
     | 
    
         
            +
                  Rum.hotkey_set.unregister_conditionless_action(hotkey)
         
     | 
| 
      
 203 
     | 
    
         
            +
                end
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                # Implement translations in terms of normal hotkeys.
         
     | 
| 
      
 206 
     | 
    
         
            +
                # TODO: Extending HotkeyProcessor to natively support translations might be a
         
     | 
| 
      
 207 
     | 
    
         
            +
                # simpler and more solid approach.
         
     | 
| 
      
 208 
     | 
    
         
            +
                def add_translation(string, to, condition, location)
         
     | 
| 
      
 209 
     | 
    
         
            +
                  down_hotkey = hotkey_from_string(string)
         
     | 
| 
      
 210 
     | 
    
         
            +
                  up_hotkey   = hotkey_from_string(string)
         
     | 
| 
      
 211 
     | 
    
         
            +
                  down_hotkey.direction = :down
         
     | 
| 
      
 212 
     | 
    
         
            +
                  up_hotkey.direction   = :up
         
     | 
| 
      
 213 
     | 
    
         
            +
                  to_key = @layout[to]
         
     | 
| 
      
 214 
     | 
    
         
            +
                  
         
     | 
| 
      
 215 
     | 
    
         
            +
                  catch_modifier_up = proc do
         
     | 
| 
      
 216 
     | 
    
         
            +
                    if down_hotkey.modifiers.include? @key
         
     | 
| 
      
 217 
     | 
    
         
            +
                      send_key_event(to_key, false)
         
     | 
| 
      
 218 
     | 
    
         
            +
                      remove_hook(catch_modifier_up)
         
     | 
| 
      
 219 
     | 
    
         
            +
                    end
         
     | 
| 
      
 220 
     | 
    
         
            +
                  end
         
     | 
| 
      
 221 
     | 
    
         
            +
                  down_action = Action.new( lambda do
         
     | 
| 
      
 222 
     | 
    
         
            +
                                              send_key_event(to_key, true)
         
     | 
| 
      
 223 
     | 
    
         
            +
                                              Rum.hotkey_processor.add_hook(catch_modifier_up)
         
     | 
| 
      
 224 
     | 
    
         
            +
                                            end,
         
     | 
| 
      
 225 
     | 
    
         
            +
                                            condition, true, location)
         
     | 
| 
      
 226 
     | 
    
         
            +
                  up_action   = Action.new( lambda do
         
     | 
| 
      
 227 
     | 
    
         
            +
                                              send_key_event(to_key, false)
         
     | 
| 
      
 228 
     | 
    
         
            +
                                              Rum.hotkey_processor.remove_hook(catch_modifier_up)
         
     | 
| 
      
 229 
     | 
    
         
            +
                                            end,
         
     | 
| 
      
 230 
     | 
    
         
            +
                                            condition, true, location)
         
     | 
| 
      
 231 
     | 
    
         
            +
                  down_action.hotkey = down_hotkey
         
     | 
| 
      
 232 
     | 
    
         
            +
                  up_action.hotkey   = up_hotkey
         
     | 
| 
      
 233 
     | 
    
         
            +
                  register(down_action)
         
     | 
| 
      
 234 
     | 
    
         
            +
                  register(up_action)
         
     | 
| 
      
 235 
     | 
    
         
            +
                end
         
     | 
| 
      
 236 
     | 
    
         
            +
             
     | 
| 
      
 237 
     | 
    
         
            +
                def hotkey_from_string str
         
     | 
| 
      
 238 
     | 
    
         
            +
                  *modifier_aliases, key_alias = str.split(' ')
         
     | 
| 
      
 239 
     | 
    
         
            +
                  fuzzy = !!modifier_aliases.delete('*')
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
                  # Lookup modifiers
         
     | 
| 
      
 242 
     | 
    
         
            +
                  modifiers = []
         
     | 
| 
      
 243 
     | 
    
         
            +
                  modifier_aliases.each do |modifier_alias|
         
     | 
| 
      
 244 
     | 
    
         
            +
                    if key = @layout.modifier_lookup(modifier_alias)
         
     | 
| 
      
 245 
     | 
    
         
            +
                      modifiers << key
         
     | 
| 
      
 246 
     | 
    
         
            +
                    else
         
     | 
| 
      
 247 
     | 
    
         
            +
                      raise "Invalid modifier: #{modifier_alias}"
         
     | 
| 
      
 248 
     | 
    
         
            +
                    end
         
     | 
| 
      
 249 
     | 
    
         
            +
                  end
         
     | 
| 
      
 250 
     | 
    
         
            +
                  modifiers = modifiers.sort_by &:id
         
     | 
| 
      
 251 
     | 
    
         
            +
                  
         
     | 
| 
      
 252 
     | 
    
         
            +
                  # Lookup key
         
     | 
| 
      
 253 
     | 
    
         
            +
                  key = @layout[key_alias]
         
     | 
| 
      
 254 
     | 
    
         
            +
                  raise "#{key_alias} is no valid key." unless key
         
     | 
| 
      
 255 
     | 
    
         
            +
                  
         
     | 
| 
      
 256 
     | 
    
         
            +
                  Hotkey.new(key, modifiers, fuzzy)
         
     | 
| 
      
 257 
     | 
    
         
            +
                end
         
     | 
| 
      
 258 
     | 
    
         
            +
             
     | 
| 
      
 259 
     | 
    
         
            +
                def register action
         
     | 
| 
      
 260 
     | 
    
         
            +
                  hotkey = register_hotkey(action.hotkey)
         
     | 
| 
      
 261 
     | 
    
         
            +
                  if action.condition
         
     | 
| 
      
 262 
     | 
    
         
            +
                    hotkey.actions.unshift action # put actions with conditions first
         
     | 
| 
      
 263 
     | 
    
         
            +
                  else
         
     | 
| 
      
 264 
     | 
    
         
            +
                    # Only one conditionless action per hotkey
         
     | 
| 
      
 265 
     | 
    
         
            +
                    unless last_action = hotkey.actions.last and last_action.condition
         
     | 
| 
      
 266 
     | 
    
         
            +
                      hotkey.actions.pop
         
     | 
| 
      
 267 
     | 
    
         
            +
                    end
         
     | 
| 
      
 268 
     | 
    
         
            +
                    hotkey.actions << action
         
     | 
| 
      
 269 
     | 
    
         
            +
                  end
         
     | 
| 
      
 270 
     | 
    
         
            +
                  action
         
     | 
| 
      
 271 
     | 
    
         
            +
                end
         
     | 
| 
      
 272 
     | 
    
         
            +
                
         
     | 
| 
      
 273 
     | 
    
         
            +
                def unregister action
         
     | 
| 
      
 274 
     | 
    
         
            +
                  if hotkey = action.hotkey
         
     | 
| 
      
 275 
     | 
    
         
            +
                    hotkey.actions.delete(action)
         
     | 
| 
      
 276 
     | 
    
         
            +
                    unregister_hotkey(hotkey) if hotkey.actions.empty?
         
     | 
| 
      
 277 
     | 
    
         
            +
                    action
         
     | 
| 
      
 278 
     | 
    
         
            +
                  end
         
     | 
| 
      
 279 
     | 
    
         
            +
                end
         
     | 
| 
      
 280 
     | 
    
         
            +
             
     | 
| 
      
 281 
     | 
    
         
            +
                def unregister_conditionless_action hotkey
         
     | 
| 
      
 282 
     | 
    
         
            +
                  if hotkey = lookup_hotkey(hotkey) and action = hotkey.actions.last
         
     | 
| 
      
 283 
     | 
    
         
            +
                    unregister action unless action.condition
         
     | 
| 
      
 284 
     | 
    
         
            +
                  end
         
     | 
| 
      
 285 
     | 
    
         
            +
                end
         
     | 
| 
      
 286 
     | 
    
         
            +
             
     | 
| 
      
 287 
     | 
    
         
            +
                def get_dict(hotkey)
         
     | 
| 
      
 288 
     | 
    
         
            +
                  if hotkey.fuzzy?
         
     | 
| 
      
 289 
     | 
    
         
            +
                    dict = if hotkey.direction == :up
         
     | 
| 
      
 290 
     | 
    
         
            +
                             @up_fuzzy
         
     | 
| 
      
 291 
     | 
    
         
            +
                           else
         
     | 
| 
      
 292 
     | 
    
         
            +
                             @down_fuzzy
         
     | 
| 
      
 293 
     | 
    
         
            +
                           end
         
     | 
| 
      
 294 
     | 
    
         
            +
                    dict = (dict[hotkey.key] ||= {})
         
     | 
| 
      
 295 
     | 
    
         
            +
                    dict_key = hotkey.modifiers
         
     | 
| 
      
 296 
     | 
    
         
            +
                  else
         
     | 
| 
      
 297 
     | 
    
         
            +
                    dict_key = key_signature(hotkey)
         
     | 
| 
      
 298 
     | 
    
         
            +
                    dict = if (dir = hotkey.direction and dir == :up) or \
         
     | 
| 
      
 299 
     | 
    
         
            +
                              (hotkey.key.modifier? and @modifier_combinations[dict_key] > 0)
         
     | 
| 
      
 300 
     | 
    
         
            +
                             @up
         
     | 
| 
      
 301 
     | 
    
         
            +
                           else
         
     | 
| 
      
 302 
     | 
    
         
            +
                             @down
         
     | 
| 
      
 303 
     | 
    
         
            +
                           end
         
     | 
| 
      
 304 
     | 
    
         
            +
                  end
         
     | 
| 
      
 305 
     | 
    
         
            +
                  [dict, dict_key]
         
     | 
| 
      
 306 
     | 
    
         
            +
                end
         
     | 
| 
      
 307 
     | 
    
         
            +
                
         
     | 
| 
      
 308 
     | 
    
         
            +
                def key_signature hotkey
         
     | 
| 
      
 309 
     | 
    
         
            +
                  hotkey.modifiers.dup << hotkey.key
         
     | 
| 
      
 310 
     | 
    
         
            +
                end
         
     | 
| 
      
 311 
     | 
    
         
            +
             
     | 
| 
      
 312 
     | 
    
         
            +
                def lookup_hotkey hotkey
         
     | 
| 
      
 313 
     | 
    
         
            +
                  dict, dict_key = get_dict(hotkey)
         
     | 
| 
      
 314 
     | 
    
         
            +
                  dict[dict_key]
         
     | 
| 
      
 315 
     | 
    
         
            +
                end
         
     | 
| 
      
 316 
     | 
    
         
            +
             
     | 
| 
      
 317 
     | 
    
         
            +
                def register_hotkey hotkey
         
     | 
| 
      
 318 
     | 
    
         
            +
                  dict, dict_key = get_dict(hotkey)
         
     | 
| 
      
 319 
     | 
    
         
            +
                  existing_hotkey = dict[dict_key] and return existing_hotkey
         
     | 
| 
      
 320 
     | 
    
         
            +
                  
         
     | 
| 
      
 321 
     | 
    
         
            +
                  dict[dict_key] = hotkey
         
     | 
| 
      
 322 
     | 
    
         
            +
                  if hotkey.key.modifier? # Modifier hotkeys aren't allowed to be fuzzy.
         
     | 
| 
      
 323 
     | 
    
         
            +
                    @modifier_hotkeys[dict_key] = hotkey
         
     | 
| 
      
 324 
     | 
    
         
            +
                  else
         
     | 
| 
      
 325 
     | 
    
         
            +
                    register_modifier_combination(hotkey.modifiers)
         
     | 
| 
      
 326 
     | 
    
         
            +
                  end
         
     | 
| 
      
 327 
     | 
    
         
            +
                  hotkey
         
     | 
| 
      
 328 
     | 
    
         
            +
                end
         
     | 
| 
      
 329 
     | 
    
         
            +
             
     | 
| 
      
 330 
     | 
    
         
            +
                def unregister_hotkey hotkey
         
     | 
| 
      
 331 
     | 
    
         
            +
                  dict, dict_key = get_dict(hotkey)
         
     | 
| 
      
 332 
     | 
    
         
            +
                  if dict.delete(dict_key)
         
     | 
| 
      
 333 
     | 
    
         
            +
                    if hotkey.key.modifier?
         
     | 
| 
      
 334 
     | 
    
         
            +
                      @modifier_hotkeys.delete(hotkey)
         
     | 
| 
      
 335 
     | 
    
         
            +
                    else
         
     | 
| 
      
 336 
     | 
    
         
            +
                      unregister_modifier_combination(hotkey.modifiers)
         
     | 
| 
      
 337 
     | 
    
         
            +
                    end
         
     | 
| 
      
 338 
     | 
    
         
            +
                  end
         
     | 
| 
      
 339 
     | 
    
         
            +
                end
         
     | 
| 
      
 340 
     | 
    
         
            +
             
     | 
| 
      
 341 
     | 
    
         
            +
                def register_modifier_combination(modifiers)
         
     | 
| 
      
 342 
     | 
    
         
            +
                  maybe_reregister_mod_hotkey(modifiers, 1, 0)
         
     | 
| 
      
 343 
     | 
    
         
            +
                end
         
     | 
| 
      
 344 
     | 
    
         
            +
             
     | 
| 
      
 345 
     | 
    
         
            +
                def unregister_modifier_combination(modifiers)
         
     | 
| 
      
 346 
     | 
    
         
            +
                  maybe_reregister_mod_hotkey(modifiers, -1, 1)
         
     | 
| 
      
 347 
     | 
    
         
            +
                end
         
     | 
| 
      
 348 
     | 
    
         
            +
             
     | 
| 
      
 349 
     | 
    
         
            +
                # Example:
         
     | 
| 
      
 350 
     | 
    
         
            +
                # 1. The modifier hotkey 'ctrl shift' is the only registered hotkey.
         
     | 
| 
      
 351 
     | 
    
         
            +
                # 2. The hotkey 'ctrl shift a' gets registered.
         
     | 
| 
      
 352 
     | 
    
         
            +
                # Now the 'ctrl shift' hotkey needs to be re-registered to avoid
         
     | 
| 
      
 353 
     | 
    
         
            +
                # getting triggered while 'ctrl shift a' is pressed.
         
     | 
| 
      
 354 
     | 
    
         
            +
                #
         
     | 
| 
      
 355 
     | 
    
         
            +
                # Vice versa:
         
     | 
| 
      
 356 
     | 
    
         
            +
                # When all conflicting modifier combinations have been
         
     | 
| 
      
 357 
     | 
    
         
            +
                # unregistered, a modifier hotkey can be re-registered to trigger
         
     | 
| 
      
 358 
     | 
    
         
            +
                # instantly.
         
     | 
| 
      
 359 
     | 
    
         
            +
                #
         
     | 
| 
      
 360 
     | 
    
         
            +
                # This function keeps track of active modifier_combinations when
         
     | 
| 
      
 361 
     | 
    
         
            +
                # hotkeys ar added or removed. It re-registers corresponding
         
     | 
| 
      
 362 
     | 
    
         
            +
                # modifier hotkeys accordingly.
         
     | 
| 
      
 363 
     | 
    
         
            +
                def maybe_reregister_mod_hotkey(modifiers, change_count, threshold)
         
     | 
| 
      
 364 
     | 
    
         
            +
                  count = @modifier_combinations[modifiers]
         
     | 
| 
      
 365 
     | 
    
         
            +
                  if count == threshold and (mod_hotkey = @modifier_hotkeys[modifiers])
         
     | 
| 
      
 366 
     | 
    
         
            +
                    unregister_hotkey(mod_hotkey)
         
     | 
| 
      
 367 
     | 
    
         
            +
                    @modifier_combinations[modifiers] = count + change_count
         
     | 
| 
      
 368 
     | 
    
         
            +
                    register_hotkey(mod_hotkey)
         
     | 
| 
      
 369 
     | 
    
         
            +
                  else
         
     | 
| 
      
 370 
     | 
    
         
            +
                    @modifier_combinations[modifiers] = count + change_count
         
     | 
| 
      
 371 
     | 
    
         
            +
                  end
         
     | 
| 
      
 372 
     | 
    
         
            +
                end
         
     | 
| 
      
 373 
     | 
    
         
            +
             
     | 
| 
      
 374 
     | 
    
         
            +
                def lookup(down, signature)
         
     | 
| 
      
 375 
     | 
    
         
            +
                  (down ? @down : @up)[signature]
         
     | 
| 
      
 376 
     | 
    
         
            +
                end
         
     | 
| 
      
 377 
     | 
    
         
            +
             
     | 
| 
      
 378 
     | 
    
         
            +
                def fuzzy_lookup(down, key, pressed_modifiers)
         
     | 
| 
      
 379 
     | 
    
         
            +
                  if (hotkeys = (down ? @down_fuzzy : @up_fuzzy)[key])
         
     | 
| 
      
 380 
     | 
    
         
            +
                    hotkeys.each do |modifiers, hotkey|
         
     | 
| 
      
 381 
     | 
    
         
            +
                      return hotkey if modifiers.all? { |mod| pressed_modifiers[mod] }
         
     | 
| 
      
 382 
     | 
    
         
            +
                    end
         
     | 
| 
      
 383 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 384 
     | 
    
         
            +
                  end
         
     | 
| 
      
 385 
     | 
    
         
            +
                end
         
     | 
| 
      
 386 
     | 
    
         
            +
              end
         
     | 
| 
      
 387 
     | 
    
         
            +
             
     | 
| 
      
 388 
     | 
    
         
            +
              # TODO: Needs refactoring.
         
     | 
| 
      
 389 
     | 
    
         
            +
              class HotkeyProcessor
         
     | 
| 
      
 390 
     | 
    
         
            +
                attr_reader :layout, :pressed_modifiers, :hooks
         
     | 
| 
      
 391 
     | 
    
         
            +
                def initialize hotkey_set
         
     | 
| 
      
 392 
     | 
    
         
            +
                  @hotkey_set = hotkey_set
         
     | 
| 
      
 393 
     | 
    
         
            +
                  @modifiers = @hotkey_set.modifiers
         
     | 
| 
      
 394 
     | 
    
         
            +
                  @layout = @hotkey_set.layout
         
     | 
| 
      
 395 
     | 
    
         
            +
                  @pressed_modifiers = {}
         
     | 
| 
      
 396 
     | 
    
         
            +
                  @was_executed = {}
         
     | 
| 
      
 397 
     | 
    
         
            +
                  @key_signature = nil
         
     | 
| 
      
 398 
     | 
    
         
            +
                  @hooks = []
         
     | 
| 
      
 399 
     | 
    
         
            +
                  @pass_key = true
         
     | 
| 
      
 400 
     | 
    
         
            +
                  
         
     | 
| 
      
 401 
     | 
    
         
            +
                  @key = nil
         
     | 
| 
      
 402 
     | 
    
         
            +
                  @last_key = nil
         
     | 
| 
      
 403 
     | 
    
         
            +
                  @down = nil
         
     | 
| 
      
 404 
     | 
    
         
            +
                  @last_event_up = nil
         
     | 
| 
      
 405 
     | 
    
         
            +
                end
         
     | 
| 
      
 406 
     | 
    
         
            +
             
     | 
| 
      
 407 
     | 
    
         
            +
                def add_hook(hook=nil, &block)
         
     | 
| 
      
 408 
     | 
    
         
            +
                  hook ||= block
         
     | 
| 
      
 409 
     | 
    
         
            +
                  @hooks.unshift hook
         
     | 
| 
      
 410 
     | 
    
         
            +
                  hook
         
     | 
| 
      
 411 
     | 
    
         
            +
                end
         
     | 
| 
      
 412 
     | 
    
         
            +
                
         
     | 
| 
      
 413 
     | 
    
         
            +
                # Return removed hook.
         
     | 
| 
      
 414 
     | 
    
         
            +
                def remove_hook(hook)
         
     | 
| 
      
 415 
     | 
    
         
            +
                  @hooks.delete hook
         
     | 
| 
      
 416 
     | 
    
         
            +
                end
         
     | 
| 
      
 417 
     | 
    
         
            +
                
         
     | 
| 
      
 418 
     | 
    
         
            +
                def key_signature key
         
     | 
| 
      
 419 
     | 
    
         
            +
                  @modifiers.select { |modifier| @pressed_modifiers[modifier] } << key
         
     | 
| 
      
 420 
     | 
    
         
            +
                end
         
     | 
| 
      
 421 
     | 
    
         
            +
             
     | 
| 
      
 422 
     | 
    
         
            +
                def execute(down, repeated)
         
     | 
| 
      
 423 
     | 
    
         
            +
                  (hotkey = @hotkey_set.lookup(down, @key_signature)                 \
         
     | 
| 
      
 424 
     | 
    
         
            +
                   and hotkey.execute(repeated)) or
         
     | 
| 
      
 425 
     | 
    
         
            +
                  (hotkey = @hotkey_set.fuzzy_lookup(down, @key, @pressed_modifiers) \
         
     | 
| 
      
 426 
     | 
    
         
            +
                   and hotkey.execute(repeated))
         
     | 
| 
      
 427 
     | 
    
         
            +
                end
         
     | 
| 
      
 428 
     | 
    
         
            +
             
     | 
| 
      
 429 
     | 
    
         
            +
                def process_event event
         
     | 
| 
      
 430 
     | 
    
         
            +
                  puts event
         
     | 
| 
      
 431 
     | 
    
         
            +
                  @key = @layout.lookup_id(event.id)
         
     | 
| 
      
 432 
     | 
    
         
            +
                  @down = event.down?
         
     | 
| 
      
 433 
     | 
    
         
            +
                  unless @key
         
     | 
| 
      
 434 
     | 
    
         
            +
                    puts "Unknown Key\n"
         
     | 
| 
      
 435 
     | 
    
         
            +
                    return true
         
     | 
| 
      
 436 
     | 
    
         
            +
                  end
         
     | 
| 
      
 437 
     | 
    
         
            +
                  modifier = @key.modifier?
         
     | 
| 
      
 438 
     | 
    
         
            +
             
     | 
| 
      
 439 
     | 
    
         
            +
                  # Skip generating unnecessary key_signatures
         
     | 
| 
      
 440 
     | 
    
         
            +
                  if modifier and @last_event_up or @key != @last_key
         
     | 
| 
      
 441 
     | 
    
         
            +
                    @key_signature = key_signature(@key)
         
     | 
| 
      
 442 
     | 
    
         
            +
                  end
         
     | 
| 
      
 443 
     | 
    
         
            +
                  @last_event_up = !@down
         
     | 
| 
      
 444 
     | 
    
         
            +
                  
         
     | 
| 
      
 445 
     | 
    
         
            +
                  print "[#{@key_signature.map { |key| key.name.capitalize }.join(', ')}]"
         
     | 
| 
      
 446 
     | 
    
         
            +
                  print ' (again)' if @key == @last_key; puts
         
     | 
| 
      
 447 
     | 
    
         
            +
                  
         
     | 
| 
      
 448 
     | 
    
         
            +
                  if @down
         
     | 
| 
      
 449 
     | 
    
         
            +
                    repeated = @was_executed[@key]
         
     | 
| 
      
 450 
     | 
    
         
            +
                    eat = @was_executed[@key] = true if execute(true, repeated)
         
     | 
| 
      
 451 
     | 
    
         
            +
                    if repeated
         
     | 
| 
      
 452 
     | 
    
         
            +
                      eat = true
         
     | 
| 
      
 453 
     | 
    
         
            +
                    elsif modifier # if repeated, the modifier has already been added to pressed_modifiers
         
     | 
| 
      
 454 
     | 
    
         
            +
                      @pressed_modifiers[@key] = true
         
     | 
| 
      
 455 
     | 
    
         
            +
                    end
         
     | 
| 
      
 456 
     | 
    
         
            +
                  else #up
         
     | 
| 
      
 457 
     | 
    
         
            +
                    @was_executed[@key] = true if execute(false, false)
         
     | 
| 
      
 458 
     | 
    
         
            +
                    if modifier
         
     | 
| 
      
 459 
     | 
    
         
            +
                      @pressed_modifiers[@key] = nil 
         
     | 
| 
      
 460 
     | 
    
         
            +
                      if @layout.action_modifiers[@key] and \
         
     | 
| 
      
 461 
     | 
    
         
            +
                        (!@pass_key or @was_executed[@key])
         
     | 
| 
      
 462 
     | 
    
         
            +
                        inhibit_modifier_action
         
     | 
| 
      
 463 
     | 
    
         
            +
                      end
         
     | 
| 
      
 464 
     | 
    
         
            +
                    end
         
     | 
| 
      
 465 
     | 
    
         
            +
                    eat = @was_executed.delete(@key)
         
     | 
| 
      
 466 
     | 
    
         
            +
                  end
         
     | 
| 
      
 467 
     | 
    
         
            +
                  
         
     | 
| 
      
 468 
     | 
    
         
            +
                  @pass_key = if modifier
         
     | 
| 
      
 469 
     | 
    
         
            +
                                @layout.core_modifiers[@key]
         
     | 
| 
      
 470 
     | 
    
         
            +
                              else
         
     | 
| 
      
 471 
     | 
    
         
            +
                                !eat
         
     | 
| 
      
 472 
     | 
    
         
            +
                              end
         
     | 
| 
      
 473 
     | 
    
         
            +
                  @hooks.dup.each { |hook| instance_eval &hook }
         
     | 
| 
      
 474 
     | 
    
         
            +
                  @last_key = @key
         
     | 
| 
      
 475 
     | 
    
         
            +
                  puts
         
     | 
| 
      
 476 
     | 
    
         
            +
                  @pass_key    
         
     | 
| 
      
 477 
     | 
    
         
            +
                end
         
     | 
| 
      
 478 
     | 
    
         
            +
              end
         
     | 
| 
      
 479 
     | 
    
         
            +
            end
         
     |