1 #
2 # tk/variable.rb : treat Tk variable object
3 #
4 require 'tk'
5
6 class TkVariable
7 include Tk
8 extend TkCore
9
10 include Comparable
11
12 #TkCommandNames = ['tkwait'.freeze].freeze
13 TkCommandNames = ['vwait'.freeze].freeze
14
15 #TkVar_CB_TBL = {}
16 #TkVar_ID_TBL = {}
17 TkVar_CB_TBL = TkCore::INTERP.create_table
18 TkVar_ID_TBL = TkCore::INTERP.create_table
19 (Tk_VARIABLE_ID = ["v".freeze, "00000".taint]).instance_eval{
20 @mutex = Mutex.new
21 def mutex; @mutex; end
22 freeze
23 }
24 TkCore::INTERP.init_ip_env{
25 TkVar_CB_TBL.mutex.synchronize{ TkVar_CB_TBL.clear }
26 TkVar_ID_TBL.mutex.synchronize{ TkVar_ID_TBL.clear }
27 }
28
29 major, minor, type, type_name, patchlevel = TclTkLib.get_version
30 USE_OLD_TRACE_OPTION_STYLE = (major < 8) || (major == 8 && minor < 4)
31
32 #TkCore::INTERP.add_tk_procs('rb_var', 'args',
33 # "ruby [format \"TkVariable.callback %%Q!%s!\" $args]")
34 TkCore::INTERP.add_tk_procs('rb_var', 'args', <<-'EOL')
35 if {[set st [catch {eval {ruby_cmd TkVariable callback} $args} ret]] != 0} {
36 set idx [string first "\n\n" $ret]
37 if {$idx > 0} {
38 global errorInfo
39 set tcl_backtrace $errorInfo
40 set errorInfo [string range $ret [expr $idx + 2] \
41 [string length $ret]]
42 append errorInfo "\n" $tcl_backtrace
43 bgerror [string range $ret 0 [expr $idx - 1]]
44 } else {
45 bgerror $ret
46 }
47 return ""
48 #return -code $st $ret
49 } else {
50 return $ret
51 }
52 EOL
53
54 #def TkVariable.callback(args)
55 def TkVariable.callback(id, name1, name2, op)
56 #name1,name2,op = tk_split_list(args)
57 #name1,name2,op = tk_split_simplelist(args)
58 if cb_obj = TkVar_CB_TBL[id]
59 #_get_eval_string(TkVar_CB_TBL[name1].trace_callback(name2,op))
60 begin
61 _get_eval_string(cb_obj.trace_callback(name2, op))
62 rescue SystemExit
63 exit(0)
64 rescue Interrupt
65 exit!(1)
66 rescue Exception => e
67 begin
68 msg = _toUTF8(e.class.inspect) + ': ' +
69 _toUTF8(e.message) + "\n" +
70 "\n---< backtrace of Ruby side >-----\n" +
71 _toUTF8(e.backtrace.join("\n")) +
72 "\n---< backtrace of Tk side >-------"
73 if TkCore::WITH_ENCODING
74 msg.force_encoding('utf-8')
75 else
76 msg.instance_variable_set(:@encoding, 'utf-8')
77 end
78 rescue Exception
79 msg = e.class.inspect + ': ' + e.message + "\n" +
80 "\n---< backtrace of Ruby side >-----\n" +
81 e.backtrace.join("\n") +
82 "\n---< backtrace of Tk side >-------"
83 end
84 fail(e, msg)
85 end
86 =begin
87 begin
88 raise 'check backtrace'
89 rescue
90 # ignore backtrace before 'callback'
91 pos = -($!.backtrace.size)
92 end
93 begin
94 _get_eval_string(TkVar_CB_TBL[name1].trace_callback(name2,op))
95 rescue
96 trace = $!.backtrace
97 raise $!, "\n#{trace[0]}: #{$!.message} (#{$!.class})\n" +
98 "\tfrom #{trace[1..pos].join("\n\tfrom ")}"
99 end
100 =end
101 else
102 ''
103 end
104 end
105
106 def self.new_hash(val = {})
107 if val.kind_of?(Hash)
108 self.new(val)
109 else
110 fail ArgumentError, 'Hash is expected'
111 end
112 end
113
114 #
115 # default_value is available only when the variable is an assoc array.
116 #
117 def default_value(val=nil, &b)
118 if b
119 @def_default = :proc
120 @default_val = proc(&b)
121 else
122 @def_default = :val
123 @default_val = val
124 end
125 self
126 end
127 def set_default_value(val)
128 @def_default = :val
129 @default_val = val
130 self
131 end
132 alias default_value= set_default_value
133 def default_proc(cmd = Proc.new)
134 @def_default = :proc
135 @default_val = cmd
136 self
137 end
138
139 def undef_default
140 @default_val = nil
141 @def_default = false
142 self
143 end
144
145 def default_value_type
146 @type
147 end
148 def default_element_value_type(idxs)
149 if idxs.kind_of?(Array)
150 index = idxs.collect{|idx| _get_eval_string(idx, true)}.join(',')
151 else
152 index = _get_eval_string(idxs, true)
153 end
154 @element_type[index]
155 end
156
157 def _set_default_value_type_core(type, idxs)
158 if type.kind_of?(Class)
159 if type == NilClass
160 type = nil
161 elsif type == Numeric
162 type = :numeric
163 elsif type == TrueClass || type == FalseClass
164 type = :bool
165 elsif type == String
166 type = :string
167 elsif type == Symbol
168 type = :symbol
169 elsif type == Array
170 type = :list
171 elsif type <= TkVariable
172 type = :variable
173 elsif type <= TkWindow
174 type = :window
175 elsif TkComm._callback_entry_class?(type)
176 type = :procedure
177 else
178 type = nil
179 end
180 else
181 case(type)
182 when nil
183 type = nil
184 when :numeric, 'numeric'
185 type = :numeric
186 when true, false, :bool, 'bool'
187 type = :bool
188 when :string, 'string'
189 type = :string
190 when :symbol, 'symbol'
191 type = :symbol
192 when :list, 'list'
193 type = :list
194 when :numlist, 'numlist'
195 type = :numlist
196 when :variable, 'variable'
197 type = :variable
198 when :window, 'window'
199 type = :window
200 when :procedure, 'procedure'
201 type = :procedure
202 else
203 return _set_default_value_type_core(type.class, idxs)
204 end
205 end
206 if idxs
207 if idxs.kind_of?(Array)
208 index = idxs.collect{|idx| _get_eval_string(idx, true)}.join(',')
209 else
210 index = _get_eval_string(idxs, true)
211 end
212 @element_type[index] = type
213 else
214 @type = type
215 end
216 type
217 end
218 private :_set_default_value_type_core
219
220 def set_default_value_type(type)
221 _set_default_value_type_core(type, nil)
222 self
223 end
224 alias default_value_type= set_default_value_type
225
226 def set_default_element_value_type(idxs, type)
227 _set_default_value_type_core(type, idxs)
228 self
229 end
230
231 def _to_default_type(val, idxs = nil)
232 if idxs
233 if idxs.kind_of?(Array)
234 index = idxs.collect{|idx| _get_eval_string(idx, true)}.join(',')
235 else
236 index = _get_eval_string(idxs, true)
237 end
238 type = @element_type[index]
239 else
240 type = @type
241 end
242 return val unless type
243 if val.kind_of?(Hash)
244 val.keys.each{|k| val[k] = _to_default_type(val[k], idxs) }
245 val
246 else
247 begin
248 case(type)
249 when :numeric
250 number(val)
251 when :bool
252 TkComm.bool(val)
253 when :string
254 val
255 when :symbol
256 val.intern
257 when :list
258 tk_split_simplelist(val)
259 when :numlist
260 tk_split_simplelist(val).collect!{|v| number(v)}
261 when :variable
262 TkVarAccess.new(val)
263 when :window
264 TkComm.window(val)
265 when :procedure
266 TkComm.procedure(val)
267 else
268 val
269 end
270 rescue
271 val
272 end
273 end
274 end
275 private :_to_default_type
276
277 def _to_default_element_type(idxs, val)
278 _to_default_type(val, idxs)
279 end
280 private :_to_default_element_type
281
282 def initialize(val="", type=nil)
283 # @id = Tk_VARIABLE_ID.join('')
284 begin
285 Tk_VARIABLE_ID.mutex.synchronize{
286 @id = Tk_VARIABLE_ID.join(TkCore::INTERP._ip_id_)
287 Tk_VARIABLE_ID[1].succ!
288 }
289 end until INTERP._invoke_without_enc('info', 'globals', @id).empty?
290
291 TkVar_ID_TBL.mutex.synchronize{
292 TkVar_ID_TBL[@id] = self
293 }
294
295 @var = @id
296 @elem = nil
297
298 @def_default = false
299 @default_val = nil
300
301 @trace_var = nil
302 @trace_elem = nil
303 @trace_opts = nil
304
305 @type = nil
306 var = self
307 @element_type = Hash.new{|k,v| var.default_value_type }
308
309 self.default_value_type = type
310
311 # teach Tk-ip that @id is global var
312 INTERP._invoke_without_enc('global', @id)
313 #INTERP._invoke('global', @id)
314
315 # create and init
316 if val.kind_of?(Hash)
317 # assoc-array variable
318 self[''] = 0
319 self.clear
320 end
321 self.value = val
322
323 =begin
324 if val == []
325 # INTERP._eval(format('global %s; set %s(0) 0; unset %s(0)',
326 # @id, @id, @id))
327 elsif val.kind_of?(Array)
328 a = []
329 # val.each_with_index{|e,i| a.push(i); a.push(array2tk_list(e))}
330 # s = '"' + a.join(" ").gsub(/[\[\]$"]/, '\\\\\&') + '"'
331 val.each_with_index{|e,i| a.push(i); a.push(e)}
332 #s = '"' + array2tk_list(a).gsub(/[\[\]$"]/, '\\\\\&') + '"'
333 s = '"' + array2tk_list(a).gsub(/[\[\]$"\\]/, '\\\\\&') + '"'
334 INTERP._eval(format('global %s; array set %s %s', @id, @id, s))
335 elsif val.kind_of?(Hash)
336 #s = '"' + val.to_a.collect{|e| array2tk_list(e)}.join(" ")\
337 # .gsub(/[\[\]$"]/, '\\\\\&') + '"'
338 s = '"' + val.to_a.collect{|e| array2tk_list(e)}.join(" ")\
339 .gsub(/[\[\]$"\\]/, '\\\\\&') + '"'
340 INTERP._eval(format('global %s; array set %s %s', @id, @id, s))
341 else
342 #s = '"' + _get_eval_string(val).gsub(/[\[\]$"]/, '\\\\\&') + '"'
343 s = '"' + _get_eval_string(val).gsub(/[\[\]$"\\]/, '\\\\\&') + '"'
344 INTERP._eval(format('global %s; set %s %s', @id, @id, s))
345 end
346 =end
347 =begin
348 if val.kind_of?(Hash)
349 #s = '"' + val.to_a.collect{|e| array2tk_list(e)}.join(" ")\
350 # .gsub(/[\[\]$"]/, '\\\\\&') + '"'
351 s = '"' + val.to_a.collect{|e| array2tk_list(e)}.join(" ")\
352 .gsub(/[\[\]$"\\]/, '\\\\\&') + '"'
353 INTERP._eval(Kernel.format('global %s; array set %s %s', @id, @id, s))
354 else
355 #s = '"' + _get_eval_string(val).gsub(/[\[\]$"]/, '\\\\\&') + '"'
356 s = '"' + _get_eval_string(val).gsub(/[\[\]$"\\]/, '\\\\\&') + '"'
357 INTERP._eval(Kernel.format('global %s; set %s %s', @id, @id, s))
358 end
359 =end
360 end
361
362 def wait(on_thread = false, check_root = false)
363 if $SAFE >= 4
364 fail SecurityError, "can't wait variable at $SAFE >= 4"
365 end
366 on_thread &= (Thread.list.size != 1)
367 if on_thread
368 if check_root
369 INTERP._thread_tkwait('variable', @id)
370 else
371 INTERP._thread_vwait(@id)
372 end
373 else
374 if check_root
375 INTERP._invoke_without_enc('tkwait', 'variable', @id)
376 else
377 INTERP._invoke_without_enc('vwait', @id)
378 end
379 end
380 end
381 def eventloop_wait(check_root = false)
382 wait(false, check_root)
383 end
384 def thread_wait(check_root = false)
385 wait(true, check_root)
386 end
387 def tkwait(on_thread = true)
388 wait(on_thread, true)
389 end
390 def eventloop_tkwait
391 wait(false, true)
392 end
393 def thread_tkwait
394 wait(true, true)
395 end
396
397 def id
398 @id
399 end
400
401 def ref(*idxs)
402 # "#{@id}(#{idxs.collect{|idx| _get_eval_string(idx)}.join(',')})"
403 TkVarAccess.new("#{@id}(#{idxs.collect{|idx| _get_eval_string(idx)}.join(',')})")
404 end
405
406 def is_hash?
407 #ITNERP._eval("global #{@id}; array exist #{@id}") == '1'
408 INTERP._invoke_without_enc('global', @id)
409 # INTERP._invoke_without_enc('array', 'exist', @id) == '1'
410 TkComm.bool(INTERP._invoke_without_enc('array', 'exist', @id))
411 end
412
413 def is_scalar?
414 ! is_hash?
415 end
416
417 def exist?(*elems)
418 INTERP._invoke_without_enc('global', @id)
419 if elems.empty?
420 TkComm.bool(tk_call('info', 'exist', @id))
421 else
422 # array
423 index = elems.collect{|idx| _get_eval_string(idx, true)}.join(',')
424 TkComm.bool(tk_call('info', 'exist', "#{@id}")) &&
425 TkComm.bool(tk_call('info', 'exist', "#{@id}(#{index})"))
426 end
427 end
428
429 def keys
430 if (is_scalar?)
431 fail RuntimeError, 'cannot get keys from a scalar variable'
432 end
433 #tk_split_simplelist(INTERP._eval("global #{@id}; array get #{@id}"))
434 INTERP._invoke_without_enc('global', @id)
435 #tk_split_simplelist(INTERP._fromUTF8(INTERP._invoke_without_enc('array', 'names', @id)))
436 tk_split_simplelist(INTERP._invoke_without_enc('array', 'names', @id),
437 false, true)
438 end
439
440 def size
441 INTERP._invoke_without_enc('global', @id)
442 TkComm.number(INTERP._invoke_without_enc('array', 'size', @id))
443 end
444
445 def clear
446 if (is_scalar?)
447 fail RuntimeError, 'cannot clear a scalar variable'
448 end
449 keys.each{|k| unset(k)}
450 self
451 end
452
453 def update(hash)
454 if (is_scalar?)
455 fail RuntimeError, 'cannot update a scalar variable'
456 end
457 hash.each{|k,v| self[k] = v}
458 self
459 end
460
461 unless const_defined?(:USE_TCLs_SET_VARIABLE_FUNCTIONS)
462 USE_TCLs_SET_VARIABLE_FUNCTIONS = true
463 end
464
465 if USE_TCLs_SET_VARIABLE_FUNCTIONS
466 ###########################################################################
467 # use Tcl function version of set tkvariable
468 ###########################################################################
469
470 def _value
471 #if INTERP._eval("global #{@id}; array exist #{@id}") == '1'
472 INTERP._invoke_without_enc('global', @id)
473 # if INTERP._invoke('array', 'exist', @id) == '1'
474 if TkComm.bool(INTERP._invoke('array', 'exist', @id))
475 #Hash[*tk_split_simplelist(INTERP._eval("global #{@id}; array get #{@id}"))]
476 Hash[*tk_split_simplelist(INTERP._invoke('array', 'get', @id))]
477 else
478 _fromUTF8(INTERP._get_global_var(@id))
479 end
480 end
481
482 def value=(val)
483 val = val._value if !@type && @type != :variable && val.kind_of?(TkVariable)
484 if val.kind_of?(Hash)
485 self.clear
486 val.each{|k, v|
487 #INTERP._set_global_var2(@id, _toUTF8(_get_eval_string(k)),
488 # _toUTF8(_get_eval_string(v)))
489 INTERP._set_global_var2(@id, _get_eval_string(k, true),
490 _get_eval_string(v, true))
491 }
492 self.value
493 # elsif val.kind_of?(Array)
494 =begin
495 INTERP._set_global_var(@id, '')
496 val.each{|v|
497 #INTERP._set_variable(@id, _toUTF8(_get_eval_string(v)),
498 INTERP._set_variable(@id, _get_eval_string(v, true),
499 TclTkLib::VarAccessFlag::GLOBAL_ONLY |
500 TclTkLib::VarAccessFlag::LEAVE_ERR_MSG |
501 TclTkLib::VarAccessFlag::APPEND_VALUE |
502 TclTkLib::VarAccessFlag::LIST_ELEMENT)
503 }
504 self.value
505 =end
506 # _fromUTF8(INTERP._set_global_var(@id, array2tk_list(val, true)))
507 else
508 #_fromUTF8(INTERP._set_global_var(@id, _toUTF8(_get_eval_string(val))))
509 _fromUTF8(INTERP._set_global_var(@id, _get_eval_string(val, true)))
510 end
511 end
512
513 def _element_value(*idxs)
514 index = idxs.collect{|idx| _get_eval_string(idx, true)}.join(',')
515 begin
516 _fromUTF8(INTERP._get_global_var2(@id, index))
517 rescue => e
518 case @def_default
519 when :proc
520 @default_val.call(self, *idxs)
521 when :val
522 @default_val
523 else
524 fail e
525 end
526 end
527 #_fromUTF8(INTERP._get_global_var2(@id, index))
528 #_fromUTF8(INTERP._get_global_var2(@id, _toUTF8(_get_eval_string(index))))
529 #_fromUTF8(INTERP._get_global_var2(@id, _get_eval_string(index, true)))
530 end
531
532 def []=(*args)
533 val = args.pop
534 type = default_element_value_type(args)
535 val = val._value if !type && type != :variable && val.kind_of?(TkVariable)
536 index = args.collect{|idx| _get_eval_string(idx, true)}.join(',')
537 _fromUTF8(INTERP._set_global_var2(@id, index, _get_eval_string(val, true)))
538 #_fromUTF8(INTERP._set_global_var2(@id, _toUTF8(_get_eval_string(index)),
539 # _toUTF8(_get_eval_string(val))))
540 #_fromUTF8(INTERP._set_global_var2(@id, _get_eval_string(index, true),
541 # _get_eval_string(val, true)))
542 end
543
544 def unset(*elems)
545 if elems.empty?
546 INTERP._unset_global_var(@id)
547 else
548 index = elems.collect{|idx| _get_eval_string(idx, true)}.join(',')
549 INTERP._unset_global_var2(@id, index)
550 end
551 end
552 alias remove unset
553
554 else
555 ###########################################################################
556 # use Ruby script version of set tkvariable (traditional methods)
557 ###########################################################################
558
559 def _value
560 begin
561 INTERP._eval(Kernel.format('global %s; set %s', @id, @id))
562 #INTERP._eval(Kernel.format('set %s', @id))
563 #INTERP._invoke_without_enc('set', @id)
564 rescue
565 if INTERP._eval(Kernel.format('global %s; array exists %s',
566 @id, @id)) != "1"
567 #if INTERP._eval(Kernel.format('array exists %s', @id)) != "1"
568 #if INTERP._invoke_without_enc('array', 'exists', @id) != "1"
569 fail
570 else
571 Hash[*tk_split_simplelist(INTERP._eval(Kernel.format('global %s; array get %s', @id, @id)))]
572 #Hash[*tk_split_simplelist(_fromUTF8(INTERP._invoke_without_enc('array', 'get', @id)))]
573 end
574 end
575 end
576
577 def value=(val)
578 val = val._value if !@type && @type != :variable && val.kind_of?(TkVariable)
579 begin
580 #s = '"' + _get_eval_string(val).gsub(/[\[\]$"]/, '\\\\\&') + '"'
581 s = '"' + _get_eval_string(val).gsub(/[\[\]$"\\]/, '\\\\\&') + '"'
582 INTERP._eval(Kernel.format('global %s; set %s %s', @id, @id, s))
583 #INTERP._eval(Kernel.format('set %s %s', @id, s))
584 #_fromUTF8(INTERP._invoke_without_enc('set', @id, _toUTF8(s)))
585 rescue
586 if INTERP._eval(Kernel.format('global %s; array exists %s',
587 @id, @id)) != "1"
588 #if INTERP._eval(Kernel.format('array exists %s', @id)) != "1"
589 #if INTERP._invoke_without_enc('array', 'exists', @id) != "1"
590 fail
591 else
592 if val == []
593 INTERP._eval(Kernel.format('global %s; unset %s; set %s(0) 0; unset %s(0)', @id, @id, @id, @id))
594 #INTERP._eval(Kernel.format('unset %s; set %s(0) 0; unset %s(0)',
595 # @id, @id, @id))
596 #INTERP._invoke_without_enc('unset', @id)
597 #INTERP._invoke_without_enc('set', @id+'(0)', 0)
598 #INTERP._invoke_without_enc('unset', @id+'(0)')
599 elsif val.kind_of?(Array)
600 a = []
601 val.each_with_index{|e,i| a.push(i); a.push(array2tk_list(e, true))}
602 #s = '"' + a.join(" ").gsub(/[\[\]$"]/, '\\\\\&') + '"'
603 s = '"' + a.join(" ").gsub(/[\[\]$"\\]/, '\\\\\&') + '"'
604 INTERP._eval(Kernel.format('global %s; unset %s; array set %s %s',
605 @id, @id, @id, s))
606 #INTERP._eval(Kernel.format('unset %s; array set %s %s',
607 # @id, @id, s))
608 #INTERP._invoke_without_enc('unset', @id)
609 #_fromUTF8(INTERP._invoke_without_enc('array','set', @id, _toUTF8(s)))
610 elsif val.kind_of?(Hash)
611 #s = '"' + val.to_a.collect{|e| array2tk_list(e)}.join(" ")\
612 # .gsub(/[\[\]$"]/, '\\\\\&') + '"'
613 s = '"' + val.to_a.collect{|e| array2tk_list(e, true)}.join(" ")\
614 .gsub(/[\[\]$\\"]/, '\\\\\&') + '"'
615 INTERP._eval(Kernel.format('global %s; unset %s; array set %s %s',
616 @id, @id, @id, s))
617 #INTERP._eval(Kernel.format('unset %s; array set %s %s',
618 # @id, @id, s))
619 #INTERP._invoke_without_enc('unset', @id)
620 #_fromUTF8(INTERP._invoke_without_enc('array','set', @id, _toUTF8(s)))
621 else
622 fail
623 end
624 end
625 end
626 end
627
628 def _element_value(*idxs)
629 index = idxs.collect{|idx| _get_eval_string(idx)}.join(',')
630 begin
631 INTERP._eval(Kernel.format('global %s; set %s(%s)', @id, @id, index))
632 rescue => e
633 case @def_default
634 when :proc
635 @default_val.call(self, *idxs)
636 when :val
637 @default_val
638 else
639 fail e
640 end
641 end
642 #INTERP._eval(Kernel.format('global %s; set %s(%s)', @id, @id, index))
643 #INTERP._eval(Kernel.format('global %s; set %s(%s)',
644 # @id, @id, _get_eval_string(index)))
645 #INTERP._eval(Kernel.format('set %s(%s)', @id, _get_eval_string(index)))
646 #INTERP._eval('set ' + @id + '(' + _get_eval_string(index) + ')')
647 end
648
649 def []=(*args)
650 val = args.pop
651 type = default_element_value_type(args)
652 val = val._value if !type && type != :variable && val.kind_of?(TkVariable)
653 index = args.collect{|idx| _get_eval_string(idx)}.join(',')
654 INTERP._eval(Kernel.format('global %s; set %s(%s) %s', @id, @id,
655 index, _get_eval_string(val)))
656 #INTERP._eval(Kernel.format('global %s; set %s(%s) %s', @id, @id,
657 # _get_eval_string(index), _get_eval_string(val)))
658 #INTERP._eval(Kernel.format('set %s(%s) %s', @id,
659 # _get_eval_string(index), _get_eval_string(val)))
660 #INTERP._eval('set ' + @id + '(' + _get_eval_string(index) + ') ' +
661 # _get_eval_string(val))
662 end
663
664 def unset(*elems)
665 if elems.empty?
666 INTERP._eval(Kernel.format('global %s; unset %s', @id, @id))
667 #INTERP._eval(Kernel.format('unset %s', @id))
668 #INTERP._eval('unset ' + @id)
669 else
670 index = elems.collect{|idx| _get_eval_string(idx, true)}.join(',')
671 INTERP._eval(Kernel.format('global %s; unset %s(%s)', @id, @id, index))
672 #INTERP._eval(Kernel.format('global %s; unset %s(%s)',
673 # @id, @id, _get_eval_string(elem)))
674 #INTERP._eval(Kernel.format('unset %s(%s)', @id, tk_tcl2ruby(elem)))
675 #INTERP._eval('unset ' + @id + '(' + _get_eval_string(elem) + ')')
676 end
677 end
678 alias remove unset
679
680 end
681
682 protected :_value, :_element_value
683
684 def value
685 _to_default_type(_value)
686 end
687
688 def [](*idxs)
689 _to_default_element_type(idxs, _element_value(*idxs))
690 end
691
692 def set_value(val)
693 self.value = val
694 self
695 end
696
697 def set_element_value(idxs, val)
698 if idxs.kind_of?(Array)
699 self[*idxs]=val
700 else
701 self[idxs]=val
702 end
703 self
704 end
705
706 def set_value_type(val)
707 self.default_value_type = val.class
708 self.value = val
709 self
710 end
711
712 alias value_type= set_value_type
713
714 def set_element_value_type(idxs, val)
715 self.set_default_element_value_type(idxs, val.class)
716 if idxs.kind_of?(Array)
717 self[*idxs]=val
718 else
719 self[idxs]=val
720 end
721 self
722 end
723
724 def numeric
725 number(_value)
726 end
727 def numeric_element(*idxs)
728 number(_element_value(*idxs))
729 end
730 def set_numeric(val)
731 case val
732 when Numeric
733 self.value=(val)
734 when TkVariable
735 self.value=(val.numeric)
736 else
737 raise ArgumentError, "Numeric is expected"
738 end
739 self
740 end
741 alias numeric= set_numeric
742 def set_numeric_element(idxs, val)
743 case val
744 when Numeric
745 val
746 when TkVariable
747 val = val.numeric
748 else
749 raise ArgumentError, "Numeric is expected"
750 end
751 if idxs.kind_of?(Array)
752 self[*idxs]=val
753 else
754 self[idxs]=val
755 end
756 self
757 end
758 def set_numeric_type(val)
759 @type = :numeric
760 self.numeric=(val)
761 self
762 end
763 alias numeric_type= set_numeric_type
764 def set_numeric_element_type(idxs, val)
765 self.set_default_element_value_type(idxs, :numeric)
766 self.set_numeric_element(idxs, val)
767 end
768
769 def bool
770 TkComm.bool(_value)
771 =begin
772 # see Tcl_GetBoolean man-page
773 case _value.downcase
774 when '0', 'false', 'no', 'off'
775 false
776 else
777 true
778 end
779 =end
780 end
781 def bool_element(*idxs)
782 TkComm.bool(_element_value(*idxs))
783 end
784 def set_bool(val)
785 if ! val
786 self.value = '0'
787 else
788 case val.to_s.downcase
789 when 'false', '0', 'no', 'off'
790 self.value = '0'
791 else
792 self.value = '1'
793 end
794 end
795 self
796 end
797 alias bool= set_bool
798 def set_bool_element(idxs, val)
799 if ! val
800 val = '0'
801 else
802 case val.to_s.downcase
803 when 'false', '0', 'no', 'off'
804 val = '0'
805 else
806 val = '1'
807 end
808 end
809 if idxs.kind_of?(Array)
810 self[*idxs]=val
811 else
812 self[idxs]=val
813 end
814 self
815 end
816 def set_bool_type(val)
817 @type = :bool
818 self.bool=(val)
819 self
820 end
821 alias bool_type= set_bool_type
822 def set_bool_element_type(idxs, val)
823 self.set_default_element_value_type(idxs, :bool)
824 self.set_bool_element(idxs, val)
825 end
826
827 def variable
828 # keeps a Tcl's variable name
829 TkVarAccess.new(self._value)
830 end
831 def variable_element(*idxs)
832 TkVarAccess.new(_element_value(*idxs))
833 end
834 def set_variable(var)
835 var = var.id if var.kind_of?(TkVariable)
836 self.value = var
837 self
838 end
839 alias variable= set_variable
840 def set_variable_element(idxs, var)
841 var = var.id if var.kind_of?(TkVariable)
842 if idxs.kind_of?(Array)
843 self[*idxs]=var
844 else
845 self[idxs]=var
846 end
847 self
848 end
849 def set_variable_type(var)
850 @type = :variable
851 var = var.id if var.kind_of?(TkVariable)
852 self.value = var
853 self
854 end
855 alias variable_type= set_variable_type
856 def set_variable_element_type(idxs, var)
857 self.set_default_element_value_type(idxs, :variable)
858 self.set_variable_element(idxs, var)
859 end
860
861 def window
862 TkComm.window(self._value)
863 end
864 def window_element(*idxs)
865 TkComm.window(_element_value(*idxs))
866 end
867 def set_window(win)
868 win = win._value if win.kind_of?(TkVariable)
869 self.value = win
870 self
871 end
872 alias window= set_window
873 def set_window_element(idxs, win)
874 win = win._value if win.kind_of?(TkVariable)
875 if idxs.kind_of?(Array)
876 self[*idxs]=win
877 else
878 self[idxs]=win
879 end
880 self
881 end
882 def set_window_type(win)
883 @type = :window
884 self.window=(win)
885 self
886 end
887 alias window_type= set_window_type
888 def set_window_element_type(idxs, win)
889 self.set_default_element_value_type(idxs, :window)
890 self.set_window_element(idxs, win)
891 end
892
893 def procedure
894 TkComm.procedure(self._value)
895 end
896 def procedure_element(*idxs)
897 TkComm.procedure(_element_value(*idxs))
898 end
899 def set_procedure(cmd)
900 self.value = cmd
901 self
902 end
903 alias procedure= set_procedure
904 def set_procedure_element(idxs, cmd)
905 cmd = cmd._value if cmd.kind_of?(TkVariable)
906 if idxs.kind_of?(Array)
907 self[*idxs]=cmd
908 else
909 self[idxs]=cmd
910 end
911 self
912 end
913 def set_procedure_type(cmd)
914 @type = :procedure
915 self.procedure=(cmd)
916 self
917 end
918 alias procedure_type= set_procedure_type
919 def set_procedure_element_type(idxs, cmd)
920 self.set_default_element_value_type(idxs, :procedure)
921 self.set_proceure_element(idxs, cmd)
922 end
923
924 def to_i
925 number(_value).to_i
926 end
927 def element_to_i(*idxs)
928 number(_element_value(*idxs)).to_i
929 end
930
931 def to_f
932 number(_value).to_f
933 end
934 def element_to_f(*idxs)
935 number(_element_value(*idxs)).to_f
936 end
937
938 def to_s
939 #string(value).to_s
940 _value
941 end
942 alias string to_s
943 def element_to_s(*idxs)
944 _element_value(*idxs)
945 end
946 def string_element(*idxs)
947 _element_value(*idxs)
948 end
949 def set_string(val)
950 val = val._value if val.kind_of?(TkVariable)
951 self.value=val
952 self
953 end
954 alias string= set_string
955 def set_string_element(idxs, val)
956 val = val._value if val.kind_of?(TkVariable)
957 if idxs.kind_of?(Array)
958 self[*idxs]=val
959 else
960 self[idxs]=val
961 end
962 self
963 end
964 def set_string_type(val)
965 @type = :string
966 self.string=(val)
967 self
968 end
969 alias string_type= set_string_type
970 def set_string_element_type(idxs, val)
971 self.set_default_element_value_type(idxs, :string)
972 self.set_string_element(idxs, val)
973 end
974
975 def to_sym
976 _value.intern
977 end
978 alias symbol to_sym
979 def element_to_sym(*idxs)
980 _element_value(*idxs).intern
981 end
982 alias symbol_element element_to_sym
983 def set_symbol(val)
984 val = val._value if val.kind_of?(TkVariable)
985 self.value=val
986 self
987 end
988 alias symbol= set_symbol
989 def set_symbol_element(idxs, val)
990 val = val._value if val.kind_of?(TkVariable)
991 if idxs.kind_of?(Array)
992 self[*idxs]=val
993 else
994 self[idxs]=val
995 end
996 self
997 end
998 def set_symbol_type(val)
999 @type = :symbol
1000 self.value=(val)
1001 self
1002 end
1003 alias symbol_type= set_symbol_type
1004 def set_symbol_element_type(idxs, val)
1005 self.set_default_element_value_type(idxs, :symbol)
1006 self.set_symbol_element(idxs, val)
1007 end
1008
1009 def list
1010 #tk_split_list(value)
1011 tk_split_simplelist(_value)
1012 end
1013 alias to_a list
1014 def list_element(*idxs)
1015 tk_split_simplelist(_element_value(*idxs))
1016 end
1017 alias element_to_a list_element
1018
1019 def numlist
1020 list.collect!{|val| number(val)}
1021 end
1022 def numlist_element(*idxs)
1023 list_element(*idxs).collect!{|val| number(val)}
1024 end
1025
1026 def set_list(val)
1027 case val
1028 when Array
1029 self.value=(val)
1030 when TkVariable
1031 self.value=(val.list)
1032 else
1033 raise ArgumentError, "Array is expected"
1034 end
1035 self
1036 end
1037 alias list= set_list
1038
1039 alias set_numlist set_list
1040 alias numlist= set_numlist
1041
1042 def set_list_element(idxs, val)
1043 case val
1044 when Array
1045 val
1046 when TkVariable
1047 val = val.list
1048 else
1049 raise ArgumentError, "Array is expected"
1050 end
1051 if idxs.kind_of?(Array)
1052 self[*idxs]=val
1053 else
1054 self[idxs]=val
1055 end
1056 self
1057 end
1058 alias set_numlist_element set_list_element
1059
1060 def set_list_type(val)
1061 @type = :list
1062 self.list=(val)
1063 self
1064 end
1065 alias list_type= set_list_type
1066 def set_list_element_type(idxs, val)
1067 self.set_default_element_value_type(idxs, :list)
1068 self.set_list_element(idxs, val)
1069 end
1070 def set_numlist_type(val)
1071 @type = :numlist
1072 self.numlist=(val)
1073 self
1074 end
1075 alias numlist_type= set_numlist_type
1076 def set_numlist_element_type(idxs, val)
1077 self.set_default_element_value_type(idxs, :numlist)
1078 self.set_numlist_element(idxs, val)
1079 end
1080
1081 def lappend(*elems)
1082 tk_call('lappend', @id, *elems)
1083 self
1084 end
1085 def element_lappend(idxs, *elems)
1086 if idxs.kind_of?(Array)
1087 idxs = idxs.collect{|idx| _get_eval_string(idx, true)}.join(',')
1088 end
1089 tk_call('lappend', "#{@id}(#{idxs})", *elems)
1090 self
1091 end
1092
1093 def lindex(idx)
1094 tk_call('lindex', self._value, idx)
1095 end
1096 alias lget lindex
1097 def element_lindex(elem_idxs, idx)
1098 if elem_idxs.kind_of?(Array)
1099 val = _element_value(*elem_idxs)
1100 else
1101 val = _element_value(elem_idxs)
1102 end
1103 tk_call('lindex', val, idx)
1104 end
1105 alias element_lget element_lindex
1106
1107 def lget_i(idx)
1108 number(lget(idx)).to_i
1109 end
1110 def element_lget_i(elem_idxs, idx)
1111 number(element_lget(elem_idxs, idx)).to_i
1112 end
1113
1114 def lget_f(idx)
1115 number(lget(idx)).to_f
1116 end
1117 def element_lget_f(elem_idxs, idx)
1118 number(element_lget(elem_idxs, idx)).to_f
1119 end
1120
1121 def lset(idx, val)
1122 tk_call('lset', @id, idx, val)
1123 self
1124 end
1125 def element_lset(elem_idxs, idx, val)
1126 if elem_idxs.kind_of?(Array)
1127 idxs = elem_idxs.collect{|i| _get_eval_string(i, true)}.join(',')
1128 end
1129 tk_call('lset', "#{@id}(#{idxs})", idx, val)
1130 self
1131 end
1132
1133 def inspect
1134 #Kernel.format "#<TkVariable: %s>", @id
1135 '#<TkVariable: ' + @id + '>'
1136 end
1137
1138 def coerce(other)
1139 case other
1140 when TkVariable
1141 [other._value, self._value]
1142 when String
1143 [other, self.to_s]
1144 when Symbol
1145 [other, self.to_sym]
1146 when Integer
1147 [other, self.to_i]
1148 when Float
1149 [other, self.to_f]
1150 when Array
1151 [other, self.to_a]
1152 else
1153 [other, self._value]
1154 end
1155 end
1156
1157 def &(other)
1158 if other.kind_of?(Array)
1159 self.to_a & other.to_a
1160 else
1161 self.to_i & other.to_i
1162 end
1163 end
1164 def |(other)
1165 if other.kind_of?(Array)
1166 self.to_a | other.to_a
1167 else
1168 self.to_i | other.to_i
1169 end
1170 end
1171 def +(other)
1172 case other
1173 when Array
1174 self.to_a + other
1175 when String
1176 self._value + other
1177 else
1178 begin
1179 number(self._value) + other
1180 rescue
1181 self._value + other.to_s
1182 end
1183 end
1184 end
1185 def -(other)
1186 if other.kind_of?(Array)
1187 self.to_a - other
1188 else
1189 number(self._value) - other
1190 end
1191 end
1192 def *(other)
1193 num_or_str(self._value) * other.to_i
1194 #begin
1195 # number(self._value) * other
1196 #rescue
1197 # self._value * other
1198 #end
1199 end
1200 def /(other)
1201 number(self._value) / other
1202 end
1203 def %(other)
1204 num_or_str(self._value) % other.to_i
1205 #begin
1206 # number(self._value) % other
1207 #rescue
1208 # self._value % other
1209 #end
1210 end
1211 def **(other)
1212 number(self._value) ** other
1213 end
1214 def =~(other)
1215 self._value =~ other
1216 end
1217
1218 def ==(other)
1219 case other
1220 when TkVariable
1221 #self.equal?(other)
1222 self._value == other._value
1223 when String
1224 self.to_s == other
1225 when Symbol
1226 self.to_sym == other
1227 when Integer
1228 self.to_i == other
1229 when Float
1230 self.to_f == other
1231 when Array
1232 self.to_a == other
1233 when Hash
1234 # false if self is not an assoc array
1235 self._value == other
1236 else
1237 # false
1238 self._value == _get_eval_string(other)
1239 end
1240 end
1241
1242 def zero?
1243 numeric.zero?
1244 end
1245 def nonzero?
1246 !(numeric.zero?)
1247 end
1248
1249 def <=>(other)
1250 if other.kind_of?(TkVariable)
1251 begin
1252 val = other.numeric
1253 other = val
1254 rescue
1255 other = other._value
1256 end
1257 elsif other.kind_of?(Numeric)
1258 begin
1259 return self.numeric <=> other
1260 rescue
1261 return self._value <=> other.to_s
1262 end
1263 elsif other.kind_of?(Array)
1264 return self.list <=> other
1265 else
1266 return self._value <=> other
1267 end
1268 end
1269
1270 def to_eval
1271 @id
1272 end
1273
1274 def trace_callback(elem, op)
1275 if @trace_var.kind_of? Array
1276 @trace_var.each{|m,e| e.call(self,elem,op) if m.index(op)}
1277 end
1278 if elem.kind_of?(String) && elem != ''
1279 if @trace_elem.kind_of?(Hash) && @trace_elem[elem].kind_of?(Array)
1280 @trace_elem[elem].each{|m,e| e.call(self,elem,op) if m.index(op)}
1281 end
1282 end
1283 end
1284
1285 def _check_trace_opt(opts)
1286 if opts.kind_of?(Array)
1287 opt_str = opts.map{|s| s.to_s}.join(' ')
1288 else
1289 opt_str = opts.to_s
1290 end
1291
1292 fail ArgumentError, 'null trace option' if opt_str.empty?
1293
1294 if opt_str =~ /[^arwu\s]/
1295 # new format (Tcl/Tk8.4+?)
1296 if opts.kind_of?(Array)
1297 opt_ary = opts.map{|opt| opt.to_s.strip}
1298 else
1299 opt_ary = opt_str.split(/\s+|\|/)
1300 opt_ary.delete('')
1301 end
1302 if USE_OLD_TRACE_OPTION_STYLE
1303 opt_ary.uniq.map{|opt|
1304 case opt
1305 when 'array'
1306 'a'
1307 when 'read'
1308 'r'
1309 when 'write'
1310 'w'
1311 when 'unset'
1312 'u'
1313 else
1314 fail ArgumentError, "unsupported trace option '#{opt}' on Tcl/Tk#{Tk::TCL_PATCHLEVEL}"
1315 end
1316 }.join
1317 else
1318 opt_ary
1319 end
1320 else
1321 # old format
1322 opt_ary = opt_str.delete('^arwu').split(//).uniq
1323 if USE_OLD_TRACE_OPTION_STYLE
1324 opt_ary.join
1325 else
1326 opt_ary.map{|c|
1327 case c
1328 when 'a'
1329 'array'
1330 when 'r'
1331 'read'
1332 when 'w'
1333 'write'
1334 when 'u'
1335 'unset'
1336 end
1337 }
1338 end
1339 end
1340 end
1341 private :_check_trace_opt
1342
1343 def trace(opts, cmd = Proc.new)
1344 opts = _check_trace_opt(opts)
1345 (@trace_var ||= []).unshift([opts,cmd])
1346
1347 if @trace_opts == nil
1348 TkVar_CB_TBL[@id] = self
1349 @trace_opts = opts
1350 if USE_OLD_TRACE_OPTION_STYLE
1351 Tk.tk_call_without_enc('trace', 'variable',
1352 @id, @trace_opts, 'rb_var ' << @id)
1353 else
1354 Tk.tk_call_without_enc('trace', 'add', 'variable',
1355 @id, @trace_opts, 'rb_var ' << @id)
1356 end
1357 else
1358 newopts = @trace_opts.dup
1359 if USE_OLD_TRACE_OPTION_STYLE
1360 opts.each_byte{|c| newopts.concat(c.chr) unless newopts.index(c.chr)}
1361 if newopts != @trace_opts
1362 Tk.tk_call_without_enc('trace', 'vdelete',
1363 @id, @trace_opts, 'rb_var ' << @id)
1364 @trace_opts.replace(newopts)
1365 Tk.tk_call_without_enc('trace', 'variable',
1366 @id, @trace_opts, 'rb_var ' << @id)
1367 end
1368 else
1369 newopts |= opts
1370 unless (newopts - @trace_opts).empty?
1371 Tk.tk_call_without_enc('trace', 'remove', 'variable',
1372 @id, @trace_opts, 'rb_var ' << @id)
1373 @trace_opts.replace(newopts)
1374 Tk.tk_call_without_enc('trace', 'add', 'variable',
1375 @id, @trace_opts, 'rb_var ' << @id)
1376 end
1377 end
1378 end
1379
1380 self
1381 end
1382
1383 def trace_element(elem, opts, cmd = Proc.new)
1384 if @elem
1385 fail(RuntimeError,
1386 "invalid for a TkVariable which denotes an element of Tcl's array")
1387 end
1388
1389 opts = _check_trace_opt(opts)
1390
1391 ((@trace_elem ||= {})[elem] ||= []).unshift([opts,cmd])
1392
1393 if @trace_opts == nil
1394 TkVar_CB_TBL[@id] = self
1395 @trace_opts = opts
1396 if USE_OLD_TRACE_OPTION_STYLE
1397 Tk.tk_call_without_enc('trace', 'add', 'variable',
1398 @id, @trace_opts, 'rb_var ' << @id)
1399 else
1400 Tk.tk_call_without_enc('trace', 'variable',
1401 @id, @trace_opts, 'rb_var ' << @id)
1402 end
1403 else
1404 newopts = @trace_opts.dup
1405 if USE_OLD_TRACE_OPTION_STYLE
1406 opts.each_byte{|c| newopts.concat(c.chr) unless newopts.index(c.chr)}
1407 if newopts != @trace_opts
1408 Tk.tk_call_without_enc('trace', 'vdelete',
1409 @id, @trace_opts, 'rb_var ' << @id)
1410 @trace_opts.replace(newopts)
1411 Tk.tk_call_without_enc('trace', 'variable',
1412 @id, @trace_opts, 'rb_var ' << @id)
1413 end
1414 else
1415 newopts |= opts
1416 unless (newopts - @trace_opts).empty?
1417 Tk.tk_call_without_enc('trace', 'remove', 'variable',
1418 @id, @trace_opts, 'rb_var ' << @id)
1419 @trace_opts.replace(newopts)
1420 Tk.tk_call_without_enc('trace', 'add', 'variable',
1421 @id, @trace_opts, 'rb_var ' << @id)
1422 end
1423 end
1424 end
1425
1426 self
1427 end
1428
1429 def trace_info
1430 return [] unless @trace_var
1431 @trace_var.dup
1432 end
1433 alias trace_vinfo trace_info
1434
1435 def trace_info_for_element(elem)
1436 if @elem
1437 fail(RuntimeError,
1438 "invalid for a TkVariable which denotes an element of Tcl's array")
1439 end
1440 return [] unless @trace_elem
1441 return [] unless @trace_elem[elem]
1442 @trace_elem[elem].dup
1443 end
1444 alias trace_vinfo_for_element trace_info_for_element
1445
1446 def trace_remove(opts,cmd)
1447 return self unless @trace_var.kind_of? Array
1448
1449 opts = _check_trace_opt(opts)
1450
1451 idx = -1
1452 if USE_OLD_TRACE_OPTION_STYLE
1453 newopts = ''
1454 @trace_var.each_with_index{|e, i|
1455 if idx < 0 && e[1] == cmd
1456 diff = false
1457 ['a', 'r', 'w', 'u'].each{|c|
1458 break if (diff = e[0].index(c) ^ opts.index(c))
1459 }
1460 unless diff
1461 #find
1462 idx = i
1463 next
1464 end
1465 end
1466 e[0].each_byte{|c| newopts.concat(c.chr) unless newopts.index(c.chr)}
1467 }
1468 else
1469 newopts = []
1470 @trace_var.each_with_index{|e, i|
1471 if idx < 0 && e[1] == cmd &&
1472 e[0].size == opts.size && (e[0] - opts).empty?
1473 # find
1474 idx = i
1475 next
1476 end
1477 newopts |= e[0]
1478 }
1479 end
1480
1481 if idx >= 0
1482 @trace_var.delete_at(idx)
1483 else
1484 return self
1485 end
1486
1487 (@trace_elem ||= {}).each{|elem|
1488 @trace_elem[elem].each{|e|
1489 if USE_OLD_TRACE_OPTION_STYLE
1490 e[0].each_byte{|c| newopts.concat(c.chr) unless newopts.index(c.chr)}
1491 else
1492 newopts |= e[0]
1493 end
1494 }
1495 }
1496
1497 if USE_OLD_TRACE_OPTION_STYLE
1498 diff = false
1499 @trace_opts.each_byte{|c| break if (diff = ! newopts.index(c))}
1500 if diff
1501 Tk.tk_call_without_enc('trace', 'vdelete',
1502 @id, @trace_opts, 'rb_var ' << @id)
1503 @trace_opts.replace(newopts)
1504 unless @trace_opts.empty?
1505 Tk.tk_call_without_enc('trace', 'variable',
1506 @id, @trace_opts, 'rb_var ' << @id)
1507 end
1508 end
1509 else
1510 unless (@trace_opts - newopts).empty?
1511 Tk.tk_call_without_enc('trace', 'remove', 'variable',
1512 @id, @trace_opts, 'rb_var ' << @id)
1513 @trace_opts.replace(newopts)
1514 unless @trace_opts.empty?
1515 Tk.tk_call_without_enc('trace', 'add', 'variable',
1516 @id, @trace_opts, 'rb_var ' << @id)
1517 end
1518 end
1519 end
1520
1521 self
1522 end
1523 alias trace_delete trace_remove
1524 alias trace_vdelete trace_remove
1525
1526 def trace_remove_for_element(elem,opts,cmd)
1527 if @elem
1528 fail(RuntimeError,
1529 "invalid for a TkVariable which denotes an element of Tcl's array")
1530 end
1531 return self unless @trace_elem.kind_of? Hash
1532 return self unless @trace_elem[elem].kind_of? Array
1533
1534 opts = _check_trace_opt(opts)
1535
1536 idx = -1
1537 if USE_OLD_TRACE_OPTION_STYLE
1538 @trace_elem[elem].each_with_index{|e, i|
1539 if idx < 0 && e[1] == cmd
1540 diff = false
1541 ['a', 'r', 'w', 'u'].each{|c|
1542 break if (diff = e[0].index(c) ^ opts.index(c))
1543 }
1544 unless diff
1545 #find
1546 idx = i
1547 next
1548 end
1549 end
1550 }
1551 else
1552 @trace_elem[elem].each_with_index{|e, i|
1553 if idx < 0 && e[1] == cmd &&
1554 e[0].size == opts.size && (e[0] - opts).empty?
1555 # find
1556 idx = i
1557 next
1558 end
1559 }
1560 end
1561
1562 if idx >= 0
1563 @trace_elem[elem].delete_at(idx)
1564 else
1565 return self
1566 end
1567
1568 if USE_OLD_TRACE_OPTION_STYLE
1569 newopts = ''
1570 @trace_var.each{|e|
1571 e[0].each_byte{|c| newopts.concat(c.chr) unless newopts.index(c.chr)}
1572 }
1573 @trace_elem.each{|elem|
1574 @trace_elem[elem].each{|e|
1575 e[0].each_byte{|c| newopts.concat(c.chr) unless newopts.index(c.chr)}
1576 }
1577 }
1578 else
1579 newopts = []
1580 @trace_var.each{|e|
1581 newopts |= e[0]
1582 }
1583 @trace_elem.each{|elem|
1584 @trace_elem[elem].each{|e|
1585 e[0].each_byte{|c| newopts.concat(c.chr) unless newopts.index(c.chr)}
1586 }
1587 }
1588 end
1589
1590 if USE_OLD_TRACE_OPTION_STYLE
1591 diff = false
1592 @trace_opts.each_byte{|c| break if (diff = ! newopts.index(c))}
1593 if diff
1594 Tk.tk_call_without_enc('trace', 'vdelete',
1595 @id, @trace_opts, 'rb_var ' << @id)
1596 @trace_opts.replace(newopts)
1597 unless @trace_opts.empty?
1598 Tk.tk_call_without_enc('trace', 'variable',
1599 @id, @trace_opts, 'rb_var ' << @id)
1600 end
1601 end
1602 else
1603 unless (@trace_opts - newopts).empty?
1604 Tk.tk_call_without_enc('trace', 'remove', 'variable',
1605 @id, @trace_opts, 'rb_var ' << @id)
1606 @trace_opts.replace(newopts)
1607 unless @trace_opts.empty?
1608 Tk.tk_call_without_enc('trace', 'add', 'variable',
1609 @id, @trace_opts, 'rb_var ' << @id)
1610 end
1611 end
1612 end
1613
1614 self
1615 end
1616 alias trace_delete_for_element trace_remove_for_element
1617 alias trace_vdelete_for_element trace_remove_for_element
1618 end
1619
1620 class TkVarAccess<TkVariable
1621 def self.new(name, *args)
1622 if name.kind_of?(TkVariable)
1623 name.value = args[0] unless args.empty?
1624 return name
1625 end
1626
1627 name = name.to_s
1628 v = nil
1629 TkVar_ID_TBL.mutex.synchronize{
1630 if v = TkVar_ID_TBL[name]
1631 v.value = args[0] unless args.empty?
1632 return v
1633 else
1634 (v = self.allocate).instance_eval{
1635 @id = name
1636 TkVar_ID_TBL[@id] = self
1637 @var = @id
1638 }
1639 end
1640 }
1641
1642 v.instance_eval{ initialize(name, *args) }
1643 v
1644 end
1645
1646 def self.new_hash(name, *args)
1647 if name.kind_of?(TkVariable)
1648 unless name.is_hash?
1649 fail ArgumentError, "already exist as a scalar variable"
1650 end
1651 name.value = args[0] unless args.empty?
1652 return name
1653 end
1654
1655 name = name.to_s
1656 v = nil
1657 TkVar_ID_TBL.mutex.synchronize{
1658 if v = TkVar_ID_TBL[name]
1659 unless v.is_hash?
1660 fail ArgumentError, "already exist as a scalar variable"
1661 end
1662 v.value = args[0] unless args.empty?
1663 return v
1664 else
1665 (v = self.allocate).instance_eval{
1666 @id = name
1667 TkVar_ID_TBL[@id] = self
1668 @var = @id
1669 }
1670 end
1671 }
1672
1673 INTERP._invoke_without_enc('global', name)
1674 if args.empty? && INTERP._invoke_without_enc('array', 'exist', name) == '0'
1675 v.instance_eval{ initialize(name, {}) } # force creating
1676 else
1677 v.instance_eval{ initialize(name, *args) }
1678 end
1679 v
1680 end
1681
1682 def initialize(varname, val=nil)
1683 # @id = varname
1684 # TkVar_ID_TBL[@id] = self
1685
1686 # @var = @id
1687 @elem = nil
1688
1689 @def_default = false
1690 @default_val = nil
1691
1692 @trace_var = nil
1693 @trace_elem = nil
1694 @trace_opts = nil
1695
1696 @type = nil
1697 var = self
1698 @element_type = Hash.new{|k,v| var.default_value_type }
1699
1700 # is an element?
1701 if @id =~ /^([^(]+)\((.+)\)$/
1702 # is an element --> var == $1, elem == $2
1703 @var = $1
1704 @elem = $2
1705 end
1706
1707 # teach Tk-ip that @id is global var
1708 INTERP._invoke_without_enc('global', @var)
1709 =begin
1710 begin
1711 INTERP._invoke_without_enc('global', @id)
1712 rescue => e
1713 if @id =~ /^(.+)\([^()]+\)$/
1714 # is an element --> varname == $1
1715 INTERP._invoke_without_enc('global', $1)
1716 else
1717 fail e
1718 end
1719 end
1720 =end
1721
1722 if val
1723 if val.kind_of?(Hash)
1724 # assoc-array variable
1725 self[''] = 0
1726 self.clear
1727 end
1728 #s = '"' + _get_eval_string(val).gsub(/[\[\]$"]/, '\\\\\&') + '"' #"
1729 #s = '"' + _get_eval_string(val).gsub(/[\[\]$"\\]/, '\\\\\&') + '"' #"
1730 #INTERP._eval(Kernel.format('global %s; set %s %s', @id, @id, s))
1731 #INTERP._set_global_var(@id, _toUTF8(_get_eval_string(val)))
1732 self.value = val
1733 end
1734 end
1735 end
1736
1737 module Tk
1738 begin
1739 INTERP._invoke_without_enc('global', 'auto_path')
1740 auto_path = INTERP._invoke('set', 'auto_path')
1741 rescue => e
1742 begin
1743 INTERP._invoke_without_enc('global', 'env')
1744 auto_path = INTERP._invoke('set', 'env(TCLLIBPATH)')
1745 rescue => e
1746 auto_path = Tk::LIBRARY
1747 end
1748 end
1749
1750 AUTO_PATH = TkVarAccess.new('auto_path', auto_path)
1751
1752 =begin
1753 AUTO_OLDPATH = tk_split_simplelist(INTERP._invoke('set', 'auto_oldpath'))
1754 AUTO_OLDPATH.each{|s| s.freeze}
1755 AUTO_OLDPATH.freeze
1756 =end
1757
1758 TCL_PACKAGE_PATH = TkVarAccess.new('tcl_pkgPath')
1759 PACKAGE_PATH = TCL_PACKAGE_PATH
1760
1761 TCL_LIBRARY_PATH = TkVarAccess.new('tcl_libPath')
1762 LIBRARY_PATH = TCL_LIBRARY_PATH
1763
1764 TCL_PRECISION = TkVarAccess.new('tcl_precision')
1765 end