1 #
2 # tk/namespace.rb : methods to manipulate Tcl/Tk namespace
3 # by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp>
4 #
5 require 'tk'
6
7 class TkNamespace < TkObject
8 extend Tk
9
10 TkCommandNames = [
11 'namespace'.freeze,
12 ].freeze
13
14 Tk_Namespace_ID_TBL = TkCore::INTERP.create_table
15
16 (Tk_Namespace_ID = ["ns".freeze, "00000".taint]).instance_eval{
17 @mutex = Mutex.new
18 def mutex; @mutex; end
19 freeze
20 }
21
22 Tk_NsCode_RetObjID_TBL = TkCore::INTERP.create_table
23
24 TkCore::INTERP.init_ip_env{
25 Tk_Namespace_ID_TBL.mutex.synchronize{ Tk_Namespace_ID_TBL.clear }
26 Tk_NsCode_RetObjID_TBL.mutex.synchronize{ Tk_NsCode_RetObjID_TBL.clear }
27 }
28
29 def TkNamespace.id2obj(id)
30 Tk_Namespace_ID_TBL.mutex.synchronize{
31 Tk_Namespace_ID_TBL[id]? Tk_Namespace_ID_TBL[id]: id
32 }
33 end
34
35 #####################################
36
37 class Ensemble < TkObject
38 def __cget_cmd
39 ['namespace', 'ensemble', 'configure', self.path]
40 end
41 private :__cget_cmd
42
43 def __config_cmd
44 ['namespace', 'ensemble', 'configure', self.path]
45 end
46 private :__config_cmd
47
48 def __configinfo_struct
49 {:key=>0, :alias=>nil, :db_name=>nil, :db_class=>nil,
50 :default_value=>nil, :current_value=>2}
51 end
52 private :__configinfo_struct
53
54 def __boolval_optkeys
55 ['prefixes']
56 end
57 private :__boolval_optkeys
58
59 def __listval_optkeys
60 ['map', 'subcommands', 'unknown']
61 end
62 private :__listval_optkeys
63
64 def self.exist?(ensemble)
65 bool(tk_call('namespace', 'ensemble', 'exists', ensemble))
66 end
67
68 def initialize(keys = {})
69 @ensemble = @path = tk_call('namespace', 'ensemble', 'create', keys)
70 end
71
72 def cget(slot)
73 if slot == :namespace || slot == 'namespace'
74 ns = super(slot)
75 Tk_Namespace_ID_TBL.mutex.synchronize{
76 if TkNamespace::Tk_Namespace_ID_TBL.key?(ns)
77 TkNamespace::Tk_Namespace_ID_TBL[ns]
78 else
79 ns
80 end
81 }
82 else
83 super(slot)
84 end
85 end
86 def cget_strict(slot)
87 if slot == :namespace || slot == 'namespace'
88 ns = super(slot)
89 Tk_Namespace_ID_TBL.mutex.synchronize{
90 if TkNamespace::Tk_Namespace_ID_TBL.key?(ns)
91 TkNamespace::Tk_Namespace_ID_TBL[ns]
92 else
93 ns
94 end
95 }
96 else
97 super(slot)
98 end
99 end
100
101 def configinfo(slot = nil)
102 if slot
103 if slot == :namespace || slot == 'namespace'
104 val = super(slot)
105 Tk_Namespace_ID_TBL.mutex.synchronize{
106 if TkNamespace::Tk_Namespace_ID_TBL.key?(val)
107 val = TkNamespace::Tk_Namespace_ID_TBL[val]
108 end
109 }
110 else
111 val = super(slot)
112 end
113
114 if TkComm::GET_CONFIGINFO_AS_ARRAY
115 [slot.to_s, val]
116 else # ! TkComm::GET_CONFIGINFO_AS_ARRAY
117 {slot.to_s => val}
118 end
119
120 else
121 info = super()
122
123 if TkComm::GET_CONFIGINFO_AS_ARRAY
124 Tk_Namespace_ID_TBL.mutex.synchronize{
125 info.map!{|inf|
126 if inf[0] == 'namespace' &&
127 TkNamespace::Tk_Namespace_ID_TBL.key?(inf[-1])
128 [inf[0], TkNamespace::Tk_Namespace_ID_TBL[inf[-1]]]
129 else
130 inf
131 end
132 }
133 }
134 else # ! TkComm::GET_CONFIGINFO_AS_ARRAY
135 val = info['namespace']
136 Tk_Namespace_ID_TBL.mutex.synchronize{
137 if TkNamespace::Tk_Namespace_ID_TBL.key?(val)
138 info['namespace'] = TkNamespace::Tk_Namespace_ID_TBL[val]
139 end
140 }
141 end
142
143 info
144 end
145 end
146
147 def exists?
148 bool(tk_call('namespace', 'ensemble', 'exists', @path))
149 end
150 end
151
152 #####################################
153
154 class ScopeArgs < Array
155 include Tk
156
157 # alias __tk_call tk_call
158 # alias __tk_call_without_enc tk_call_without_enc
159 # alias __tk_call_with_enc tk_call_with_enc
160 def tk_call(*args)
161 #super('namespace', 'eval', @namespace, *args)
162 args = args.collect{|arg| (s = _get_eval_string(arg, true))? s: ''}
163 super('namespace', 'eval', @namespace,
164 TkCore::INTERP._merge_tklist(*args))
165 end
166 def tk_call_without_enc(*args)
167 #super('namespace', 'eval', @namespace, *args)
168 args = args.collect{|arg| (s = _get_eval_string(arg, true))? s: ''}
169 super('namespace', 'eval', @namespace,
170 TkCore::INTERP._merge_tklist(*args))
171 end
172 def tk_call_with_enc(*args)
173 #super('namespace', 'eval', @namespace, *args)
174 args = args.collect{|arg| (s = _get_eval_string(arg, true))? s: ''}
175 super('namespace', 'eval', @namespace,
176 TkCore::INTERP._merge_tklist(*args))
177 end
178
179 def initialize(namespace, *args)
180 @namespace = namespace
181 super(args.size)
182 self.replace(args)
183 end
184 end
185
186 #####################################
187
188 class NsCode < TkObject
189 def initialize(scope, use_obj_id = false)
190 @scope = scope + ' '
191 @use_obj_id = use_obj_id
192 end
193 def path
194 @scope
195 end
196 def to_eval
197 @scope
198 end
199 def call(*args)
200 ret = TkCore::INTERP._eval_without_enc(@scope + array2tk_list(args))
201 if @use_obj_id
202 ret = TkNamespace::Tk_NsCode_RetObjID_TBL.delete(ret.to_i)
203 end
204 ret
205 end
206 end
207
208 #####################################
209
210 def install_cmd(cmd)
211 lst = tk_split_simplelist(super(cmd), false, false)
212 if lst[1] =~ /^::/
213 lst[1] = @fullname
214 else
215 lst.insert(1, @fullname)
216 end
217 TkCore::INTERP._merge_tklist(*lst)
218 end
219
220 alias __tk_call tk_call
221 alias __tk_call_without_enc tk_call_without_enc
222 alias __tk_call_with_enc tk_call_with_enc
223 def tk_call(*args)
224 #super('namespace', 'eval', @fullname, *args)
225 args = args.collect{|arg| (s = _get_eval_string(arg, true))? s: ''}
226 super('namespace', 'eval', @fullname,
227 TkCore::INTERP._merge_tklist(*args))
228 end
229 def tk_call_without_enc(*args)
230 #super('namespace', 'eval', @fullname, *args)
231 args = args.collect{|arg| (s = _get_eval_string(arg, true))? s: ''}
232 super('namespace', 'eval', @fullname,
233 TkCore::INTERP._merge_tklist(*args))
234 end
235 def tk_call_with_enc(*args)
236 #super('namespace', 'eval', @fullname, *args)
237 args = args.collect{|arg| (s = _get_eval_string(arg, true))? s: ''}
238 super('namespace', 'eval', @fullname,
239 TkCore::INTERP._merge_tklist(*args))
240 end
241 alias ns_tk_call tk_call
242 alias ns_tk_call_without_enc tk_call_without_enc
243 alias ns_tk_call_with_enc tk_call_with_enc
244
245 def initialize(name = nil, parent = nil)
246 unless name
247 Tk_Namespace_ID.mutex.synchronize{
248 # name = Tk_Namespace_ID.join('')
249 name = Tk_Namespace_ID.join(TkCore::INTERP._ip_id_)
250 Tk_Namespace_ID[1].succ!
251 }
252 end
253 name = __tk_call('namespace', 'current') if name == ''
254 if parent
255 if parent =~ /^::/
256 if name =~ /^::/
257 @fullname = parent + name
258 else
259 @fullname = parent +'::'+ name
260 end
261 else
262 ancestor = __tk_call('namespace', 'current')
263 ancestor = '' if ancestor == '::'
264 if name =~ /^::/
265 @fullname = ancestor + '::' + parent + name
266 else
267 @fullname = ancestor + '::'+ parent +'::'+ name
268 end
269 end
270 else # parent == nil
271 ancestor = __tk_call('namespace', 'current')
272 ancestor = '' if ancestor == '::'
273 if name =~ /^::/
274 @fullname = name
275 else
276 @fullname = ancestor + '::' + name
277 end
278 end
279 @path = @fullname
280 @parent = __tk_call('namespace', 'qualifiers', @fullname)
281 @name = __tk_call('namespace', 'tail', @fullname)
282
283 # create namespace
284 __tk_call('namespace', 'eval', @fullname, '')
285
286 Tk_Namespace_ID_TBL.mutex.synchronize{
287 Tk_Namespace_ID_TBL[@fullname] = self
288 }
289 end
290
291 def self.children(*args)
292 # args ::= [<namespace>] [<pattern>]
293 # <pattern> must be glob-style pattern
294 tk_split_simplelist(tk_call('namespace', 'children', *args)).collect{|ns|
295 # ns is fullname
296 Tk_Namespace_ID_TBL.mutex.synchronize{
297 if Tk_Namespace_ID_TBL.key?(ns)
298 Tk_Namespace_ID_TBL[ns]
299 else
300 ns
301 end
302 }
303 }
304 end
305 def children(pattern=None)
306 TkNamespace.children(@fullname, pattern)
307 end
308
309 def self.code(script = Proc.new)
310 TkNamespace.new('').code(script)
311 end
312 =begin
313 def code(script = Proc.new)
314 if script.kind_of?(String)
315 cmd = proc{|*args| ScopeArgs.new(@fullname,*args).instance_eval(script)}
316 elsif script.kind_of?(Proc)
317 cmd = proc{|*args| ScopeArgs.new(@fullname,*args).instance_eval(&script)}
318 else
319 fail ArgumentError, "String or Proc is expected"
320 end
321 TkNamespace::NsCode.new(tk_call_without_enc('namespace', 'code',
322 _get_eval_string(cmd, false)))
323 end
324 =end
325 def code(script = Proc.new)
326 if script.kind_of?(String)
327 cmd = proc{|*args|
328 if TkCore::WITH_RUBY_VM ### Ruby 1.9 !!!!
329 obj = ScopeArgs.new(@fullname,*args)
330 ret = obj.instance_exec(obj, script)
331 else
332 ret = ScopeArgs.new(@fullname,*args).instance_eval(script)
333 end
334 id = ret.object_id
335 TkNamespace::Tk_NsCode_RetObjID_TBL[id] = ret
336 id
337 }
338 elsif script.kind_of?(Proc)
339 cmd = proc{|*args|
340 if TkCore::WITH_RUBY_VM ### Ruby 1.9 !!!!
341 obj = ScopeArgs.new(@fullname,*args)
342 ret = obj.instance_exec(obj, &script)
343 else
344 ret = ScopeArgs.new(@fullname,*args).instance_eval(&script)
345 end
346 id = ret.object_id
347 TkNamespace::Tk_NsCode_RetObjID_TBL[id] = ret
348 id
349 }
350 else
351 fail ArgumentError, "String or Proc is expected"
352 end
353 TkNamespace::NsCode.new(tk_call_without_enc('namespace', 'code',
354 _get_eval_string(cmd, false)),
355 true)
356 end
357
358 def self.current_path
359 tk_call('namespace', 'current')
360 end
361 def current_path
362 @fullname
363 end
364
365 def self.current
366 ns = self.current_path
367 Tk_Namespace_ID_TBL.mutex.synchronize{
368 if Tk_Namespace_ID_TBL.key?(ns)
369 Tk_Namespace_ID_TBL[ns]
370 else
371 ns
372 end
373 }
374 end
375 def current_namespace
376 # ns_tk_call('namespace', 'current')
377 # @fullname
378 self
379 end
380 alias current current_namespace
381
382 def self.delete(*ns_list)
383 tk_call('namespace', 'delete', *ns_list)
384 ns_list.each{|ns|
385 Tk_Namespace_ID_TBL.mutex.synchronize{
386 if ns.kind_of?(TkNamespace)
387 Tk_Namespace_ID_TBL.delete(ns.path)
388 else
389 Tk_Namespace_ID_TBL.delete(ns.to_s)
390 end
391 }
392 }
393 end
394 def delete
395 TkNamespece.delete(@fullname)
396 end
397
398 def self.ensemble_create(*keys)
399 tk_call('namespace', 'ensemble', 'create', *hash_kv(keys))
400 end
401 def self.ensemble_configure(cmd, slot, value=None)
402 if slot.kind_of?(Hash)
403 tk_call('namespace', 'ensemble', 'configure', cmd, *hash_kv(slot))
404 else
405 tk_call('namespace', 'ensemble', 'configure', cmd, '-'+slot.to_s, value)
406 end
407 end
408 def self.ensemble_configinfo(cmd, slot = nil)
409 if slot
410 tk_call('namespace', 'ensemble', 'configure', cmd, '-' + slot.to_s)
411 else
412 inf = {}
413 Hash(*tk_split_simplelist(tk_call('namespace', 'ensemble', 'configure', cmd))).each{|k, v| inf[k[1..-1]] = v}
414 inf
415 end
416 end
417 def self.ensemble_exist?(cmd)
418 bool(tk_call('namespace', 'ensemble', 'exists', cmd))
419 end
420
421 def self.eval(namespace, cmd = Proc.new, *args)
422 #tk_call('namespace', 'eval', namespace, cmd, *args)
423 TkNamespace.new(namespace).eval(cmd, *args)
424 end
425 =begin
426 def eval(cmd = Proc.new, *args)
427 #TkNamespace.eval(@fullname, cmd, *args)
428 #ns_tk_call(cmd, *args)
429 code_obj = code(cmd)
430 ret = code_obj.call(*args)
431 # uninstall_cmd(TkCore::INTERP._split_tklist(code_obj.path)[-1])
432 uninstall_cmd(_fromUTF8(TkCore::INTERP._split_tklist(_toUTF8(code_obj.path))[-1]))
433 tk_tcl2ruby(ret)
434 end
435 =end
436 def eval(cmd = Proc.new, *args)
437 code_obj = code(cmd)
438 ret = code_obj.call(*args)
439 uninstall_cmd(_fromUTF8(TkCore::INTERP._split_tklist(_toUTF8(code_obj.path))[-1]))
440 ret
441 end
442
443 def self.exist?(ns)
444 bool(tk_call('namespace', 'exists', ns))
445 end
446 def exist?
447 TkNamespece.exist?(@fullname)
448 end
449
450 def self.export(*patterns)
451 tk_call('namespace', 'export', *patterns)
452 end
453 def self.export_with_clear(*patterns)
454 tk_call('namespace', 'export', '-clear', *patterns)
455 end
456 def export
457 TkNamespace.export(@fullname)
458 end
459 def export_with_clear
460 TkNamespace.export_with_clear(@fullname)
461 end
462
463 def self.forget(*patterns)
464 tk_call('namespace', 'forget', *patterns)
465 end
466 def forget
467 TkNamespace.forget(@fullname)
468 end
469
470 def self.import(*patterns)
471 tk_call('namespace', 'import', *patterns)
472 end
473 def self.force_import(*patterns)
474 tk_call('namespace', 'import', '-force', *patterns)
475 end
476 def import
477 TkNamespace.import(@fullname)
478 end
479 def force_import
480 TkNamespace.force_import(@fullname)
481 end
482
483 def self.inscope(namespace, script, *args)
484 tk_call('namespace', 'inscope', namespace, script, *args)
485 end
486 def inscope(script, *args)
487 TkNamespace.inscope(@fullname, script, *args)
488 end
489
490 def self.origin(cmd)
491 tk_call('namespace', 'origin', cmd)
492 end
493
494 def self.parent(namespace=None)
495 ns = tk_call('namespace', 'parent', namespace)
496 Tk_Namespace_ID_TBL.mutex.synchronize{
497 if Tk_Namespace_ID_TBL.key?(ns)
498 Tk_Namespace_ID_TBL[ns]
499 else
500 ns
501 end
502 }
503 end
504 def parent
505 tk_call('namespace', 'parent', @fullname)
506 end
507
508 def self.get_path
509 tk_call('namespace', 'path')
510 end
511 def self.set_path(*namespace_list)
512 tk_call('namespace', 'path', array2tk_list(namespace_list))
513 end
514 def set_path
515 tk_call('namespace', 'path', @fullname)
516 end
517
518 def self.qualifiers(str)
519 tk_call('namespace', 'qualifiers', str)
520 end
521
522 def self.tail(str)
523 tk_call('namespace', 'tail', str)
524 end
525
526 def self.upvar(namespace, *var_pairs)
527 tk_call('namespace', 'upvar', namespace, *(var_pairs.flatten))
528 end
529 def upvar(*var_pairs)
530 TkNamespace.inscope(@fullname, *(var_pairs.flatten))
531 end
532
533 def self.get_unknown_handler
534 tk_tcl2ruby(tk_call('namespace', 'unknown'))
535 end
536 def self.set_unknown_handler(cmd = Proc.new)
537 tk_call('namespace', 'unknown', cmd)
538 end
539
540 def self.which(name)
541 tk_call('namespace', 'which', name)
542 end
543 def self.which_command(name)
544 tk_call('namespace', 'which', '-command', name)
545 end
546 def self.which_variable(name)
547 tk_call('namespace', 'which', '-variable', name)
548 end
549 end
550
551 TkNamespace::Global = TkNamespace.new('::')