1 #
2 # tk/validation.rb - validation support module for entry, spinbox, and so on
3 #
4 require 'tk'
5
6 module Tk
7 module ValidateConfigure
8 def self.__def_validcmd(scope, klass, keys=nil)
9 keys = klass._config_keys unless keys
10 keys.each{|key|
11 eval("def #{key}(*args, &b)
12 __validcmd_call(#{klass.name}, '#{key}', *args, &b)
13 end", scope)
14 }
15 end
16
17 def __validcmd_call(klass, key, *args, &b)
18 return cget(key) if args.empty? && !b
19
20 cmd = (b)? proc(&b) : args.shift
21
22 if cmd.kind_of?(klass)
23 configure(key, cmd)
24 elsif !args.empty?
25 configure(key, [cmd, args])
26 else
27 configure(key, cmd)
28 end
29 end
30
31 def __validation_class_list
32 # maybe need to override
33 []
34 end
35
36 def __get_validate_key2class
37 k2c = {}
38 __validation_class_list.each{|klass|
39 klass._config_keys.each{|key|
40 k2c[key.to_s] = klass
41 }
42 }
43 k2c
44 end
45
46 def __conv_vcmd_on_hash_kv(keys)
47 key2class = __get_validate_key2class
48
49 keys = _symbolkey2str(keys)
50 key2class.each{|key, klass|
51 if keys[key].kind_of?(Array)
52 cmd, *args = keys[key]
53 #keys[key] = klass.new(cmd, args.join(' '))
54 keys[key] = klass.new(cmd, *args)
55 # elsif keys[key].kind_of?(Proc) || keys[key].kind_of?(Method)
56 elsif TkComm._callback_entry?(keys[key])
57 keys[key] = klass.new(keys[key])
58 end
59 }
60 keys
61 end
62
63 def create_self(keys)
64 super(__conv_vcmd_on_hash_kv(keys))
65 end
66 private :create_self
67
68 def configure(slot, value=TkComm::None)
69 if slot.kind_of?(Hash)
70 super(__conv_vcmd_on_hash_kv(slot))
71 else
72 super(__conv_vcmd_on_hash_kv(slot=>value))
73 end
74 self
75 end
76 =begin
77 def configure(slot, value=TkComm::None)
78 key2class = __get_validate_key2class
79
80 if slot.kind_of?(Hash)
81 slot = _symbolkey2str(slot)
82 key2class.each{|key, klass|
83 if slot[key].kind_of?(Array)
84 cmd, *args = slot[key]
85 slot[key] = klass.new(cmd, args.join(' '))
86 elsif slot[key].kind_of?(Proc) || slot[key].kind_of?(Method)
87 slot[key] = klass.new(slot[key])
88 end
89 }
90 super(slot)
91
92 else
93 slot = slot.to_s
94 if (klass = key2class[slot])
95 if value.kind_of?(Array)
96 cmd, *args = value
97 value = klass.new(cmd, args.join(' '))
98 elsif value.kind_of?(Proc) || value.kind_of?(Method)
99 value = klass.new(value)
100 end
101 end
102 super(slot, value)
103 end
104
105 self
106 end
107 =end
108 end
109
110 module ItemValidateConfigure
111 def self.__def_validcmd(scope, klass, keys=nil)
112 keys = klass._config_keys unless keys
113 keys.each{|key|
114 eval("def item_#{key}(id, *args, &b)
115 __item_validcmd_call(#{klass.name}, '#{key}', id, *args, &b)
116 end", scope)
117 }
118 end
119
120 def __item_validcmd_call(tagOrId, klass, key, *args, &b)
121 return itemcget(tagid(tagOrId), key) if args.empty? && !b
122
123 cmd = (b)? proc(&b) : args.shift
124
125 if cmd.kind_of?(klass)
126 itemconfigure(tagid(tagOrId), key, cmd)
127 elsif !args.empty?
128 itemconfigure(tagid(tagOrId), key, [cmd, args])
129 else
130 itemconfigure(tagid(tagOrId), key, cmd)
131 end
132 end
133
134 def __item_validation_class_list(id)
135 # maybe need to override
136 []
137 end
138
139 def __get_item_validate_key2class(id)
140 k2c = {}
141 __item_validation_class_list(id).each{|klass|
142 klass._config_keys.each{|key|
143 k2c[key.to_s] = klass
144 }
145 }
146 end
147
148 def __conv_item_vcmd_on_hash_kv(keys)
149 key2class = __get_item_validate_key2class(tagid(tagOrId))
150
151 keys = _symbolkey2str(keys)
152 key2class.each{|key, klass|
153 if keys[key].kind_of?(Array)
154 cmd, *args = keys[key]
155 #keys[key] = klass.new(cmd, args.join(' '))
156 keys[key] = klass.new(cmd, *args)
157 # elsif keys[key].kind_of?(Proc) || keys[key].kind_of?(Method)
158 elsif TkComm._callback_entry?(keys[key])
159 keys[key] = klass.new(keys[key])
160 end
161 }
162 keys
163 end
164
165 def itemconfigure(tagOrId, slot, value=TkComm::None)
166 if slot.kind_of?(Hash)
167 super(__conv_item_vcmd_on_hash_kv(slot))
168 else
169 super(__conv_item_vcmd_on_hash_kv(slot=>value))
170 end
171 self
172 end
173 =begin
174 def itemconfigure(tagOrId, slot, value=TkComm::None)
175 key2class = __get_item_validate_key2class(tagid(tagOrId))
176
177 if slot.kind_of?(Hash)
178 slot = _symbolkey2str(slot)
179 key2class.each{|key, klass|
180 if slot[key].kind_of?(Array)
181 cmd, *args = slot[key]
182 slot[key] = klass.new(cmd, args.join(' '))
183 elsif slot[key].kind_of?(Proc) || slot[key].kind_of?(Method)
184 slot[key] = klass.new(slot[key])
185 end
186 }
187 super(slot)
188
189 else
190 slot = slot.to_s
191 if (klass = key2class[slot])
192 if value.kind_of?(Array)
193 cmd, *args = value
194 value = klass.new(cmd, args.join(' '))
195 elsif value.kind_of?(Proc) || value.kind_of?(Method)
196 value = klass.new(value)
197 end
198 end
199 super(slot, value)
200 end
201
202 self
203 end
204 =end
205 end
206 end
207
208 class TkValidateCommand
209 include TkComm
210 extend TkComm
211
212 class ValidateArgs < TkUtil::CallbackSubst
213 KEY_TBL = [
214 [ ?d, ?n, :action ],
215 [ ?i, ?x, :index ],
216 [ ?s, ?e, :current ],
217 [ ?v, ?s, :type ],
218 [ ?P, ?e, :value ],
219 [ ?S, ?e, :string ],
220 [ ?V, ?s, :triggered ],
221 [ ?W, ?w, :widget ],
222 nil
223 ]
224
225 PROC_TBL = [
226 [ ?n, TkComm.method(:number) ],
227 [ ?s, TkComm.method(:string) ],
228 [ ?w, TkComm.method(:window) ],
229
230 [ ?e, proc{|val|
231 #enc = Tk.encoding
232 enc = ((Tk.encoding)? Tk.encoding : Tk.encoding_system)
233 if enc
234 Tk.fromUTF8(TkComm::string(val), enc)
235 else
236 TkComm::string(val)
237 end
238 }
239 ],
240
241 [ ?x, proc{|val|
242 idx = TkComm::number(val)
243 if idx < 0
244 nil
245 else
246 idx
247 end
248 }
249 ],
250
251 nil
252 ]
253
254 =begin
255 # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
256 KEY_TBL.map!{|inf|
257 if inf.kind_of?(Array)
258 inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
259 inf[1] = inf[1].getbyte(0) if inf[1].kind_of?(String)
260 end
261 inf
262 }
263
264 PROC_TBL.map!{|inf|
265 if inf.kind_of?(Array)
266 inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
267 end
268 inf
269 }
270 =end
271
272 _setup_subst_table(KEY_TBL, PROC_TBL);
273
274 #
275 # NOTE: The order of parameters which passed to callback procedure is
276 # <extra_arg>, <extra_arg>, ... , <subst_arg>, <subst_arg>, ...
277 #
278
279 #def self._get_extra_args_tbl
280 # # return an array of convert procs
281 # []
282 #end
283
284 def self.ret_val(val)
285 (val)? '1': '0'
286 end
287 end
288
289 ###############################################
290
291 def self._config_keys
292 # array of config-option key (string or symbol)
293 ['vcmd', 'validatecommand', 'invcmd', 'invalidcommand']
294 end
295
296 def _initialize_for_cb_class(klass, cmd = Proc.new, *args)
297 extra_args_tbl = klass._get_extra_args_tbl
298
299 if args.compact.size > 0
300 args.map!{|arg| klass._sym2subst(arg)}
301 args = args.join(' ')
302 keys = klass._get_subst_key(args)
303 if cmd.kind_of?(String)
304 id = cmd
305 elsif cmd.kind_of?(TkCallbackEntry)
306 @id = install_cmd(cmd)
307 else
308 @id = install_cmd(proc{|*arg|
309 ex_args = []
310 extra_args_tbl.reverse_each{|conv| ex_args << conv.call(arg.pop)}
311 klass.ret_val(cmd.call(
312 *(ex_args.concat(klass.scan_args(keys, arg)))
313 ))
314 }) + ' ' + args
315 end
316 else
317 keys, args = klass._get_all_subst_keys
318 if cmd.kind_of?(String)
319 id = cmd
320 elsif cmd.kind_of?(TkCallbackEntry)
321 @id = install_cmd(cmd)
322 else
323 @id = install_cmd(proc{|*arg|
324 ex_args = []
325 extra_args_tbl.reverse_each{|conv| ex_args << conv.call(arg.pop)}
326 klass.ret_val(cmd.call(
327 *(ex_args << klass.new(*klass.scan_args(keys, arg)))
328 ))
329 }) + ' ' + args
330 end
331 end
332 end
333
334 def initialize(cmd = Proc.new, *args)
335 _initialize_for_cb_class(self.class::ValidateArgs, cmd, *args)
336 end
337
338 def to_eval
339 @id
340 end
341 end
342
343 module TkValidation
344 include Tk::ValidateConfigure
345
346 class ValidateCmd < TkValidateCommand
347 module Action
348 Insert = 1
349 Delete = 0
350 Others = -1
351 Focus = -1
352 Forced = -1
353 Textvariable = -1
354 TextVariable = -1
355 end
356 end
357
358 #####################################
359
360 def __validation_class_list
361 super() << ValidateCmd
362 end
363
364 Tk::ValidateConfigure.__def_validcmd(binding, ValidateCmd)
365
366 =begin
367 def validatecommand(cmd = Proc.new, args = nil)
368 if cmd.kind_of?(ValidateCmd)
369 configure('validatecommand', cmd)
370 elsif args
371 configure('validatecommand', [cmd, args])
372 else
373 configure('validatecommand', cmd)
374 end
375 end
376 =end
377 # def validatecommand(*args, &b)
378 # __validcmd_call(ValidateCmd, 'validatecommand', *args, &b)
379 # end
380 # alias vcmd validatecommand
381
382 =begin
383 def invalidcommand(cmd = Proc.new, args = nil)
384 if cmd.kind_of?(ValidateCmd)
385 configure('invalidcommand', cmd)
386 elsif args
387 configure('invalidcommand', [cmd, args])
388 else
389 configure('invalidcommand', cmd)
390 end
391 end
392 =end
393 # def invalidcommand(*args, &b)
394 # __validcmd_call(ValidateCmd, 'invalidcommand', *args, &b)
395 # end
396 # alias invcmd invalidcommand
397 end