1#
2# tk/variable.rb : treat Tk variable object
3#
4require 'tk'
5
6class 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, TkUtil.untrust("00000")]).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, 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
461unless const_defined?(:USE_TCLs_SET_VARIABLE_FUNCTIONS)
462  USE_TCLs_SET_VARIABLE_FUNCTIONS = true
463end
464
465if 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
554else
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
680end
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 to_hash
698    hash = {}
699    self.keys.each{|k|
700      hash[k] = self[k]
701    }
702    hash
703  end
704
705  def set_element_value(idxs, val)
706    if idxs.kind_of?(Array)
707      self[*idxs]=val
708    else
709      self[idxs]=val
710    end
711    self
712  end
713
714  def set_value_type(val)
715    self.default_value_type = val.class
716    self.value = val
717    self
718  end
719
720  alias value_type= set_value_type
721
722  def set_element_value_type(idxs, val)
723    self.set_default_element_value_type(idxs, val.class)
724    if idxs.kind_of?(Array)
725      self[*idxs]=val
726    else
727      self[idxs]=val
728    end
729    self
730  end
731
732  def numeric
733    number(_value)
734  end
735  def numeric_element(*idxs)
736    number(_element_value(*idxs))
737  end
738  def set_numeric(val)
739    case val
740    when Numeric
741      self.value=(val)
742    when TkVariable
743      self.value=(val.numeric)
744    else
745      raise ArgumentError, "Numeric is expected"
746    end
747    self
748  end
749  alias numeric= set_numeric
750  def set_numeric_element(idxs, val)
751    case val
752    when Numeric
753      val
754    when TkVariable
755      val = val.numeric
756    else
757      raise ArgumentError, "Numeric is expected"
758    end
759    if idxs.kind_of?(Array)
760      self[*idxs]=val
761    else
762      self[idxs]=val
763    end
764    self
765  end
766  def set_numeric_type(val)
767    @type = :numeric
768    self.numeric=(val)
769    self
770  end
771  alias numeric_type= set_numeric_type
772  def set_numeric_element_type(idxs, val)
773    self.set_default_element_value_type(idxs, :numeric)
774    self.set_numeric_element(idxs, val)
775  end
776
777  def bool
778    TkComm.bool(_value)
779=begin
780    # see Tcl_GetBoolean man-page
781    case _value.downcase
782    when '0', 'false', 'no', 'off'
783      false
784    else
785      true
786    end
787=end
788  end
789  def bool_element(*idxs)
790    TkComm.bool(_element_value(*idxs))
791  end
792  def set_bool(val)
793    if ! val
794      self.value = '0'
795    else
796      case val.to_s.downcase
797      when 'false', '0', 'no', 'off'
798        self.value = '0'
799      else
800        self.value = '1'
801      end
802    end
803    self
804  end
805  alias bool= set_bool
806  def set_bool_element(idxs, val)
807    if ! val
808      val = '0'
809    else
810      case val.to_s.downcase
811      when 'false', '0', 'no', 'off'
812        val = '0'
813      else
814        val = '1'
815      end
816    end
817    if idxs.kind_of?(Array)
818      self[*idxs]=val
819    else
820      self[idxs]=val
821    end
822    self
823  end
824  def set_bool_type(val)
825    @type = :bool
826    self.bool=(val)
827    self
828  end
829  alias bool_type= set_bool_type
830  def set_bool_element_type(idxs, val)
831    self.set_default_element_value_type(idxs, :bool)
832    self.set_bool_element(idxs, val)
833  end
834
835  def variable
836    # keeps a Tcl's variable name
837    TkVarAccess.new(self._value)
838  end
839  def variable_element(*idxs)
840    TkVarAccess.new(_element_value(*idxs))
841  end
842  def set_variable(var)
843    var = var.id if var.kind_of?(TkVariable)
844    self.value = var
845    self
846  end
847  alias variable= set_variable
848  def set_variable_element(idxs, var)
849    var = var.id if var.kind_of?(TkVariable)
850    if idxs.kind_of?(Array)
851      self[*idxs]=var
852    else
853      self[idxs]=var
854    end
855    self
856  end
857  def set_variable_type(var)
858    @type = :variable
859    var = var.id if var.kind_of?(TkVariable)
860    self.value = var
861    self
862  end
863  alias variable_type= set_variable_type
864  def set_variable_element_type(idxs, var)
865    self.set_default_element_value_type(idxs, :variable)
866    self.set_variable_element(idxs, var)
867  end
868
869  def window
870    TkComm.window(self._value)
871  end
872  def window_element(*idxs)
873    TkComm.window(_element_value(*idxs))
874  end
875  def set_window(win)
876    win = win._value if win.kind_of?(TkVariable)
877    self.value = win
878    self
879  end
880  alias window= set_window
881  def set_window_element(idxs, win)
882    win = win._value if win.kind_of?(TkVariable)
883    if idxs.kind_of?(Array)
884      self[*idxs]=win
885    else
886      self[idxs]=win
887    end
888    self
889  end
890  def set_window_type(win)
891    @type = :window
892    self.window=(win)
893    self
894  end
895  alias window_type= set_window_type
896  def set_window_element_type(idxs, win)
897    self.set_default_element_value_type(idxs, :window)
898    self.set_window_element(idxs, win)
899  end
900
901  def procedure
902    TkComm.procedure(self._value)
903  end
904  def procedure_element(*idxs)
905    TkComm.procedure(_element_value(*idxs))
906  end
907  def set_procedure(cmd)
908    self.value = cmd
909    self
910  end
911  alias procedure= set_procedure
912  def set_procedure_element(idxs, cmd)
913    cmd = cmd._value if cmd.kind_of?(TkVariable)
914    if idxs.kind_of?(Array)
915      self[*idxs]=cmd
916    else
917      self[idxs]=cmd
918    end
919    self
920  end
921  def set_procedure_type(cmd)
922    @type = :procedure
923    self.procedure=(cmd)
924    self
925  end
926  alias procedure_type= set_procedure_type
927  def set_procedure_element_type(idxs, cmd)
928    self.set_default_element_value_type(idxs, :procedure)
929    self.set_proceure_element(idxs, cmd)
930  end
931
932  def to_proc
933    cmd = self.procedure
934    if cmd.respond_to?(:call)
935      cmd
936    else
937      # cmd is a String
938      cmd.to_sym.to_proc
939    end
940  end
941
942  def to_i
943    number(_value).to_i
944  end
945  alias to_int to_i
946  def element_to_i(*idxs)
947    number(_element_value(*idxs)).to_i
948  end
949
950  def to_f
951    number(_value).to_f
952  end
953  def element_to_f(*idxs)
954    number(_element_value(*idxs)).to_f
955  end
956
957  def to_s
958    #string(value).to_s
959    _value
960  end
961  alias string to_s
962  alias to_str to_s
963  def element_to_s(*idxs)
964    _element_value(*idxs)
965  end
966  def string_element(*idxs)
967    _element_value(*idxs)
968  end
969  def set_string(val)
970    val = val._value if val.kind_of?(TkVariable)
971    self.value=val
972    self
973  end
974  alias string= set_string
975  def set_string_element(idxs, val)
976    val = val._value if val.kind_of?(TkVariable)
977    if idxs.kind_of?(Array)
978      self[*idxs]=val
979    else
980      self[idxs]=val
981    end
982    self
983  end
984  def set_string_type(val)
985    @type = :string
986    self.string=(val)
987    self
988  end
989  alias string_type= set_string_type
990  def set_string_element_type(idxs, val)
991    self.set_default_element_value_type(idxs, :string)
992    self.set_string_element(idxs, val)
993  end
994
995  def to_sym
996    _value.intern
997  end
998  alias symbol to_sym
999  def element_to_sym(*idxs)
1000    _element_value(*idxs).intern
1001  end
1002  alias symbol_element element_to_sym
1003  def set_symbol(val)
1004    val = val._value if val.kind_of?(TkVariable)
1005    self.value=val
1006    self
1007  end
1008  alias symbol= set_symbol
1009  def set_symbol_element(idxs, val)
1010    val = val._value if val.kind_of?(TkVariable)
1011    if idxs.kind_of?(Array)
1012      self[*idxs]=val
1013    else
1014      self[idxs]=val
1015    end
1016    self
1017  end
1018  def set_symbol_type(val)
1019    @type = :symbol
1020    self.value=(val)
1021    self
1022  end
1023  alias symbol_type= set_symbol_type
1024  def set_symbol_element_type(idxs, val)
1025    self.set_default_element_value_type(idxs, :symbol)
1026    self.set_symbol_element(idxs, val)
1027  end
1028
1029  def list
1030    #tk_split_list(value)
1031    tk_split_simplelist(_value)
1032  end
1033  alias to_a list
1034  alias to_ary list
1035  def list_element(*idxs)
1036    tk_split_simplelist(_element_value(*idxs))
1037  end
1038  alias element_to_a list_element
1039
1040  def numlist
1041    list.collect!{|val| number(val)}
1042  end
1043  def numlist_element(*idxs)
1044    list_element(*idxs).collect!{|val| number(val)}
1045  end
1046
1047  def set_list(val)
1048    case val
1049    when Array
1050      self.value=(val)
1051    when TkVariable
1052      self.value=(val.list)
1053    else
1054      raise ArgumentError, "Array is expected"
1055    end
1056    self
1057  end
1058  alias list= set_list
1059
1060  alias set_numlist set_list
1061  alias numlist= set_numlist
1062
1063  def set_list_element(idxs, val)
1064    case val
1065    when Array
1066      val
1067    when TkVariable
1068      val = val.list
1069    else
1070      raise ArgumentError, "Array is expected"
1071    end
1072    if idxs.kind_of?(Array)
1073      self[*idxs]=val
1074    else
1075      self[idxs]=val
1076    end
1077    self
1078  end
1079  alias set_numlist_element set_list_element
1080
1081  def set_list_type(val)
1082    @type = :list
1083    self.list=(val)
1084    self
1085  end
1086  alias list_type= set_list_type
1087  def set_list_element_type(idxs, val)
1088    self.set_default_element_value_type(idxs, :list)
1089    self.set_list_element(idxs, val)
1090  end
1091  def set_numlist_type(val)
1092    @type = :numlist
1093    self.numlist=(val)
1094    self
1095  end
1096  alias numlist_type= set_numlist_type
1097  def set_numlist_element_type(idxs, val)
1098    self.set_default_element_value_type(idxs, :numlist)
1099    self.set_numlist_element(idxs, val)
1100  end
1101
1102  def lappend(*elems)
1103    tk_call('lappend', @id, *elems)
1104    self
1105  end
1106  def element_lappend(idxs, *elems)
1107    if idxs.kind_of?(Array)
1108      idxs = idxs.collect{|idx| _get_eval_string(idx, true)}.join(',')
1109    end
1110    tk_call('lappend', "#{@id}(#{idxs})", *elems)
1111    self
1112  end
1113
1114  def lindex(idx)
1115    tk_call('lindex', self._value, idx)
1116  end
1117  alias lget lindex
1118  def element_lindex(elem_idxs, idx)
1119    if elem_idxs.kind_of?(Array)
1120      val = _element_value(*elem_idxs)
1121    else
1122      val = _element_value(elem_idxs)
1123    end
1124    tk_call('lindex', val, idx)
1125  end
1126  alias element_lget element_lindex
1127
1128  def lget_i(idx)
1129    number(lget(idx)).to_i
1130  end
1131  def element_lget_i(elem_idxs, idx)
1132    number(element_lget(elem_idxs, idx)).to_i
1133  end
1134
1135  def lget_f(idx)
1136    number(lget(idx)).to_f
1137  end
1138  def element_lget_f(elem_idxs, idx)
1139    number(element_lget(elem_idxs, idx)).to_f
1140  end
1141
1142  def lset(idx, val)
1143    tk_call('lset', @id, idx, val)
1144    self
1145  end
1146  def element_lset(elem_idxs, idx, val)
1147    if elem_idxs.kind_of?(Array)
1148      idxs = elem_idxs.collect{|i| _get_eval_string(i, true)}.join(',')
1149    end
1150    tk_call('lset', "#{@id}(#{idxs})", idx, val)
1151    self
1152  end
1153
1154  def inspect
1155    #Kernel.format "#<TkVariable: %s>", @id
1156    '#<TkVariable: ' + @id + '>'
1157  end
1158
1159  def coerce(other)
1160    case other
1161    when TkVariable
1162      [other._value, self._value]
1163    when String
1164      [other, self.to_s]
1165    when Symbol
1166      [other, self.to_sym]
1167    when Numeric
1168      [other, self.numeric]
1169    when Array
1170      [other, self.to_a]
1171    else
1172      [other, self._value]
1173    end
1174  end
1175
1176  def +@
1177    self.numeric
1178  end
1179  def -@
1180    -(self.numeric)
1181  end
1182
1183  def &(other)
1184    if other.kind_of?(Array)
1185      self.to_a & other.to_a
1186    else
1187      self.to_i & other.to_i
1188    end
1189  end
1190  def |(other)
1191    if other.kind_of?(Array)
1192      self.to_a | other.to_a
1193    else
1194      self.to_i | other.to_i
1195    end
1196  end
1197  def +(other)
1198    case other
1199    when Array
1200      self.to_a + other
1201    when String
1202      self._value + other
1203    else
1204      begin
1205        number(self._value) + other
1206      rescue
1207        self._value + other.to_s
1208      end
1209    end
1210  end
1211  def -(other)
1212    if other.kind_of?(Array)
1213      self.to_a - other
1214    else
1215      number(self._value) - other
1216    end
1217  end
1218  def *(other)
1219    num_or_str(self._value) * other
1220    #begin
1221    #  number(self._value) * other
1222    #rescue
1223    #  self._value * other
1224    #end
1225  end
1226  def /(other)
1227    number(self._value) / other
1228  end
1229  def %(other)
1230    num_or_str(self._value) % other
1231    #begin
1232    #  number(self._value) % other
1233    #rescue
1234    #  self._value % other
1235    #end
1236  end
1237  def **(other)
1238    number(self._value) ** other
1239  end
1240  def =~(other)
1241    self._value =~ other
1242  end
1243
1244  def ==(other)
1245    case other
1246    when TkVariable
1247      #self.equal?(other)
1248      self._value == other._value
1249    when String
1250      self.to_s == other
1251    when Symbol
1252      self.to_sym == other
1253    when Integer
1254      self.to_i == other
1255    when Float
1256      self.to_f == other
1257    when Array
1258      self.to_a == other
1259    when Hash
1260      # false if self is not an assoc array
1261      self._value == other
1262    else
1263      # false
1264      self._value == _get_eval_string(other)
1265    end
1266  end
1267
1268  def ===(other)
1269    if other.kind_of?(TkVariable)
1270      self.id == other.id
1271    else
1272      super
1273    end
1274  end
1275
1276  def zero?
1277    numeric.zero?
1278  end
1279  def nonzero?
1280    !(numeric.zero?)
1281  end
1282
1283  def <=>(other)
1284    if other.kind_of?(TkVariable)
1285      begin
1286        val = other.numeric
1287        other = val
1288      rescue
1289        other = other._value
1290      end
1291    elsif other.kind_of?(Numeric)
1292      begin
1293        return self.numeric <=> other
1294      rescue
1295        return self._value <=> other.to_s
1296      end
1297    elsif other.kind_of?(Array)
1298      return self.list <=> other
1299    else
1300      return self._value <=> other
1301    end
1302  end
1303
1304  def to_eval
1305    @id
1306  end
1307
1308  def trace_callback(elem, op)
1309    if @trace_var.kind_of? Array
1310      @trace_var.each{|m,e| e.call(self,elem,op) if m.index(op)}
1311    end
1312    if elem.kind_of?(String) && elem != ''
1313      if @trace_elem.kind_of?(Hash) && @trace_elem[elem].kind_of?(Array)
1314        @trace_elem[elem].each{|m,e| e.call(self,elem,op) if m.index(op)}
1315      end
1316    end
1317  end
1318
1319  def _check_trace_opt(opts)
1320    if opts.kind_of?(Array)
1321      opt_str = opts.map{|s| s.to_s}.join(' ')
1322    else
1323      opt_str = opts.to_s
1324    end
1325
1326    fail ArgumentError, 'null trace option' if opt_str.empty?
1327
1328    if opt_str =~ /[^arwu\s]/
1329      # new format (Tcl/Tk8.4+?)
1330      if opts.kind_of?(Array)
1331        opt_ary = opts.map{|opt| opt.to_s.strip}
1332      else
1333        opt_ary = opt_str.split(/\s+|\|/)
1334        opt_ary.delete('')
1335      end
1336      if USE_OLD_TRACE_OPTION_STYLE
1337        opt_ary.uniq.map{|opt|
1338          case opt
1339          when 'array'
1340            'a'
1341          when 'read'
1342            'r'
1343          when 'write'
1344            'w'
1345          when 'unset'
1346            'u'
1347          else
1348            fail ArgumentError, "unsupported trace option '#{opt}' on Tcl/Tk#{Tk::TCL_PATCHLEVEL}"
1349          end
1350        }.join
1351      else
1352        opt_ary
1353      end
1354    else
1355      # old format
1356      opt_ary = opt_str.delete('^arwu').split(//).uniq
1357      if USE_OLD_TRACE_OPTION_STYLE
1358        opt_ary.join
1359      else
1360        opt_ary.map{|c|
1361          case c
1362          when 'a'
1363            'array'
1364          when 'r'
1365            'read'
1366          when 'w'
1367            'write'
1368          when 'u'
1369            'unset'
1370          end
1371        }
1372      end
1373    end
1374  end
1375  private :_check_trace_opt
1376
1377  def trace(opts, cmd = Proc.new)
1378    opts = _check_trace_opt(opts)
1379    (@trace_var ||= []).unshift([opts,cmd])
1380
1381    if @trace_opts == nil
1382      TkVar_CB_TBL[@id] = self
1383      @trace_opts = opts.dup
1384      if USE_OLD_TRACE_OPTION_STYLE
1385        Tk.tk_call_without_enc('trace', 'variable',
1386                               @id, @trace_opts, 'rb_var ' << @id)
1387      else
1388        Tk.tk_call_without_enc('trace', 'add', 'variable',
1389                               @id, @trace_opts, 'rb_var ' << @id)
1390      end
1391    else
1392      newopts = @trace_opts.dup
1393      if USE_OLD_TRACE_OPTION_STYLE
1394        opts.each_byte{|c| newopts.concat(c.chr) unless newopts.index(c.chr)}
1395        if newopts != @trace_opts
1396          Tk.tk_call_without_enc('trace', 'vdelete',
1397                                 @id, @trace_opts, 'rb_var ' << @id)
1398          @trace_opts.replace(newopts)
1399          Tk.tk_call_without_enc('trace', 'variable',
1400                                 @id, @trace_opts, 'rb_var ' << @id)
1401        end
1402      else
1403        newopts |= opts
1404        unless (newopts - @trace_opts).empty?
1405          Tk.tk_call_without_enc('trace', 'remove', 'variable',
1406                                 @id, @trace_opts, 'rb_var ' << @id)
1407          @trace_opts.replace(newopts)
1408          Tk.tk_call_without_enc('trace', 'add', 'variable',
1409                                 @id, @trace_opts, 'rb_var ' << @id)
1410        end
1411      end
1412    end
1413
1414    self
1415  end
1416
1417  def trace_element(elem, opts, cmd = Proc.new)
1418    if @elem
1419      fail(RuntimeError,
1420           "invalid for a TkVariable which denotes an element of Tcl's array")
1421    end
1422
1423    opts = _check_trace_opt(opts)
1424
1425    ((@trace_elem ||= {})[elem] ||= []).unshift([opts,cmd])
1426
1427    if @trace_opts == nil
1428      TkVar_CB_TBL[@id] = self
1429      @trace_opts = opts.dup
1430      if USE_OLD_TRACE_OPTION_STYLE
1431        Tk.tk_call_without_enc('trace', 'add', 'variable',
1432                               @id, @trace_opts, 'rb_var ' << @id)
1433      else
1434        Tk.tk_call_without_enc('trace', 'variable',
1435                               @id, @trace_opts, 'rb_var ' << @id)
1436      end
1437    else
1438      newopts = @trace_opts.dup
1439      if USE_OLD_TRACE_OPTION_STYLE
1440        opts.each_byte{|c| newopts.concat(c.chr) unless newopts.index(c.chr)}
1441        if newopts != @trace_opts
1442          Tk.tk_call_without_enc('trace', 'vdelete',
1443                                 @id, @trace_opts, 'rb_var ' << @id)
1444          @trace_opts.replace(newopts)
1445          Tk.tk_call_without_enc('trace', 'variable',
1446                                 @id, @trace_opts, 'rb_var ' << @id)
1447        end
1448      else
1449        newopts |= opts
1450        unless (newopts - @trace_opts).empty?
1451          Tk.tk_call_without_enc('trace', 'remove', 'variable',
1452                                 @id, @trace_opts, 'rb_var ' << @id)
1453          @trace_opts.replace(newopts)
1454          Tk.tk_call_without_enc('trace', 'add', 'variable',
1455                                 @id, @trace_opts, 'rb_var ' << @id)
1456        end
1457      end
1458    end
1459
1460    self
1461  end
1462
1463  def trace_info
1464    return [] unless @trace_var
1465    @trace_var.dup
1466  end
1467  alias trace_vinfo trace_info
1468
1469  def trace_info_for_element(elem)
1470    if @elem
1471      fail(RuntimeError,
1472           "invalid for a TkVariable which denotes an element of Tcl's array")
1473    end
1474    return [] unless @trace_elem
1475    return [] unless @trace_elem[elem]
1476    @trace_elem[elem].dup
1477  end
1478  alias trace_vinfo_for_element trace_info_for_element
1479
1480  def trace_remove(opts,cmd)
1481    return self unless @trace_var.kind_of? Array
1482
1483    opts = _check_trace_opt(opts)
1484
1485    idx = -1
1486    if USE_OLD_TRACE_OPTION_STYLE
1487      newopts = ''
1488      @trace_var.each_with_index{|e, i|
1489        if idx < 0 && e[1] == cmd
1490          diff = false
1491          ['a', 'r', 'w', 'u'].each{|c|
1492            break if (diff = e[0].index(c) ^ opts.index(c))
1493          }
1494          unless diff
1495            #find
1496            idx = i
1497            next
1498          end
1499        end
1500        e[0].each_byte{|c| newopts.concat(c.chr) unless newopts.index(c.chr)}
1501      }
1502    else
1503      newopts = []
1504      @trace_var.each_with_index{|e, i|
1505        if idx < 0 && e[1] == cmd &&
1506            e[0].size == opts.size && (e[0] - opts).empty?
1507          # find
1508          idx = i
1509          next
1510        end
1511        newopts |= e[0]
1512      }
1513    end
1514
1515    if idx >= 0
1516      @trace_var.delete_at(idx)
1517    else
1518      return self
1519    end
1520
1521    (@trace_elem ||= {}).each{|elem|
1522      @trace_elem[elem].each{|e|
1523        if USE_OLD_TRACE_OPTION_STYLE
1524          e[0].each_byte{|c| newopts.concat(c.chr) unless newopts.index(c.chr)}
1525        else
1526          newopts |= e[0]
1527        end
1528      }
1529    }
1530
1531    if USE_OLD_TRACE_OPTION_STYLE
1532      diff = false
1533      @trace_opts.each_byte{|c| break if (diff = ! newopts.index(c))}
1534      if diff
1535        Tk.tk_call_without_enc('trace', 'vdelete',
1536                               @id, @trace_opts, 'rb_var ' << @id)
1537        @trace_opts.replace(newopts)
1538        unless @trace_opts.empty?
1539          Tk.tk_call_without_enc('trace', 'variable',
1540                                 @id, @trace_opts, 'rb_var ' << @id)
1541        end
1542      end
1543    else
1544      unless (@trace_opts - newopts).empty?
1545        Tk.tk_call_without_enc('trace', 'remove', 'variable',
1546                               @id, @trace_opts, 'rb_var ' << @id)
1547        @trace_opts.replace(newopts)
1548        unless @trace_opts.empty?
1549          Tk.tk_call_without_enc('trace', 'add', 'variable',
1550                                 @id, @trace_opts, 'rb_var ' << @id)
1551        end
1552      end
1553    end
1554
1555    self
1556  end
1557  alias trace_delete  trace_remove
1558  alias trace_vdelete trace_remove
1559
1560  def trace_remove_for_element(elem,opts,cmd)
1561    if @elem
1562      fail(RuntimeError,
1563           "invalid for a TkVariable which denotes an element of Tcl's array")
1564    end
1565    return self unless @trace_elem.kind_of? Hash
1566    return self unless @trace_elem[elem].kind_of? Array
1567
1568    opts = _check_trace_opt(opts)
1569
1570    idx = -1
1571    if USE_OLD_TRACE_OPTION_STYLE
1572      @trace_elem[elem].each_with_index{|e, i|
1573        if idx < 0 && e[1] == cmd
1574          diff = false
1575          ['a', 'r', 'w', 'u'].each{|c|
1576            break if (diff = e[0].index(c) ^ opts.index(c))
1577          }
1578          unless diff
1579            #find
1580            idx = i
1581            next
1582          end
1583        end
1584      }
1585    else
1586      @trace_elem[elem].each_with_index{|e, i|
1587        if idx < 0 && e[1] == cmd &&
1588            e[0].size == opts.size && (e[0] - opts).empty?
1589          # find
1590          idx = i
1591          next
1592        end
1593      }
1594    end
1595
1596    if idx >= 0
1597      @trace_elem[elem].delete_at(idx)
1598    else
1599      return self
1600    end
1601
1602    if USE_OLD_TRACE_OPTION_STYLE
1603      newopts = ''
1604      @trace_var.each{|e|
1605        e[0].each_byte{|c| newopts.concat(c.chr) unless newopts.index(c.chr)}
1606      }
1607      @trace_elem.each{|elem|
1608        @trace_elem[elem].each{|e|
1609          e[0].each_byte{|c| newopts.concat(c.chr) unless newopts.index(c.chr)}
1610        }
1611      }
1612    else
1613      newopts = []
1614      @trace_var.each{|e|
1615        newopts |= e[0]
1616      }
1617      @trace_elem.each{|elem|
1618        @trace_elem[elem].each{|e|
1619          e[0].each_byte{|c| newopts.concat(c.chr) unless newopts.index(c.chr)}
1620        }
1621      }
1622    end
1623
1624    if USE_OLD_TRACE_OPTION_STYLE
1625      diff = false
1626      @trace_opts.each_byte{|c| break if (diff = ! newopts.index(c))}
1627      if diff
1628        Tk.tk_call_without_enc('trace', 'vdelete',
1629                               @id, @trace_opts, 'rb_var ' << @id)
1630        @trace_opts.replace(newopts)
1631        unless @trace_opts.empty?
1632          Tk.tk_call_without_enc('trace', 'variable',
1633                                 @id, @trace_opts, 'rb_var ' << @id)
1634        end
1635      end
1636    else
1637      unless (@trace_opts - newopts).empty?
1638        Tk.tk_call_without_enc('trace', 'remove', 'variable',
1639                               @id, @trace_opts, 'rb_var ' << @id)
1640        @trace_opts.replace(newopts)
1641        unless @trace_opts.empty?
1642          Tk.tk_call_without_enc('trace', 'add', 'variable',
1643                                 @id, @trace_opts, 'rb_var ' << @id)
1644        end
1645      end
1646    end
1647
1648    self
1649  end
1650  alias trace_delete_for_element  trace_remove_for_element
1651  alias trace_vdelete_for_element trace_remove_for_element
1652end
1653
1654class TkVarAccess<TkVariable
1655  def self.new(name, *args)
1656    if name.kind_of?(TkVariable)
1657      name.value = args[0] unless args.empty?
1658      return name
1659    end
1660
1661    name = name.to_s
1662    v = nil
1663    TkVar_ID_TBL.mutex.synchronize{
1664      if v = TkVar_ID_TBL[name]
1665        v.value = args[0] unless args.empty?
1666        return v
1667      else
1668        (v = self.allocate).instance_eval{
1669          @id = name
1670          TkVar_ID_TBL[@id] = self
1671          @var = @id
1672        }
1673      end
1674    }
1675
1676    v.instance_eval{ initialize(name, *args) }
1677    v
1678  end
1679
1680  def self.new_hash(name, *args)
1681    if name.kind_of?(TkVariable)
1682      unless name.is_hash?
1683        fail ArgumentError, "already exist as a scalar variable"
1684      end
1685      name.value = args[0] unless args.empty?
1686      return name
1687    end
1688
1689    name = name.to_s
1690    v = nil
1691    TkVar_ID_TBL.mutex.synchronize{
1692      if v = TkVar_ID_TBL[name]
1693        unless v.is_hash?
1694          fail ArgumentError, "already exist as a scalar variable"
1695        end
1696        v.value = args[0] unless args.empty?
1697        return v
1698      else
1699        (v = self.allocate).instance_eval{
1700          @id = name
1701          TkVar_ID_TBL[@id] = self
1702          @var = @id
1703        }
1704      end
1705    }
1706
1707    INTERP._invoke_without_enc('global', name)
1708    if args.empty? && INTERP._invoke_without_enc('array', 'exist', name) == '0'
1709      v.instance_eval{ initialize(name, {}) }  # force creating
1710    else
1711      v.instance_eval{ initialize(name, *args) }
1712    end
1713    v
1714  end
1715
1716  def initialize(varname, val=nil)
1717    # @id = varname
1718    # TkVar_ID_TBL[@id] = self
1719
1720    # @var  = @id
1721    @elem = nil
1722
1723    @def_default = false
1724    @default_val = nil
1725
1726    @trace_var  = nil
1727    @trace_elem = nil
1728    @trace_opts = nil
1729
1730    @type = nil
1731    var = self
1732    @element_type = Hash.new{|k,v| var.default_value_type }
1733
1734    # is an element?
1735    if @id =~ /^([^(]+)\((.+)\)$/
1736      # is an element --> var == $1, elem == $2
1737      @var  = $1
1738      @elem = $2
1739    end
1740
1741    # teach Tk-ip that @id is global var
1742    INTERP._invoke_without_enc('global', @var)
1743=begin
1744    begin
1745      INTERP._invoke_without_enc('global', @id)
1746    rescue => e
1747      if @id =~ /^(.+)\([^()]+\)$/
1748        # is an element --> varname == $1
1749        INTERP._invoke_without_enc('global', $1)
1750      else
1751        fail e
1752      end
1753    end
1754=end
1755
1756    if val
1757      if val.kind_of?(Hash)
1758        # assoc-array variable
1759        self[''] = 0
1760        self.clear
1761      end
1762      #s = '"' + _get_eval_string(val).gsub(/[\[\]$"]/, '\\\\\&') + '"' #"
1763      #s = '"' + _get_eval_string(val).gsub(/[\[\]$"\\]/, '\\\\\&') + '"' #"
1764      #INTERP._eval(Kernel.format('global %s; set %s %s', @id, @id, s))
1765      #INTERP._set_global_var(@id, _toUTF8(_get_eval_string(val)))
1766      self.value = val
1767    end
1768  end
1769end
1770
1771module Tk
1772  begin
1773    INTERP._invoke_without_enc('global', 'auto_path')
1774    auto_path = INTERP._invoke('set', 'auto_path')
1775  rescue => e
1776    begin
1777      INTERP._invoke_without_enc('global', 'env')
1778      auto_path = INTERP._invoke('set', 'env(TCLLIBPATH)')
1779    rescue => e
1780      auto_path = Tk::LIBRARY
1781    end
1782  end
1783
1784  AUTO_PATH = TkVarAccess.new('auto_path', auto_path)
1785
1786=begin
1787  AUTO_OLDPATH = tk_split_simplelist(INTERP._invoke('set', 'auto_oldpath'))
1788  AUTO_OLDPATH.each{|s| s.freeze}
1789  AUTO_OLDPATH.freeze
1790=end
1791
1792  TCL_PACKAGE_PATH = TkVarAccess.new('tcl_pkgPath')
1793  PACKAGE_PATH = TCL_PACKAGE_PATH
1794
1795  TCL_LIBRARY_PATH = TkVarAccess.new('tcl_libPath')
1796  LIBRARY_PATH = TCL_LIBRARY_PATH
1797
1798  TCL_PRECISION = TkVarAccess.new('tcl_precision')
1799end
1800