rbindkeys 0.0.1
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/.gitignore +23 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +52 -0
- data/Rakefile +11 -0
- data/bin/rbindkeys +5 -0
- data/lib/rbindkeys.rb +38 -0
- data/lib/rbindkeys/bind_resolver.rb +58 -0
- data/lib/rbindkeys/bind_set.rb +10 -0
- data/lib/rbindkeys/bind_tree.rb +148 -0
- data/lib/rbindkeys/cli.rb +103 -0
- data/lib/rbindkeys/device.rb +25 -0
- data/lib/rbindkeys/device_operator.rb +78 -0
- data/lib/rbindkeys/fix_resolver.rb +26 -0
- data/lib/rbindkeys/key_bind.rb +21 -0
- data/lib/rbindkeys/key_event_handler.rb +243 -0
- data/lib/rbindkeys/key_event_handler/configurer.rb +142 -0
- data/lib/rbindkeys/log_utils.rb +51 -0
- data/lib/rbindkeys/observer.rb +140 -0
- data/lib/rbindkeys/version.rb +3 -0
- data/lib/rbindkeys/virtual_device.rb +14 -0
- data/lib/rbindkeys/window_matcher.rb +35 -0
- data/rbindkeys.gemspec +26 -0
- data/sample/emacs.rb +100 -0
- data/sample/swap_left_ctrl_and_caps.rb +4 -0
- data/spec/bind_resolver_spec.rb +101 -0
- data/spec/bind_tree_bind_spec.rb +48 -0
- data/spec/bind_tree_resolve_for_pressed_event_spec.rb +74 -0
- data/spec/bind_tree_resolve_for_released_event_spec.rb +63 -0
- data/spec/cli_spec.rb +81 -0
- data/spec/device_operator_spec.rb +57 -0
- data/spec/fix_resolver_spec.rb +57 -0
- data/spec/key_event_handler/configurer_spec.rb +201 -0
- data/spec/key_event_handler/handle_spec.rb +222 -0
- metadata +190 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
require "revdev"
|
4
|
+
|
5
|
+
module Rbindkeys
|
6
|
+
|
7
|
+
class Device < Revdev::EventDevice
|
8
|
+
|
9
|
+
def release_all_key
|
10
|
+
ie = Revdev::InputEvent.new nil, 0, 0, 0
|
11
|
+
Revdev.constants.select{|c| c.match(/^(?:KEY|BTN)/)}.each do |c|
|
12
|
+
ie.type = Revdev::EV_KEY
|
13
|
+
ie.code = Revdev.const_get c
|
14
|
+
ie.value = 0
|
15
|
+
write_input_event ie
|
16
|
+
|
17
|
+
ie.type = Revdev::EV_SYN
|
18
|
+
ie.code = 0
|
19
|
+
write_input_event ie
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
require 'revdev'
|
4
|
+
|
5
|
+
module Rbindkeys
|
6
|
+
|
7
|
+
# device operations like send key event, send LED event, etc.
|
8
|
+
class DeviceOperator
|
9
|
+
|
10
|
+
LOG = LogUtils.get_logger name
|
11
|
+
|
12
|
+
# real event device
|
13
|
+
attr_reader :device
|
14
|
+
|
15
|
+
# uinput device
|
16
|
+
attr_reader :virtural
|
17
|
+
|
18
|
+
# key code set which was send press event but is not send release event
|
19
|
+
attr_reader :pressed_key_set
|
20
|
+
|
21
|
+
def initialize dev, vdev
|
22
|
+
@device = dev
|
23
|
+
@virtual = vdev
|
24
|
+
@pressed_key_set = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def release_key code
|
28
|
+
send_key code, 0
|
29
|
+
end
|
30
|
+
def press_key code
|
31
|
+
send_key code, 1
|
32
|
+
end
|
33
|
+
def pressing_key code
|
34
|
+
send_key code, 2
|
35
|
+
end
|
36
|
+
|
37
|
+
def send_key code, state
|
38
|
+
send_event Revdev::EV_KEY, code, state
|
39
|
+
end
|
40
|
+
|
41
|
+
def send_event *args
|
42
|
+
event =
|
43
|
+
case args.length
|
44
|
+
when 1 then args[0]
|
45
|
+
when 3 then
|
46
|
+
@cache_input_event ||= Revdev::InputEvent.new nil, 0, 0, 0
|
47
|
+
@cache_input_event.type = args[0]
|
48
|
+
@cache_input_event.code = args[1]
|
49
|
+
@cache_input_event.value = args[2]
|
50
|
+
@cache_input_event
|
51
|
+
else raise ArgumentError, "expect a InputEvent or 3 Fixnums (type, code, state)"
|
52
|
+
end
|
53
|
+
dev = case event.type
|
54
|
+
when Revdev::EV_KEY then @virtual
|
55
|
+
when Revdev::EV_LED then @device
|
56
|
+
else @virtual
|
57
|
+
end
|
58
|
+
|
59
|
+
update_pressed_key_set event
|
60
|
+
dev.write_input_event event
|
61
|
+
LOG.info "write\t#{KeyEventHandler.get_state_by_value event} "+
|
62
|
+
"#{event.hr_code}(#{event.code})" if LOG.info?
|
63
|
+
end
|
64
|
+
|
65
|
+
def update_pressed_key_set event
|
66
|
+
if event.type == Revdev::EV_KEY
|
67
|
+
case event.value
|
68
|
+
when 0 then @pressed_key_set.delete event.code
|
69
|
+
when 1 then @pressed_key_set << event.code
|
70
|
+
when 2 then # do nothing
|
71
|
+
else raise UnknownKeyValue, "expect 0, 1 or 2"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
module Rbindkeys
|
4
|
+
|
5
|
+
# last resolvers which return @val as the resolve result
|
6
|
+
class FixResolver < BindResolver
|
7
|
+
private_class_method :new
|
8
|
+
@@pool = {}
|
9
|
+
|
10
|
+
def self.instance val
|
11
|
+
@@pool[val] or (@@pool[val] = new val)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize val
|
15
|
+
@val = val
|
16
|
+
end
|
17
|
+
|
18
|
+
def bind input, output
|
19
|
+
raise RuntimeError, 'cannot bind any input/output'
|
20
|
+
end
|
21
|
+
|
22
|
+
def resolve code, code_set
|
23
|
+
@val
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
module Rbindkeys
|
4
|
+
class KeyBind
|
5
|
+
|
6
|
+
attr_reader :input
|
7
|
+
|
8
|
+
attr_reader :output
|
9
|
+
|
10
|
+
# when a signal of any input release event was accepted,
|
11
|
+
# if @inputs_recovery is true, outputs are released and other inputs are pressed,
|
12
|
+
# if @inputs_recovery is false or nil, outputs are released.
|
13
|
+
attr_reader :input_recovery
|
14
|
+
|
15
|
+
def initialize input, output, opt = {}
|
16
|
+
@input = input
|
17
|
+
@output = output
|
18
|
+
@input_recovery = opt[:input_recovery]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,243 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
require 'rbindkeys/key_event_handler/configurer'
|
4
|
+
require 'active_window_x'
|
5
|
+
|
6
|
+
module Rbindkeys
|
7
|
+
|
8
|
+
# retrive key binds with key event
|
9
|
+
class KeyEventHandler
|
10
|
+
include Revdev
|
11
|
+
|
12
|
+
LOG = LogUtils.get_logger name
|
13
|
+
|
14
|
+
# device operator
|
15
|
+
attr_reader :operator
|
16
|
+
|
17
|
+
# defaulut key bind set which retrive key binds with a key event
|
18
|
+
attr_reader :default_bind_resolver
|
19
|
+
|
20
|
+
# current key bind set which retrive key binds with a key event
|
21
|
+
attr_reader :bind_resolver
|
22
|
+
|
23
|
+
# a hash (key:WindowMatcher, val:BindResolver) to switch BindResolver
|
24
|
+
# by the title or app_name of active window
|
25
|
+
attr_reader :window_bind_resolver_map
|
26
|
+
|
27
|
+
# proccessed resolver before bind_resolver
|
28
|
+
attr_reader :pre_bind_resolver
|
29
|
+
|
30
|
+
# code set of pressed key on the event device
|
31
|
+
attr_reader :pressed_key_set
|
32
|
+
|
33
|
+
# pressed key binds
|
34
|
+
attr_reader :active_bind_set
|
35
|
+
|
36
|
+
def initialize device_operator
|
37
|
+
@operator = device_operator
|
38
|
+
@default_bind_resolver = BindResolver.new
|
39
|
+
@window_bind_resolver = nil
|
40
|
+
@bind_resolver = @default_bind_resolver
|
41
|
+
@window_bind_resolver_map = []
|
42
|
+
@pre_bind_resolver = {}
|
43
|
+
@pressed_key_set = []
|
44
|
+
@active_bind_set = []
|
45
|
+
end
|
46
|
+
|
47
|
+
def load_config file
|
48
|
+
code = File.read file
|
49
|
+
instance_eval code, file
|
50
|
+
end
|
51
|
+
|
52
|
+
def handle event
|
53
|
+
if LOG.info?
|
54
|
+
LOG.info "" unless LOG.debug?
|
55
|
+
LOG.info "read\t#{KeyEventHandler.get_state_by_value event} "+
|
56
|
+
"#{event.hr_code}(#{event.code})"
|
57
|
+
end
|
58
|
+
|
59
|
+
# handle pre_key_bind_set
|
60
|
+
event.code = (@pre_bind_resolver[event.code] or event.code)
|
61
|
+
|
62
|
+
# swich to handle event with event.value
|
63
|
+
result =
|
64
|
+
case event.value
|
65
|
+
when 0 then handle_release_event event
|
66
|
+
when 1 then handle_press_event event
|
67
|
+
when 2 then handle_pressing_event event
|
68
|
+
else raise UnknownKeyValue, "expect 0, 1 or 2 as event.value(#{event.value})"
|
69
|
+
end
|
70
|
+
|
71
|
+
case result
|
72
|
+
when :through
|
73
|
+
fill_gap_pressed_state event if event.value == 1
|
74
|
+
@operator.send_event event
|
75
|
+
when :ignore
|
76
|
+
# ignore the original event
|
77
|
+
end
|
78
|
+
|
79
|
+
handle_pressed_keys event
|
80
|
+
|
81
|
+
LOG.info "pressed_keys real:#{@pressed_key_set.inspect} "+
|
82
|
+
"virtual:#{@operator.pressed_key_set.inspect}" if LOG.info?
|
83
|
+
end
|
84
|
+
|
85
|
+
def handle_release_event event
|
86
|
+
release_bind_set = []
|
87
|
+
@active_bind_set.reject! do |key_bind|
|
88
|
+
if key_bind.input.include? event.code
|
89
|
+
release_bind_set << key_bind
|
90
|
+
true
|
91
|
+
else
|
92
|
+
false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
if release_bind_set.empty?
|
97
|
+
:through
|
98
|
+
else
|
99
|
+
release_bind_set.each do |kb|
|
100
|
+
kb.output.each {|c|@operator.release_key c}
|
101
|
+
if kb.input_recovery
|
102
|
+
kb.input.clone.delete_if {|c|c==event.code}.each {|c|@operator.press_key c}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
:ignore
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def set_bind_resolver resolver
|
110
|
+
old_resolver = @bind_resolver if LOG.info?
|
111
|
+
@bind_resolver = resolver
|
112
|
+
LOG.info "switch bind_resolver: #{old_resolver} => "+
|
113
|
+
"#{@bind_resolver}" if LOG.info?
|
114
|
+
@bind_resolver
|
115
|
+
end
|
116
|
+
|
117
|
+
def handle_press_event event
|
118
|
+
r = @bind_resolver.resolve event.code, @pressed_key_set
|
119
|
+
|
120
|
+
LOG.debug "resolve result: #{r.inspect}" if LOG.debug?
|
121
|
+
|
122
|
+
if r.kind_of? KeyBind
|
123
|
+
|
124
|
+
if @bind_resolver.two_stroke?
|
125
|
+
set_bind_resolver (@window_bind_resolver or @default_bind_resolver)
|
126
|
+
end
|
127
|
+
|
128
|
+
if r.output.kind_of? Array
|
129
|
+
r.input.clone.delete_if{|c|c==event.code}.each {|c| @operator.release_key c}
|
130
|
+
r.output.each {|c| @operator.press_key c}
|
131
|
+
@active_bind_set << r
|
132
|
+
:ignore
|
133
|
+
elsif r.output.kind_of? BindResolver
|
134
|
+
set_bind_resolver r.output
|
135
|
+
:ignore
|
136
|
+
elsif r.output.kind_of? Proc
|
137
|
+
r.output.call event, @operator
|
138
|
+
elsif r.output.kind_of? Symbol
|
139
|
+
r
|
140
|
+
end
|
141
|
+
else
|
142
|
+
r
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def handle_pressing_event event
|
147
|
+
if @active_bind_set.empty?
|
148
|
+
:through
|
149
|
+
else
|
150
|
+
@active_bind_set.each {|kb| kb.output.each {|c| @operator.pressing_key c}}
|
151
|
+
:ignore
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def fill_gap_pressed_state event
|
156
|
+
return if @operator.pressed_key_set == @pressed_key_set
|
157
|
+
sub = @pressed_key_set - @operator.pressed_key_set
|
158
|
+
if event.value == 0
|
159
|
+
sub.delete event.code
|
160
|
+
end
|
161
|
+
sub.each {|code| @operator.press_key code}
|
162
|
+
end
|
163
|
+
|
164
|
+
def handle_pressed_keys event
|
165
|
+
if event.value == 1
|
166
|
+
@pressed_key_set << event.code
|
167
|
+
@pressed_key_set.sort! # TODO do not sort. implement an bubble insertion
|
168
|
+
elsif event.value == 0
|
169
|
+
if @pressed_key_set.delete(event.code).nil?
|
170
|
+
LOG.warn "#{event.code} does not exists on @pressed_keys" if LOG.warn?
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def active_window_changed window
|
176
|
+
if not window.nil?
|
177
|
+
title = window.title
|
178
|
+
app_name = window.app_name
|
179
|
+
if LOG.info?
|
180
|
+
LOG.info "" unless LOG.debug?
|
181
|
+
LOG.info "change active_window: :class => \"#{app_name}\", :title => \"#{title}\""
|
182
|
+
end
|
183
|
+
|
184
|
+
@window_bind_resolver_map.each do |matcher, bind_resolver|
|
185
|
+
if matcher.match? app_name, title
|
186
|
+
if LOG.info?
|
187
|
+
LOG.info "=> matcher #{matcher.app_name.inspect}, #{matcher.title.inspect}"
|
188
|
+
LOG.info " bind_resolver #{bind_resolver.inspect}"
|
189
|
+
end
|
190
|
+
set_bind_resolver bind_resolver
|
191
|
+
@window_bind_resolver = bind_resolver
|
192
|
+
return
|
193
|
+
end
|
194
|
+
end
|
195
|
+
else
|
196
|
+
if LOG.info?
|
197
|
+
LOG.info "" unless LOG.debug?
|
198
|
+
LOG.info "change active_window: nil"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
LOG.info "=> no matcher" if LOG.info?
|
203
|
+
set_bind_resolver @default_bind_resolver
|
204
|
+
@window_bind_resolver = nil
|
205
|
+
return
|
206
|
+
end
|
207
|
+
|
208
|
+
class << self
|
209
|
+
# parse and normalize to Fixnum/Array
|
210
|
+
def parse_code code, depth = 0
|
211
|
+
if code.kind_of? Symbol
|
212
|
+
code = parse_symbol code
|
213
|
+
elsif code.kind_of? Array
|
214
|
+
raise ArgumentError, "expect Array is the depth less than 1" if depth >= 1
|
215
|
+
code.map!{|c| parse_code c, (depth+1)}
|
216
|
+
elsif code.kind_of? Fixnum and depth == 0
|
217
|
+
code = [code]
|
218
|
+
elsif not code.kind_of? Fixnum
|
219
|
+
raise ArgumentError, "expect Symbol / Fixnum / Array"
|
220
|
+
end
|
221
|
+
code
|
222
|
+
end
|
223
|
+
|
224
|
+
# TODO convert :j -> KEY_J, :ctrl -> KEY_LEFTCTRL
|
225
|
+
def parse_symbol sym
|
226
|
+
if not sym.kind_of? Symbol
|
227
|
+
raise ArgumentError, "expect Symbol / Fixnum / Array"
|
228
|
+
end
|
229
|
+
Revdev.const_get sym
|
230
|
+
end
|
231
|
+
|
232
|
+
def get_state_by_value ev
|
233
|
+
case ev.value
|
234
|
+
when 0 then 'released '
|
235
|
+
when 1 then 'pressed '
|
236
|
+
when 2 then 'pressing '
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
#
|
3
|
+
# a part of KeyEventHandler (see lib/rbindkeys/key_event_handler.rb)
|
4
|
+
#
|
5
|
+
|
6
|
+
module Rbindkeys
|
7
|
+
class KeyEventHandler
|
8
|
+
|
9
|
+
# pre-prosessed key codes replacement for all inputs
|
10
|
+
def pre_bind_key input, output
|
11
|
+
if not (input.kind_of? Fixnum and output.kind_of? Fixnum)
|
12
|
+
raise ArgumentError, 'expect Fixnum for input and output'
|
13
|
+
end
|
14
|
+
|
15
|
+
if @pre_bind_resolver.has_key? input
|
16
|
+
raise DuplicateNodeError, "1st arg (#{input}) was already entried"
|
17
|
+
end
|
18
|
+
|
19
|
+
LOG.info "pre_bind_key #{input.inspect},\t#{output.inspect}" if LOG.info?
|
20
|
+
|
21
|
+
@pre_bind_resolver[input] = output
|
22
|
+
[input, output]
|
23
|
+
end
|
24
|
+
|
25
|
+
# define a new key binding
|
26
|
+
def bind_key input, output=nil, resolver=@bind_resolver, &block
|
27
|
+
if input.kind_of?(Array) or input.kind_of?(Fixnum)
|
28
|
+
input = KeyEventHandler.parse_code input
|
29
|
+
else
|
30
|
+
raise ArgumentError, '1st arg expect Array / Fixnum'
|
31
|
+
end
|
32
|
+
|
33
|
+
if block_given?
|
34
|
+
output = block
|
35
|
+
elsif output.nil?
|
36
|
+
raise ArgumentError, 'expect 1 arg with a block / 2 args'
|
37
|
+
elsif output.kind_of? BindResolver
|
38
|
+
elsif output.kind_of?(Array) or output.kind_of?(Fixnum)
|
39
|
+
output = KeyEventHandler::parse_code output
|
40
|
+
elsif output == :through or output == :ignore
|
41
|
+
else
|
42
|
+
raise ArgumentError, '2nd arg expect Array / Fixnum / BindResolver / '+
|
43
|
+
'Symbol(:through/:ignore)'
|
44
|
+
end
|
45
|
+
|
46
|
+
LOG.info "bind_key #{input.inspect},\t#{output.inspect}\t#{resolver}" if LOG.info?
|
47
|
+
|
48
|
+
resolver.bind input, output
|
49
|
+
end
|
50
|
+
|
51
|
+
# setting for 2stroke key binding
|
52
|
+
# _input_ :: prefix key. (e.g. [KEY_LEFTCTRL, KEY_X] (C-x)
|
53
|
+
# _resolver_ :: upper bind_resolver for fall throught
|
54
|
+
# _block_ :: to define sub-binds on this prefix key bind
|
55
|
+
def bind_prefix_key input, resolver=@bind_resolver, &block
|
56
|
+
if not block_given?
|
57
|
+
raise ArgumentError, "expect a block"
|
58
|
+
end
|
59
|
+
|
60
|
+
input = KeyEventHandler::parse_code input
|
61
|
+
LOG.info "bind_prefix_key #{input.inspect}\t#{resolver}" if LOG.info?
|
62
|
+
tmp = input.clone
|
63
|
+
tail_input = tmp.pop
|
64
|
+
|
65
|
+
binded_resolver = resolver.just_resolve tail_input, tmp
|
66
|
+
if binded_resolver == nil
|
67
|
+
binded_resolver = BindResolver.new :ignore, true
|
68
|
+
resolver.bind input, binded_resolver
|
69
|
+
elsif not binded_resolver.kind_of? BindResolver
|
70
|
+
raise DuplicateNodeError, "the codes (#{input.inspect}) was already binded"
|
71
|
+
end
|
72
|
+
|
73
|
+
@bind_resolver = binded_resolver
|
74
|
+
yield
|
75
|
+
@bind_resolver = resolver
|
76
|
+
binded_resolver
|
77
|
+
end
|
78
|
+
|
79
|
+
def new_bind_resolver upper_resolver=@bind_resolver, &block
|
80
|
+
if not block_given?
|
81
|
+
raise ArgumentError, "expect a block"
|
82
|
+
end
|
83
|
+
|
84
|
+
new_resolver = BindResolver.new upper_resolver
|
85
|
+
|
86
|
+
old_resolver = @bind_resolver
|
87
|
+
@bind_resolver = new_resolver
|
88
|
+
yield
|
89
|
+
@bind_resolver = old_resolver
|
90
|
+
|
91
|
+
new_resolver
|
92
|
+
end
|
93
|
+
|
94
|
+
# switch bind_resolver if the active window match _arg_
|
95
|
+
#
|
96
|
+
# _upper\_resolver_ :: a upper bind_resolver, :through, :ignore
|
97
|
+
# _arg_ :: a hash or a regexp
|
98
|
+
#
|
99
|
+
# ==arg
|
100
|
+
# if a hash, which have entries :title => Regexp and/or :class => Regexp,
|
101
|
+
# was given, this bind is active when the window title match with
|
102
|
+
# :title's Regexp AND the window class match with :class's Regexp.
|
103
|
+
# if a regexp was given, this bind is active when the window title match
|
104
|
+
# with the given regexp
|
105
|
+
def window upper_resolver, arg
|
106
|
+
if upper_resolver.nil?
|
107
|
+
upper_resolver = @bind_resolver
|
108
|
+
elsif upper_resolver.kind_of? Symbol
|
109
|
+
upper_resolver = FixResolver.instance upper_resolver
|
110
|
+
elsif not upper_resolver.kind_of? BindResolver
|
111
|
+
raise ArgumentError, "1nd argument is expected to be a BindResolver or"+
|
112
|
+
" a Symbol : #{ upper_resolver.to_s}"
|
113
|
+
end
|
114
|
+
|
115
|
+
if arg.kind_of? Regexp
|
116
|
+
arg = { :title => arg }
|
117
|
+
elsif arg.kind_of? Hash
|
118
|
+
arg.each do |k, v|
|
119
|
+
if not (k.kind_of?(Symbol) and v.kind_of?(Regexp))
|
120
|
+
raise ArgumentError, 'the 2nd argument Hash must only have'+
|
121
|
+
" Symbol keys and Regexp values : #{arg.inspect}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
else
|
125
|
+
raise ArgumentError, "2nd argument is expected to be a Hash or a Regexp : #{arg}"
|
126
|
+
end
|
127
|
+
|
128
|
+
resolver = BindResolver.new(upper_resolver)
|
129
|
+
@window_bind_resolver_map.push [WindowMatcher.new(arg), resolver]
|
130
|
+
|
131
|
+
if block_given?
|
132
|
+
old_resolver = @bind_resolver
|
133
|
+
@bind_resolver = resolver
|
134
|
+
yield
|
135
|
+
@bind_resolver = old_resolver
|
136
|
+
end
|
137
|
+
|
138
|
+
resolver
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|