1#
2#  tkextlib/tktable/tktable.rb
3#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
4#
5
6require 'tk'
7require 'tk/validation'
8
9# call setup script for general 'tkextlib' libraries
10require 'tkextlib/setup.rb'
11
12# call setup script
13require 'tkextlib/tktable/setup.rb'
14
15# TkPackage.require('Tktable', '2.8')
16TkPackage.require('Tktable')
17
18module Tk
19  class TkTable < TkWindow
20    PACKAGE_NAME = 'Tktable'.freeze
21    def self.package_name
22      PACKAGE_NAME
23    end
24
25    def self.package_version
26      begin
27        TkPackage.require('Tktable')
28      rescue
29        ''
30      end
31    end
32
33    class CellTag < TkObject
34    end
35
36    module ConfigMethod
37    end
38  end
39end
40
41module Tk::TkTable::ConfigMethod
42  include TkItemConfigMethod
43
44  def __item_cget_cmd(id)  # id := [ type, tagOrId ]
45    [self.path, id[0], 'cget', id[1]]
46  end
47  private :__item_cget_cmd
48
49  def __item_config_cmd(id)  # id := [ type, tagOrId ]
50    [self.path, id[0], 'configure', id[1]]
51  end
52  private :__item_config_cmd
53
54  def __item_pathname(id)
55    if id.kind_of?(Array)
56      id = tagid(id[1])
57    end
58    [self.path, id].join(';')
59  end
60  private :__item_pathname
61
62  def __item_boolval_optkeys(id)
63    super(id) << 'multiline' << 'showtext' << 'wrap'
64  end
65  private :__item_boolval_optkeys
66
67  def __item_strval_optkeys(id)
68    super(id) << 'ellipsis'
69  end
70  private :__item_strval_optkeys
71
72  def __item_val2ruby_optkeys(id)  # { key=>method, ... }
73    super(id).update('window'=>proc{|k,v| window(v)})
74  end
75  private :__item_val2ruby_optkeys
76
77  def tag_cget_tkstring(tagOrId, option)
78    itemcget_tkstring(['tag', tagid(tagOrId)], option)
79  end
80  def tag_cget(tagOrId, option)
81    itemcget(['tag', tagid(tagOrId)], option)
82  end
83  def tag_cget_strict(tagOrId, option)
84    itemcget_strict(['tag', tagid(tagOrId)], option)
85  end
86  def tag_configure(tagOrId, slot, value=None)
87    itemconfigure(['tag', tagid(tagOrId)], slot, value)
88  end
89  def tag_configinfo(tagOrId, slot=nil)
90    itemconfiginfo(['tag', tagid(tagOrId)], slot)
91  end
92  def current_tag_configinfo(tagOrId, slot=nil)
93    current_itemconfiginfo(['tag', tagid(tagOrId)], slot)
94  end
95
96  def window_cget_tkstring(tagOrId, option)
97    itemcget_tkstring(['window', tagid(tagOrId)], option)
98  end
99  def window_cget(tagOrId, option)
100    itemcget(['window', tagid(tagOrId)], option)
101  end
102  def window_cget_strict(tagOrId, option)
103    itemcget_strict(['window', tagid(tagOrId)], option)
104  end
105  def window_configure(tagOrId, slot, value=None)
106    if slot == :window || slot == 'window'
107      value = _epath(value)
108    elsif slot.kind_of?(Hash)
109      if slot.key?(:window) || slot.key?('window')
110        slot = _symbolkey2str(slot)
111        slot['window'] = _epath(slot['window'])
112      end
113    end
114    itemconfigure(['window', tagid(tagOrId)], slot, value)
115  end
116  def window_configinfo(tagOrId, slot=nil)
117    itemconfiginfo(['window', tagid(tagOrId)], slot)
118  end
119  def current_window_configinfo(tagOrId, slot=nil)
120    current_itemconfiginfo(['window', tagid(tagOrId)], slot)
121  end
122
123  private :itemcget_tkstring, :itemcget, :itemcget_strict
124  private :itemconfigure, :itemconfiginfo, :current_itemconfiginfo
125end
126
127#####################################################
128
129class Tk::TkTable::CellTag
130  include TkTreatTagFont
131
132  CellTagID_TBL = TkCore::INTERP.create_table
133
134  (CellTag_ID = ['tktbl:celltag'.freeze, TkUtil.untrust('00000')]).instance_eval{
135    @mutex = Mutex.new
136    def mutex; @mutex; end
137    freeze
138  }
139
140  TkCore::INTERP.init_ip_env{
141    CellTagID_TBL.mutex.synchronize{ CellTagID_TBL.clear }
142  }
143
144  def self.id2obj(table, id)
145    tpath = table.path
146    CellTagID_TBL.mutex.synchronize{
147      if CellTagID_TBL[tpath]
148        CellTagID_TBL[tpath][id]? CellTagID_TBL[tpath][id] : id
149      else
150        id
151      end
152    }
153  end
154
155  def initialize(parent, keys=nil)
156    @parent = @t = parent
157    @tpath - parent.path
158    CellTag_ID.mutex.synchronize{
159      @path = @id = CellTag_ID.join(TkCore::INTERP._ip_id_)
160      CellTag_ID[1].succ!
161    }
162    CellTagID_TBL.mutex.synchronize{
163      CellTagID_TBL[@tpath] = {} unless CellTagID_TBL[@tpath]
164      CellTagID_TBL[@tpath][@id] = self
165    }
166    configure(keys) if keys
167  end
168
169  def id
170    @id
171  end
172
173  def destroy
174    tk_call(@tpath, 'tag', 'delete', @id)
175    CellTagID_TBL.mutex.synchronize{
176      CellTagID_TBL[@tpath].delete(@id) if CellTagID_TBL[@tpath]
177    }
178    self
179  end
180  alias delete destroy
181
182  def exist?
183    @t.tag_exist?(@id)
184  end
185  def include?(idx)
186    @t.tag_include?(@id, idx)
187  end
188
189  def add_cell(*args)
190    @t.tag_cell(@id, *args)
191  end
192  def add_col(*args)
193    @t.tag_col(@id, *args)
194  end
195  def add_row(*args)
196    @t.tag_row(@id, *args)
197  end
198
199  def raise(target=None)
200    @t.tag_raise(@id, target)
201  end
202  def lower(target=None)
203    @t.tag_lower(@id, target)
204  end
205
206  def cget_tkstring(key)
207    @t.tag_cget_tkstring(@id, key)
208  end
209  def cget(key)
210    @t.tag_cget(@id, key)
211  end
212  def cget_strict(key)
213    @t.tag_cget_strict(@id, key)
214  end
215  def configure(key, val=None)
216    @t.tag_configure(@id, key, val)
217  end
218  def configinfo(key=nil)
219    @t.tag_configinfo(@id, key)
220  end
221  def current_configinfo(key=nil)
222    @t.current_tag_configinfo(@id, key)
223  end
224end
225
226class Tk::TkTable::NamedCellTag < Tk::TkTable::CellTag
227  def self.new(parent, name, keys=nil)
228    obj = nil
229    CellTagID_TBL.mutex.synchronize{
230      if CellTagID_TBL[parent.path] && CellTagID_TBL[parent.path][name]
231        obj = CellTagID_TBL[parent.path][name]
232      else
233        #super(parent, name, keys)
234        (obj = self.allocate).instance_eval{
235          @parent = @t = parent
236          @tpath = parent.path
237          @path = @id = name
238          CellTagID_TBL[@tpath] = {} unless CellTagID_TBL[@tpath]
239          CellTagID_TBL[@tpath][@id] = self
240        }
241      end
242    }
243    obj.configure(keys) if keys && ! keys.empty?
244    obj
245  end
246
247  def initialize(parent, name, keys=nil)
248    # dummy:: not called by 'new' method
249    @parent = @t = parent
250    @tpath = parent.path
251    @path = @id = name
252    CellTagID_TBL.mutex.synchronize{
253      CellTagID_TBL[@tpath] = {} unless CellTagID_TBL[@tpath]
254      CellTagID_TBL[@tpath][@id] = self
255    }
256    configure(keys) if keys && ! keys.empty?
257  end
258end
259
260#####################################################
261
262class Tk::TkTable
263  TkCommandNames = ['table'.freeze].freeze
264  WidgetClassName = 'Table'.freeze
265  WidgetClassNames[WidgetClassName] ||= self
266
267  include Scrollable
268  include Tk::TkTable::ConfigMethod
269  include Tk::ValidateConfigure
270
271  def __destroy_hook__
272    Tk::TkTable::CelTag::CellTagID_TBL.mutex.synchronize{
273      Tk::TkTable::CelTag::CellTagID_TBL.delete(@path)
274    }
275  end
276
277  def __boolval_optkeys
278    super() << 'autoclear' << 'flashmode' << 'invertselected' <<
279      'multiline' << 'selecttitle' << 'wrap'
280  end
281  private :__boolval_optkeys
282
283  def __strval_optkeys
284    super() << 'colseparator' << 'ellipsis' << 'rowseparator' << 'sparsearray'
285  end
286  private :__strval_optkeys
287
288
289  #################################
290
291  class BrowseCommand < TkValidateCommand
292    class ValidateArgs < TkUtil::CallbackSubst
293      KEY_TBL = [
294        [ ?c, ?n, :column ],
295        [ ?C, ?s, :index ],
296        [ ?i, ?x, :cursor ],
297        [ ?r, ?n, :row ],
298        [ ?s, ?s, :last_index ],
299        [ ?S, ?s, :new_index ],
300        [ ?W, ?w, :widget ],
301        nil
302      ]
303
304      PROC_TBL = [
305        [ ?n, TkComm.method(:number) ],
306        [ ?x, TkComm.method(:num_or_str) ],
307        [ ?s, TkComm.method(:string) ],
308        [ ?w, TkComm.method(:window) ],
309        nil
310      ]
311
312=begin
313      # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
314      KEY_TBL.map!{|inf|
315        if inf.kind_of?(Array)
316          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
317          inf[1] = inf[1].getbyte(0) if inf[1].kind_of?(String)
318        end
319        inf
320      }
321
322      PROC_TBL.map!{|inf|
323        if inf.kind_of?(Array)
324          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
325        end
326        inf
327      }
328=end
329
330      _setup_subst_table(KEY_TBL, PROC_TBL);
331
332      def self.ret_val(val)
333        val
334      end
335    end
336
337    def self._config_keys
338      ['browsecommand', 'browsecmd']
339    end
340  end
341  #--------------------------------
342  class CellCommand < TkValidateCommand
343    class ValidateArgs < TkUtil::CallbackSubst
344      KEY_TBL = [
345        [ ?c, ?n, :column ],
346        [ ?C, ?s, :index ],
347        [ ?i, ?m, :rw_mode ],
348        [ ?r, ?n, :row ],
349        [ ?s, ?v, :value ],
350        [ ?W, ?w, :widget ],
351        nil
352      ]
353
354      PROC_TBL = [
355        [ ?n, TkComm.method(:number) ],
356        [ ?s, TkComm.method(:string) ],
357        [ ?w, TkComm.method(:window) ],
358        [ ?m, proc{|val| (val == '0')? (:r) : (:w)} ],
359        [ ?v, proc{|val| TkComm.tk_tcl2ruby(val, true, false)} ],
360        nil
361      ]
362
363=begin
364      # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
365      KEY_TBL.map!{|inf|
366        if inf.kind_of?(Array)
367          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
368          inf[1] = inf[1].getbyte(0) if inf[1].kind_of?(String)
369        end
370        inf
371      }
372
373      PROC_TBL.map!{|inf|
374        if inf.kind_of?(Array)
375          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
376        end
377        inf
378      }
379=end
380
381      _setup_subst_table(KEY_TBL, PROC_TBL);
382
383      def self.ret_val(val)
384        TkComm._get_eval_string(val)
385      end
386    end
387
388    def self._config_keys
389      ['command']
390    end
391  end
392  #--------------------------------
393  class SelectionCommand < TkValidateCommand
394    class ValidateArgs < TkUtil::CallbackSubst
395      KEY_TBL = [
396        [ ?c, ?n, :sel_columns ],
397        [ ?C, ?s, :sel_area ],
398        [ ?i, ?n, :total ],
399        [ ?r, ?n, :sel_rows ],
400        [ ?s, ?s, :value ],
401        [ ?W, ?w, :widget ],
402        nil
403      ]
404
405      PROC_TBL = [
406        [ ?n, TkComm.method(:number) ],
407        [ ?s, TkComm.method(:string) ],
408        [ ?w, TkComm.method(:window) ],
409        nil
410      ]
411
412=begin
413      # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
414      KEY_TBL.map!{|inf|
415        if inf.kind_of?(Array)
416          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
417          inf[1] = inf[1].getbyte(0) if inf[1].kind_of?(String)
418        end
419        inf
420      }
421
422      PROC_TBL.map!{|inf|
423        if inf.kind_of?(Array)
424          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
425        end
426        inf
427      }
428=end
429
430      _setup_subst_table(KEY_TBL, PROC_TBL);
431
432      def self.ret_val(val)
433        val.to_s
434      end
435    end
436
437    def self._config_keys
438      ['selectioncommand', 'selcmd']
439    end
440  end
441  #--------------------------------
442  class ValidateCommand < TkValidateCommand
443    class ValidateArgs < TkUtil::CallbackSubst
444      KEY_TBL = [
445        [ ?c, ?n, :column ],
446        [ ?C, ?s, :index ],
447        [ ?i, ?x, :cursor ],
448        [ ?r, ?n, :row ],
449        [ ?s, ?v, :current_value ],
450        [ ?S, ?v, :new_value ],
451        [ ?W, ?w, :widget ],
452        nil
453      ]
454
455      PROC_TBL = [
456        [ ?n, TkComm.method(:number) ],
457        [ ?x, TkComm.method(:num_or_str) ],
458        [ ?s, TkComm.method(:string) ],
459        [ ?w, TkComm.method(:window) ],
460        [ ?v, proc{|val| TkComm.tk_tcl2ruby(val, true, false)} ],
461        nil
462      ]
463
464=begin
465      # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
466      KEY_TBL.map!{|inf|
467        if inf.kind_of?(Array)
468          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
469          inf[1] = inf[1].getbyte(0) if inf[1].kind_of?(String)
470        end
471        inf
472      }
473
474      PROC_TBL.map!{|inf|
475        if inf.kind_of?(Array)
476          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
477        end
478        inf
479      }
480=end
481
482      _setup_subst_table(KEY_TBL, PROC_TBL);
483    end
484
485    def self._config_keys
486      ['vcmd', 'validatecommand']
487    end
488  end
489
490  #################################
491
492  def __validation_class_list
493    super() <<
494      BrowseCommand << CellCommand << SelectionCommand << ValidateCommand
495  end
496
497  Tk::ValidateConfigure.__def_validcmd(binding, BrowseCommand)
498  Tk::ValidateConfigure.__def_validcmd(binding, CellCommand)
499  Tk::ValidateConfigure.__def_validcmd(binding, SelectionCommand)
500  Tk::ValidateConfigure.__def_validcmd(binding, ValidateCommand)
501
502  #################################
503
504  def activate(idx)
505    tk_send('activate', tagid(idx))
506  end
507
508  def bbox(idx)
509    list(tk_send('bbox', tagid(idx)))
510  end
511
512  def border_mark(x, y)
513    simplelist(tk_send('border', 'mark', x, y))
514  end
515  def border_mark_row(x, y)
516    tk_send('border', 'mark', x, y, 'row')
517  end
518  def border_mark_col(x, y)
519    tk_send('border', 'mark', x, y, 'col')
520  end
521  def border_dragto(x, y)
522    tk_send('border', 'dragto', x, y)
523  end
524
525  def clear_cache(first=None, last=None)
526    tk_send('clear', 'cache', tagid(first), tagid(last))
527    self
528  end
529  def clear_sizes(first=None, last=None)
530    tk_send('clear', 'sizes', tagid(first), tagid(last))
531    self
532  end
533  def clear_tags(first=None, last=None)
534    tk_send('clear', 'tags', tagid(first), tagid(last))
535    self
536  end
537  def clear_all(first=None, last=None)
538    tk_send('clear', 'all', tagid(first), tagid(last))
539    self
540  end
541
542  def curselection
543    simplelist(tk_send('curselection'))
544  end
545  def curselection=(val)
546    tk_send('curselection', val)
547    val
548  end
549
550  def curvalue
551    tk_tcl2ruby(tk_send('curvalue'), true, false)
552  end
553  def curvalue=(val)
554    tk_send('curvalue', val)
555    val
556  end
557
558  def delete_active(idx1, idx2=None)
559    tk_send('delete', 'active', tagid(idx1), tagid(idx2))
560    self
561  end
562  def delete_cols(*args) # ?switches_array?, index, ?count?
563    params = []
564    if args[0].kind_of?(Array)
565      switches = args.shift
566      switches.each{|k| params << "-#{k}"}
567    end
568    params << '--'
569    params << tagid(args.shift)
570    params.concat(args)
571    tk_send('delete', 'cols', *params)
572    self
573  end
574  def delete_rows(*args) # ?switches_array?, index, ?count?
575    params = []
576    if args[0].kind_of?(Array)
577      switches = args.shift
578      switches.each{|k| params << "-#{k}"}
579    end
580    params << '--'
581    params << tagid(args.shift)
582    params.concat(args)
583    tk_send('delete', 'rows', *params)
584    self
585  end
586
587  def get(idx)
588    tk_tcl2ruby(tk_send('get', tagid(idx)), true, false)
589  end
590  def get_area(idx1, idx2)
591    simplelist(tk_send('get', tagid(idx1), tagid(idx2))).collect{|v|
592      tk_tcl2ruby(v, true, false)
593    }
594  end
595
596  def height_list
597    list(tk_send('height'))
598  end
599  def height(row)
600    number(tk_send('height', row))
601  end
602  def set_height(*pairs)
603    tk_send('height', *(pairs.flatten))
604    self
605  end
606
607  def hidden_list
608    simplelist(tk_send('hidden'))
609  end
610  def hidden?(idx, *args)
611    if args.empty?
612      if (ret = tk_send('hidden', tagid(idx))) == ''
613        false
614      else
615        ret
616      end
617    else
618      bool(tk_send('hidden', tagid(idx), *(args.collect{|i| tagid(i)})))
619    end
620  end
621
622  def icursor
623    number(tk_send('icursor'))
624  end
625  def icursor_set(idx)
626    number(tk_send('icursor', tagid(idx)))
627  end
628
629  def index(idx)
630    tk_send('index', tagid(idx))
631  end
632  def row_index(idx)
633    number(tk_send('index', tagid(idx), 'row'))
634  end
635  def col_index(idx)
636    number(tk_send('index', tagid(idx), 'col'))
637  end
638
639  def insert_active(idx, val)
640    tk_send('insert', 'active', tagid(idx), val)
641    self
642  end
643  def insert_cols(*args) # ?switches_array?, index, ?count?
644    params = []
645    if args[0].kind_of?(Array)
646      switches = args.shift
647      switches.each{|k| params << "-#{k}"}
648    end
649    params << '--'
650    params.concat(args)
651    params << tagid(args.shift)
652    tk_send('insert', 'cols', *params)
653    self
654  end
655  def insert_rows(*args) # ?switches_array?, index, ?count?
656    params = []
657    if args[0].kind_of?(Array)
658      switches = args.shift
659      switches.each{|k| params << "-#{k}"}
660    end
661    params << '--'
662    params << tagid(args.shift)
663    params.concat(args)
664    tk_send('insert', 'rows', *params)
665    self
666  end
667
668  # def postscript(*args)
669  #   tk_send('postscript', *args)
670  # end
671
672  def reread
673    tk_send('reread')
674    self
675  end
676
677  def scan_mark(x, y)
678    tk_send('scan', 'mark', x, y)
679    self
680  end
681  def scan_dragto(x, y)
682    tk_send('scan', 'dragto', x, y)
683    self
684  end
685
686  def see(idx)
687    tk_send('see', tagid(idx))
688    self
689  end
690
691  def selection_anchor(idx)
692    tk_send('selection', 'anchor', tagid(idx))
693    self
694  end
695  def selection_clear(first, last=None)
696    tk_send('selection', 'clear', tagid(first), tagid(last))
697    self
698  end
699  def selection_clear_all
700    selection_clear('all')
701  end
702  def selection_include?(idx)
703    bool(tk_send('selection', 'includes', tagid(idx)))
704  end
705  def selection_present
706    bool(tk_send('selection', 'present'))
707  end
708  def selection_set(first, last=None)
709    tk_send('selection', 'set', tagid(first), tagid(last))
710    self
711  end
712
713  def set(*pairs) # idx, val, idx, val, ...
714    args = []
715    0.step(pairs.size-1, 2){|i|
716      args << tagid(pairs[i])
717      args << pairs[i+1]
718    }
719    tk_send('set', *args)
720    self
721  end
722  def set_row(*pairs) # idx, val, idx, val, ...
723    args = []
724    0.step(pairs.size-1, 2){|i|
725      args << tagid(pairs[i])
726      args << pairs[i+1]
727    }
728    tk_send('set', 'row', *args)
729    self
730  end
731  def set_col(*pairs) # idx, val, idx, val, ...
732    args = []
733    0.step(pairs.size-1, 2){|i|
734      args << tagid(pairs[i])
735      args << pairs[i+1]
736    }
737    tk_send('set', 'col', *args)
738    self
739  end
740=begin
741  def set(*pairs) # idx, val, idx, val, ...  OR [idx, val], [idx, val], ...
742    if pairs[0].kind_of?(Array)
743      # [idx, val], [idx, val], ...
744      args = []
745      pairs.each{|idx, val| args << tagid(idx) << val }
746      tk_send('set', *args)
747    else
748      # idx, val, idx, val, ...
749      args = []
750      0.step(pairs.size-1, 2){|i|
751        args << tagid(pairs[i])
752        args << pairs[i+1]
753      }
754      tk_send('set', *args)
755    end
756    self
757  end
758  def set_row(*pairs)
759    if pairs[0].kind_of?(Array)
760      # [idx, val], [idx, val], ...
761      args = []
762      pairs.each{|idx, val| args << tagid(idx) << val }
763      tk_send('set', 'row', *args)
764    else
765      # idx, val, idx, val, ...
766      args = []
767      0.step(pairs.size-1, 2){|i|
768        args << tagid(pairs[i])
769        args << pairs[i+1]
770      }
771      tk_send('set', 'row', *args)
772    end
773    self
774  end
775  def set_col(*pairs)
776    if pairs[0].kind_of?(Array)
777      # [idx, val], [idx, val], ...
778      args = []
779      pairs.each{|idx, val| args << idx << val }
780      tk_send('set', 'col', *args)
781    else
782      # idx, val, idx, val, ...
783      args = []
784      0.step(pairs.size-1, 2){|i|
785        args << tagid(pairs[i])
786        args << pairs[i+1]
787      }
788      tk_send('set', 'col', *args)
789    end
790    self
791  end
792=end
793
794  def spans
795    simplelist(tk_send('spans')).collect{|inf|
796      lst = simplelist(inf)
797      idx = lst[0]
798      rows, cols = lst[1].split(',').map!{|n| Integer(n)}
799      [idx [rows, cols]]
800    }
801  end
802  alias span_list spans
803  def span(idx)
804    lst = simplelist(tk_send('spans', tagid(idx)))
805    idx = lst[0]
806    rows, cols = lst[1].split(',').map!{|n| Integer(n)}
807    [idx [rows, cols]]
808  end
809  def set_spans(*pairs)
810    # idx, val, idx, val, ...
811    args = []
812    0.step(pairs.size-1, 2){|i|
813      args << tagid(pairs[i])
814      val = pairs[i+1]
815      if val.kind_of?(Array)
816        args << val.join(',')
817      else
818        args << val
819      end
820    }
821    tk_send('spans', *args)
822    self
823  end
824=begin
825  def set_spans(*pairs)
826    if pairs[0].kind_of?(Array)
827      # [idx, val], [idx, val], ...
828      args = []
829      pairs.each{|idx, val|
830        args << tagid(idx)
831        if val.kind_of?(Array)
832          args << val.join(',')
833        else
834          args << val
835        end
836      }
837      tk_send('spans', *args)
838    else
839      # idx, val, idx, val, ...
840      args = []
841      0.step(pairs.size-1, 2){|i|
842        args << tagid(pairs[i])
843        val = pairs[i+1]
844        if val.kind_of?(Array)
845          args << val.join(',')
846        else
847          args << val
848        end
849      }
850      tk_send('spans', *args)
851    end
852    self
853  end
854=end
855
856  def tagid(tag)
857    if tag.kind_of?(Tk::TkTable::CellTag)
858      tag.id
859    elsif tag.kind_of?(Array)
860      if tag[0].kind_of?(Integer) && tag[1].kind_of?(Integer)
861        # [row, col]
862        tag.join(',')
863      else
864        tag
865      end
866    else
867      tag
868    end
869  end
870
871  def tagid2obj(tagid)
872    Tk::TkTable::CellTag::CellTagID_TBL.mutex.synchronize{
873      if Tk::TkTable::CellTag::CellTagID_TBL.key?(@path)
874        if Tk::TkTable::CellTag::CellTagID_TBL[@path].key?(tagid)
875          Tk::TkTable::CellTag::CellTagID_TBL[@path][tagid]
876        else
877          tagid
878        end
879      else
880        tagid
881      end
882    }
883  end
884
885  def tag_cell(tag, *cells)
886    tk_send('tag', 'cell', tagid(tag), *(cells.collect{|idx| tagid(idx)}))
887    self
888  end
889  def tag_reset(*cells)
890    tk_send('tag', 'cell', '', *(cells.collect{|idx| tagid(idx)}))
891    self
892  end
893  def tag_col(tag, *cols)
894    tk_send('tag', 'col', tagid(tag), *cols)
895    self
896  end
897  def tag_col_reset(*cols)
898    tk_send('tag', 'col', '', *cols)
899    self
900  end
901  def tag_delete(tag)
902    tk_send('tag', 'delete', tagid(tag))
903    Tk::TkTable::CellTag::CellTagID_TBL.mutex.synchronize{
904      if Tk::TkTable::CellTag::CellTagID_TBL[@path]
905        if tag.kind_of? Tk::TkTable::CellTag
906          Tk::TkTable::CellTag::CellTagID_TBL[@path].delete(tag.id)
907        else
908          Tk::TkTable::CellTag::CellTagID_TBL[@path].delete(tag)
909        end
910      end
911    }
912    self
913  end
914  def tag_exist?(tag)
915    bool(tk_send('tag', 'exists', tagid(tag)))
916  end
917  def tag_include?(tag, idx)
918    bool(tk_send('tag', 'includes', tagid(tag), tagid(idx)))
919  end
920  def tag_lower(tag, target=None)
921    tk_send('tag', 'lower', tagid(tag), tagid(target))
922    self
923  end
924  def tag_names(pat=None)
925    simplelist(tk_send('tag', 'names', pat)).collect{|tag| tagid2obj(tag)}
926  end
927  def tag_raise(tag, target=None)
928    tk_send('tag', 'raise', tagid(tag), tagid(target))
929    self
930  end
931  def tag_row(tag, *rows)
932    tk_send('tag', 'row', tagid(tag), *rows)
933    self
934  end
935  def tag_row_reset(*rows)
936    tk_send('tag', 'row', '', *rows)
937    self
938  end
939
940  def validate(idx)
941    bool(tk_send('validate', tagid(idx)))
942  end
943
944  def width_list
945    list(tk_send('width'))
946  end
947  def width(row)
948    number(tk_send('width', row))
949  end
950  def set_width(*pairs)
951    tk_send('width', *(pairs.flatten))
952    self
953  end
954
955  def window_delete(*args)
956    tk_send('window', 'delete', *(args.collect{|idx| tagid(idx)}))
957    self
958  end
959  def window_move(from_idx, to_idx)
960    tk_send('window', 'move', tagid(from_idx), tagid(to_idx))
961    self
962  end
963  def window_names(pat=None)
964    simplelist(tk_send('window', 'names', pat))
965  end
966end
967