1#
2#  tkextlib/blt/treeview.rb
3#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
4#
5
6require 'tk'
7require 'tkextlib/blt.rb'
8require 'tk/validation.rb'
9
10module Tk::BLT
11  class Treeview < TkWindow
12    module ConfigMethod
13    end
14
15    module TagOrID_Methods
16    end
17
18    class Node < TkObject
19    end
20
21    class Tag < TkObject
22    end
23  end
24
25  class Hiertable < Treeview
26  end
27end
28
29######################################
30
31module Tk::BLT::Treeview::ConfigMethod
32  include TkItemConfigMethod
33
34  def __item_boolval_optkeys(id)
35    case id
36    when Array
37      # id := [ 'column', name ]
38      ['edit', 'hide']
39    when 'sort'
40      ['decreasing']
41    else
42      []
43    end
44  end
45  private :__item_boolval_optkeys
46
47  def __item_strval_optkeys(id)
48    case id
49    when Array
50      # id := [ 'column', name ]
51      super() << 'titleforeground' << 'titleshadow'
52    when 'sort'
53      ['decreasing']
54    else
55      []
56    end
57  end
58  private :__item_strval_optkeys
59
60  def __item_listval_optkeys(id)
61    case id
62    when 'entry'
63      ['bindtags']
64    else
65      []
66    end
67  end
68  private :__item_listval_optkeys
69
70  def __item_cget_cmd(id)
71    if id.kind_of?(Array)
72      # id := [ type, name ]
73      [self.path, id[0], 'cget', id[1]]
74    else
75      [self.path, id, 'cget']
76    end
77  end
78  private :__item_cget_cmd
79
80  def __item_config_cmd(id)
81    if id.kind_of?(Array)
82      # id := [ type, name ]
83      [self.path, id[0], 'configure', id[1]]
84    else
85      [self.path, id, 'configure']
86    end
87  end
88  private :__item_config_cmd
89
90  def __item_pathname(id)
91    if id.kind_of?(Array)
92      id = tagid(id[1])
93    end
94    [self.path, id].join(';')
95  end
96  private :__item_pathname
97
98  def column_cget_tkstring(name, option)
99    itemcget_tkstring(['column', name], option)
100  end
101  def column_cget(name, option)
102    itemcget(['column', name], option)
103  end
104  def column_cget_strict(name, option)
105    itemcget_strict(['column', name], option)
106  end
107  def column_configure(name, slot, value=None)
108    itemconfigure(['column', name], slot, value)
109  end
110  def column_configinfo(name, slot=nil)
111    itemconfiginfo(['column', name], slot)
112  end
113  def current_column_configinfo(name, slot=nil)
114    current_itemconfiginfo(['column', name], slot)
115  end
116
117  def button_cget_tkstring(option)
118    itemcget_tkstring('button', option)
119  end
120  def button_cget(option)
121    itemcget('button', option)
122  end
123  def button_cget_strict(option)
124    itemcget_strict('button', option)
125  end
126  def button_configure(slot, value=None)
127    itemconfigure('button', slot, value)
128  end
129  def button_configinfo(slot=nil)
130    itemconfiginfo('button', slot)
131  end
132  def current_button_configinfo(slot=nil)
133    current_itemconfiginfo('button', slot)
134  end
135
136  def entry_cget_tkstring(option)
137    itemcget_tkstring('entry', option)
138  end
139  def entry_cget(option)
140    ret = itemcget('entry', option)
141    if option == 'bindtags' || option == :bindtags
142      ret.collect{|tag| TkBindTag.id2obj(tag)}
143    else
144      ret
145    end
146  end
147  def entry_cget_strict(option)
148    ret = itemcget_strict('entry', option)
149    if option == 'bindtags' || option == :bindtags
150      ret.collect{|tag| TkBindTag.id2obj(tag)}
151    else
152      ret
153    end
154  end
155  def entry_configure(slot, value=None)
156    itemconfigure('entry', slot, value)
157  end
158  def entry_configinfo(slot=nil)
159    ret = itemconfiginfo('entry', slot)
160
161    if TkComm::GET_CONFIGINFO_AS_ARRAY
162      if slot
163        if slot == 'bindtags' || slot == :bindtags
164          ret[-2] = ret[-2].collect{|tag| TkBindTag.id2obj(tag)}
165          ret[-1] = ret[-1].collect{|tag| TkBindTag.id2obj(tag)}
166        end
167      else
168        inf = ret.assoc('bindtags')
169        inf[-2] = inf[-2].collect{|tag| TkBindTag.id2obj(tag)}
170        inf[-1] = inf[-1].collect{|tag| TkBindTag.id2obj(tag)}
171      end
172
173    else # ! TkComm::GET_CONFIGINFO_AS_ARRAY
174      if (inf = ret['bindtags'])
175        inf[-2] = inf[-2].collect{|tag| TkBindTag.id2obj(tag)}
176        inf[-1] = inf[-1].collect{|tag| TkBindTag.id2obj(tag)}
177        ret['bindtags'] = inf
178      end
179    end
180
181    ret
182  end
183  def current_entry_configinfo(slot=nil)
184    ret = current_itemconfiginfo('entry', slot)
185
186    if (val = ret['bindtags'])
187      ret['bindtags'] = val.collect{|tag| TkBindTag.id2obj(tag)}
188    end
189
190    ret
191  end
192
193  def sort_cget_tkstring(option)
194    itemcget_tkstring('sort', option)
195  end
196  def sort_cget(option)
197    itemcget('sort', option)
198  end
199  def sort_cget_strict(option)
200    itemcget_strict('sort', option)
201  end
202  def sort_configure(slot, value=None)
203    itemconfigure('sort', slot, value)
204  end
205  def sort_configinfo(slot=nil)
206    itemconfiginfo('sort', slot)
207  end
208  def current_sort_configinfo(slot=nil)
209    current_itemconfiginfo('sort', slot)
210  end
211
212  def text_cget_tkstring(option)
213    itemcget_tkstring('text', option)
214  end
215  def text_cget(option)
216    itemcget('text', option)
217  end
218  def text_cget_strict(option)
219    itemcget_strict('text', option)
220  end
221  def text_configure(slot, value=None)
222    itemconfigure('text', slot, value)
223  end
224  def text_configinfo(slot=nil)
225    itemconfiginfo('text', slot)
226  end
227  def current_text_configinfo(slot=nil)
228    current_itemconfiginfo('text', slot)
229  end
230
231  private :itemcget_tkstring, :itemcget, :itemcget_strict
232  private :itemconfigure, :itemconfiginfo, :current_itemconfiginfo
233end
234
235class Tk::BLT::Treeview
236  TkCommandNames = ['::blt::treeview'.freeze].freeze
237  WidgetClassName = 'TreeView'.freeze
238  WidgetClassNames[WidgetClassName] ||= self
239
240  include Scrollable
241  include ValidateConfigure
242  include ItemValidateConfigure
243  include Tk::BLT::Treeview::ConfigMethod
244
245  ########################
246
247  def __boolval_optkeys
248    ['autocreate', 'allowduplicates', 'exportselection', 'flat', 'hideroot',
249      'newtags', 'showtitles', 'sortselection']
250  end
251  private :__boolval_optkeys
252
253  def __strval_optkeys
254    super() + ['focusforeground', 'linecolor', 'separator', 'trim']
255  end
256  private :__strval_optkeys
257
258  ########################
259
260  class OpenCloseCommand < TkValidateCommand
261    class ValidateArgs < TkUtil::CallbackSubst
262      KEY_TBL = [
263        [ ?W, ?w, :widget ],
264        [ ?p, ?s, :name ],
265        [ ?P, ?s, :fullpath ],
266        [ ?#, ?x, :node_id ],
267        nil
268      ]
269
270      PROC_TBL = [
271        [ ?x, TkComm.method(:num_or_str) ],
272        [ ?s, TkComm.method(:string) ],
273        [ ?w, TkComm.method(:window) ],
274        nil
275      ]
276
277=begin
278      # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
279      KEY_TBL.map!{|inf|
280        if inf.kind_of?(Array)
281          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
282          inf[1] = inf[1].getbyte(0) if inf[1].kind_of?(String)
283        end
284        inf
285      }
286
287      PROC_TBL.map!{|inf|
288        if inf.kind_of?(Array)
289          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
290        end
291        inf
292      }
293=end
294
295      _setup_subst_table(KEY_TBL, PROC_TBL);
296
297      def self.ret_val(val)
298        val
299      end
300    end
301
302    def self._config_keys
303      ['opencommand', 'closecomand']
304    end
305  end
306
307  def __validation_class_list
308    super() << OpenCloseCommand
309  end
310
311  Tk::ValidateConfigure.__def_validcmd(binding, OpenCloseCommand)
312
313  ########################
314
315  def __item_validation_class_list(id)
316    case id
317    when 'entry'
318      super(id) << OpenCloseCommand
319    else
320      super(id)
321    end
322  end
323
324  Tk::ItemValidateConfigure.__def_validcmd(binding, OpenCloseCommand)
325
326  ########################
327
328  def __destroy_hook__
329    Tk::BLT::Treeview::Node::TreeNodeID_TBL.mutex.synchronize{
330      Tk::BLT::Treeview::Node::TreeNodeID_TBL.delete(@path)
331    }
332    Tk::BLT::Treeview::Tag::TreeTagID_TBL.mutex.synchronize{
333      Tk::BLT::Treeview::Tag::TreeTagID_TBL.delete(@path)
334    }
335  end
336
337  def tagid(tag)
338    if tag.kind_of?(Tk::BLT::Treeview::Node) \
339      || tag.kind_of?(Tk::BLT::Treeview::Tag)
340      tag.id
341    else
342      tag  # maybe an Array of configure paramters
343    end
344  end
345  private :tagid
346
347  def tagid2obj(tagid)
348    if tagid.kind_of?(Integer)
349      Tk::BLT::Treeview::Node.id2obj(self, tagid.to_s)
350    elsif tagid.kind_of?(String)
351      if tagid =~ /^\d+$/
352        Tk::BLT::Treeview::Node.id2obj(self, tagid)
353      else
354        Tk::BLT::Treeview::Tag.id2obj(self, tagid)
355      end
356    else
357      tagid
358    end
359  end
360
361  def bbox(*tags)
362    list(tk_send('bbox', *(tags.collect{|tag| tagid(tag)})))
363  end
364
365  def screen_bbox(*tags)
366    list(tk_send('bbox', '-screen', *(tags.collect{|tag| tagid(tag)})))
367  end
368
369  def tag_bind(tag, seq, *args)
370    if TkComm._callback_entry?(args[0]) || !block_given?
371      cmd = args.shift
372    else
373      cmd = Proc.new
374    end
375    _bind([@path, 'bind', tagid(tag)], seq, cmd, *args)
376    self
377  end
378  def tag_bind_append(tag, seq, *args)
379    if TkComm._callback_entry?(args[0]) || !block_given?
380      cmd = args.shift
381    else
382      cmd = Proc.new
383    end
384    _bind_append([@path, 'bind', tagid(tag)], seq, cmd, *args)
385    self
386  end
387  def tag_bind_remove(tag, seq)
388    _bind_remove([@path, 'bind', tagid(tag)], seq)
389    self
390  end
391  def tag_bindinfo(tag, seq=nil)
392    _bindinfo([@path, 'bind', tagid(tag)], seq)
393  end
394
395  def button_activate(tag)
396    tk_send('button', 'activate', tagid(tag))
397    self
398  end
399
400  def button_bind(tag, seq, *args)
401    if TkComm._callback_entry?(args[0]) || !block_given?
402      cmd = args.shift
403    else
404      cmd = Proc.new
405    end
406    _bind([@path, 'button', 'bind', tagid(tag)], seq, cmd, *args)
407    self
408  end
409  def button_bind_append(tag, seq, *args)
410    if TkComm._callback_entry?(args[0]) || !block_given?
411      cmd = args.shift
412    else
413      cmd = Proc.new
414    end
415    _bind_append([@path, 'button', 'bind', tagid(tag)], seq, cmd, *args)
416    self
417  end
418  def button_bind_remove(tag, seq)
419    _bind_remove([@path, 'button', 'bind', tagid(tag)], seq)
420    self
421  end
422  def button_bindinfo(tag, seq=nil)
423    _bindinfo([@path, 'button', 'bind', tagid(tag)], seq)
424  end
425
426  def close(*tags)
427    tk_send('close', *(tags.collect{|tag| tagid(tag)}))
428    self
429  end
430  def close_recurse(*tags)
431    tk_send('close', '-recurse', *(tags.collect{|tag| tagid(tag)}))
432    self
433  end
434
435  def column_activate(column=None)
436    if column == None
437      tk_send('column', 'activate')
438    else
439      tk_send('column', 'activate', column)
440      self
441    end
442  end
443
444  def column_delete(*fields)
445    tk_send('column', 'delete', *fields)
446    self
447  end
448  def column_insert(pos, field, *opts)
449    tk_send('column', 'insert', pos, field, *opts)
450    self
451  end
452  def column_invoke(field)
453    tk_send('column', 'invoke', field)
454    self
455  end
456  def column_move(name, dest)
457    tk_send('column', 'move', name, dest)
458    self
459  end
460  def column_names()
461    simplelist(tk_send('column', 'names'))
462  end
463  def column_nearest(x, y=None)
464    tk_send('column', 'nearest', x, y)
465  end
466
467  def curselection
468    simplelist(tk_send('curselection')).collect{|id| tagid2obj(id)}
469  end
470
471  def delete(*tags)
472    tk_send('delete', *(tags.collect{|tag| tagid(tag)}))
473    self
474  end
475
476  def entry_activate(tag)
477    tk_send('entry', 'activate', tagid(tag))
478    self
479  end
480  def entry_children(tag, first=None, last=None)
481    simplelist(tk_send('entry', 'children', tagid(tag),
482                       first, last)).collect{|id| tagid2obj(id)}
483  end
484  def entry_delete(tag, first=None, last=None)
485    tk_send('entry', 'delete', tagid(tag), first, last)
486  end
487  def entry_before?(tag1, tag2)
488    bool(tk_send('entry', 'isbefore', tagid(tag1), tagid(tag2)))
489  end
490  def entry_hidden?(tag)
491    bool(tk_send('entry', 'ishidden', tagid(tag)))
492  end
493  def entry_open?(tag)
494    bool(tk_send('entry', 'isopen', tagid(tag)))
495  end
496
497  def entry_size(tag)
498    number(tk_send('entry', 'size', tagid(tag)))
499  end
500  def entry_size_recurse(tag)
501    number(tk_send('entry', 'size', '-recurse', tagid(tag)))
502  end
503
504  def _search_flags(keys)
505    keys = _symbolkey2str(keys)
506    keys['exact'] = None if keys.delete('exact')
507    keys['glob'] = None if keys.delete('glob')
508    keys['regexp'] = None if keys.delete('regexp')
509    keys['nonmatching'] = None if keys.delete('nonmatching')
510  end
511  private :_search_flags
512
513  ################################
514
515  class FindExecFlagValue < TkValidateCommand
516    class ValidateArgs < TkUtil::CallbackSubst
517      KEY_TBL = [
518        [ ?W, ?w, :widget ],
519        [ ?p, ?s, :name ],
520        [ ?P, ?s, :fullpath ],
521        [ ?#, ?x, :node_id ],
522        nil
523      ]
524
525      PROC_TBL = [
526        [ ?x, TkComm.method(:num_or_str) ],
527        [ ?s, TkComm.method(:string) ],
528        [ ?w, TkComm.method(:window) ],
529        nil
530      ]
531
532=begin
533      # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
534      KEY_TBL.map!{|inf|
535        if inf.kind_of?(Array)
536          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
537          inf[1] = inf[1].getbyte(0) if inf[1].kind_of?(String)
538        end
539        inf
540      }
541
542      PROC_TBL.map!{|inf|
543        if inf.kind_of?(Array)
544          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
545        end
546        inf
547      }
548=end
549
550      _setup_subst_table(KEY_TBL, PROC_TBL);
551
552      def self.ret_val(val)
553        val
554      end
555    end
556
557    def self._config_keys
558      []
559    end
560  end
561
562  def _find_exec_flag_value(val)
563    if val.kind_of?(Array)
564      cmd, *args = val
565      #FindExecFlagValue.new(cmd, args.join(' '))
566      FindExecFlagValue.new(cmd, *args)
567    elsif TkComm._callback_entry?(val)
568      FindExecFlagValue.new(val)
569    else
570      val
571    end
572  end
573
574  ################################
575
576  def find(first, last, keys={})
577    keys = _search_flags(keys)
578    keys['exec'] = _find_exec_flag_value(keys['exec']) if keys.key?('exec')
579    args = hash_kv(keys) << '--' << tagid(first) << tagid(last)
580    simplelist(tk_send('find', *args)).collect{|id| tagid2obj(id)}
581  end
582
583  def tag_focus(tag)
584    tk_send('focus', tagid(tag))
585    self
586  end
587  def get(*tags)
588    simplelist(tk_send('get', *(tags.collect{|tag| tagid(tag)})))
589  end
590  def get_full(*tags)
591    simplelist(tk_send('get', '-full', *(tags.collect{|tag| tagid(tag)})))
592  end
593
594  def hide(*tags)
595    if tags[-1].kind_of?(Hash)
596      keys = tags.pop
597    else
598      keys = {}
599    end
600    keys = _search_flags(keys)
601    args = hash_kv(keys) << '--'
602    args.concat(tags.collect{|t| tagid(t)})
603    tk_send('hide', *args)
604    self
605  end
606
607  def index(str)
608    tagid2obj(tk_send('index', str))
609  end
610  def index_at(tag, str)
611    tagid2obj(tk_send('index', '-at', tagid(tag), str))
612  end
613  def index_at_path(tag, str)
614    tagid2obj(tk_send('index', '-at', tagid(tag), '-path', str))
615  end
616
617  def insert(pos, parent=nil, keys={})
618    Tk::BLT::Treeview::Node.new(pos, parent, keys)
619  end
620  def insert_at(tag, pos, parent=nil, keys={})
621    if parent.kind_of?(Hash)
622      keys = parent
623      parent = nil
624    end
625
626    keys = _symbolkey2str(keys)
627    keys['at'] = tagid(tag)
628
629    Tk::BLT::Treeview::Node.new(pos, parent, keys)
630  end
631
632  def move_before(tag, dest)
633    tk_send('move', tagid(tag), 'before', tagid(dest))
634    self
635  end
636  def move_after(tag, dest)
637    tk_send('move', tagid(tag), 'after', tagid(dest))
638    self
639  end
640  def move_into(tag, dest)
641    tk_send('move', tagid(tag), 'into', tagid(dest))
642    self
643  end
644
645  def nearest(x, y, var=None)
646    tagid2obj(tk_send('nearest', x, y, var))
647  end
648
649  def open(*tags)
650    tk_send('open', *(tags.collect{|tag| tagid(tag)}))
651    self
652  end
653  def open_recurse(*tags)
654    tk_send('open', '-recurse', *(tags.collect{|tag| tagid(tag)}))
655    self
656  end
657
658  def range(first, last)
659    simplelist(tk_send('range', tagid(first), tagid(last))).collect{|id|
660      tagid2obj(id)
661    }
662  end
663  def range_open(first, last)
664    simplelist(tk_send('range', '-open',
665                       tagid(first), tagid(last))).collect{|id|
666      tagid2obj(id)
667    }
668  end
669
670  def scan_mark(x, y)
671    tk_send_without_enc('scan', 'mark', x, y)
672    self
673  end
674  def scan_dragto(x, y)
675    tk_send_without_enc('scan', 'dragto', x, y)
676    self
677  end
678
679  def see(tag)
680    tk_send_without_enc('see', tagid(tag))
681    self
682  end
683  def see_anchor(anchor, tag)
684    tk_send_without_enc('see', '-anchor', anchor, tagid(tag))
685    self
686  end
687
688  def selection_anchor(tag)
689    tk_send_without_enc('selection', 'anchor', tagid(tag))
690    self
691  end
692  def selection_cancel()
693    tk_send_without_enc('selection', 'cancel')
694    self
695  end
696  def selection_clear(first, last=None)
697    tk_send_without_enc('selection', 'clear', tagid(first), tagid(last))
698    self
699  end
700  def selection_clear_all()
701    tk_send_without_enc('selection', 'clearall')
702    self
703  end
704  def selection_mark(tag)
705    tk_send_without_enc('selection', 'mark', tagid(tag))
706    self
707  end
708  def selection_include?(tag)
709    bool(tk_send('selection', 'include', tagid(tag)))
710  end
711  def selection_present?()
712    bool(tk_send('selection', 'present'))
713  end
714  def selection_set(first, last=None)
715    tk_send_without_enc('selection', 'set', tagid(first), tagid(last))
716    self
717  end
718  def selection_toggle(first, last=None)
719    tk_send_without_enc('selection', 'toggle', tagid(first), tagid(last))
720    self
721  end
722
723  def show(*tags)
724    if tags[-1].kind_of?(Hash)
725      keys = tags.pop
726    else
727      keys = {}
728    end
729    keys = _search_flags(keys)
730    args = hash_kv(keys) << '--'
731    args.concat(tags.collect{|t| tagid(t)})
732    tk_send('show', *args)
733    self
734  end
735
736  def sort_auto(mode)
737    tk_send('sort', 'auto', mode)
738    self
739  end
740  def sort_auto=(mode)
741    tk_send('sort', 'auto', mode)
742    mode
743  end
744  def sort_auto?
745    bool(tk_send('sort', 'auto'))
746  end
747  def sort_once(*tags)
748    tk_send('sort', 'once', *(tags.collect{|tag| tagid(tag)}))
749    self
750  end
751  def sort_once_recurse(*tags)
752    tk_send('sort', 'once', '-recurse', *(tags.collect{|tag| tagid(tag)}))
753    self
754  end
755
756  def tag_add(tag, *ids)
757    tk_send('tag', 'add', tagid(tag), *ids)
758    self
759  end
760  def tag_delete(tag, *ids)
761    tk_send('tag', 'delete', tagid(tag), *ids)
762    self
763  end
764  def tag_forget(tag)
765    tk_send('tag', 'forget', tagid(tag))
766    self
767  end
768  def tag_names(id=nil)
769    id = (id)? tagid(id): None
770
771    simplelist(tk_send('tag', 'nodes', id)).collect{|tag|
772      Tk::BLT::Treeview::Tag.id2obj(self, tag)
773    }
774  end
775  def tag_nodes(tag)
776    simplelist(tk_send('tag', 'nodes', tagid(tag))).collect{|id|
777      Tk::BLT::Treeview::Node.id2obj(self, id)
778    }
779  end
780
781  def text_apply
782    tk_send('text', 'apply')
783    self
784  end
785  def text_cancel
786    tk_send('text', 'cancel')
787    self
788  end
789
790  def text_delete(first, last)
791    tk_send('text', 'delete', first, last)
792    self
793  end
794  def text_get(x, y)
795    tk_send('text', 'get', x, y)
796  end
797  def text_get_root(x, y)
798    tk_send('text', 'get', '-root', x, y)
799  end
800  def text_icursor(idx)
801    tk_send('text', 'icursor', idx)
802    self
803  end
804  def text_index(idx)
805    num_or_str(tk_send('text', 'index', idx))
806  end
807  def text_insert(idx, str)
808    tk_send('text', 'insert', idx, str)
809    self
810  end
811
812  def text_selection_adjust(idx)
813    tk_send('text', 'selection', 'adjust', idx)
814    self
815  end
816  def text_selection_clear
817    tk_send('text', 'selection', 'clear')
818    self
819  end
820  def text_selection_from(idx)
821    tk_send('text', 'selection', 'from', idx)
822    self
823  end
824  def text_selection_present
825    num_or_str(tk_send('text', 'selection', 'present'))
826  end
827  def text_selection_range(start, last)
828    tk_send('text', 'selection', 'range', start, last)
829    self
830  end
831  def text_selection_to(idx)
832    tk_send('text', 'selection', 'to', idx)
833    self
834  end
835
836  def toggle(tag)
837    tk_send('toggle', tagid(tag))
838    self
839  end
840end
841
842######################################
843
844module Tk::BLT::Treeview::TagOrID_Methods
845  def bbox
846    @tree.bbox(self)
847  end
848  def screen_bbox
849    @tree.screen_bbox(self)
850  end
851
852  def bind(seq, *args)
853    @tree.tag_bind(self, seq, *args)
854    self
855  end
856  def bind_append(seq, *args)
857    @tree.tag_bind_append(self, seq, *args)
858    self
859  end
860  def bind_remove(seq)
861    @tree.tag_bind_remove(self, seq)
862    self
863  end
864  def bindinfo(seq=nil)
865    @tree.tag_bindinfo(self, seq)
866  end
867
868  def button_activate
869    @tree.button_activate(self)
870    self
871  end
872
873  def button_bind(seq, *args)
874    @tree.button_bind(self, seq, *args)
875    self
876  end
877  def button_bind_append(seq, *args)
878    @tree.button_bind_append(self, seq, *args)
879    self
880  end
881  def button_bind_remove(seq)
882    @tree.button_bind_remove(self, seq)
883    self
884  end
885  def button_bindinfo(seq=nil)
886    @tree.button_bindinfo(self, seq)
887  end
888
889  def close
890    @tree.close(self)
891    self
892  end
893  def close_recurse
894    @tree.close_recurse(self)
895    self
896  end
897
898  def delete
899    @tree.delete(self)
900    self
901  end
902
903  def entry_activate
904    @tree.entry_activate(self)
905    self
906  end
907  def entry_children(first=None, last=None)
908    @tree.entry_children(self, first, last)
909  end
910  def entry_delete(first=None, last=None)
911    @tree.entry_delete(self, first, last)
912  end
913  def entry_before?(tag)
914    @tree.entry_before?(self, tag)
915  end
916  def entry_hidden?
917    @tree.entry_before?(self)
918  end
919  def entry_open?
920    @tree.entry_open?(self)
921  end
922
923  def entry_size
924    @tree.entry_size(self)
925  end
926  def entry_size_recurse
927    @tree.entry_size_recurse(self)
928  end
929
930  def focus
931    @tree.tag_focus(self)
932    self
933  end
934
935  def get
936    @tree.get(self)
937  end
938  def get_full
939    @tree.get_full(self)
940  end
941
942  def hide
943    @tree.hide(self)
944    self
945  end
946
947  def index(str)
948    @tree.index_at(self, str)
949  end
950  def index_path(str)
951    @tree.index_at_path(self, str)
952  end
953
954  def insert(pos, parent=nil, keys={})
955    @tree.insert_at(self, pos, parent, keys)
956  end
957
958  def move_before(dest)
959    @tree.move_before(self, dest)
960    self
961  end
962  def move_after(dest)
963    @tree.move_after(self, dest)
964    self
965  end
966  def move_into(dest)
967    @tree.move_into(self, dest)
968    self
969  end
970
971  def open
972    @tree.open(self)
973    self
974  end
975  def open_recurse
976    @tree.open_recurse(self)
977    self
978  end
979
980  def range_to(tag)
981    @tree.range(self, tag)
982  end
983  def range_open_to(tag)
984    @tree.range(self, tag)
985  end
986
987  def see
988    @tree.see(self)
989    self
990  end
991  def see_anchor(anchor)
992    @tree.see_anchor(anchor, self)
993    self
994  end
995
996  def selection_anchor
997    @tree.selection_anchor(self)
998    self
999  end
1000  def selection_clear
1001    @tree.selection_clear(self)
1002    self
1003  end
1004  def selection_mark
1005    @tree.selection_mark(self)
1006    self
1007  end
1008  def selection_include?
1009    @tree.selection_include?(self)
1010  end
1011  def selection_set
1012    @tree.selection_set(self)
1013    self
1014  end
1015  def selection_toggle
1016    @tree.selection_toggle(self)
1017    self
1018  end
1019
1020  def show
1021    @tree.show(self)
1022    self
1023  end
1024
1025  def sort_once
1026    @tree.sort_once(self)
1027    self
1028  end
1029  def sort_once_recurse
1030    @tree.sort_once_recurse(self)
1031    self
1032  end
1033
1034  def toggle
1035    @tree.toggle(self)
1036    self
1037  end
1038end
1039
1040######################################
1041
1042class Tk::BLT::Treeview::Node < TkObject
1043  include Tk::BLT::Treeview::TagOrID_Methods
1044
1045  TreeNodeID_TBL = TkCore::INTERP.create_table
1046
1047  (TreeNode_ID = ['blt_treeview_node'.freeze, TkUtil.untrust('00000')]).instance_eval{
1048    @mutex = Mutex.new
1049    def mutex; @mutex; end
1050    freeze
1051  }
1052
1053  TkCore::INTERP.init_ip_env{
1054    TreeNodeID_TBL.mutex.synchronize{ TreeNodeID_TBL.clear }
1055  }
1056
1057  def self.id2obj(tree, id)
1058    tpath = tree.path
1059    TreeNodeID_TBL.mutex.synchronize{
1060      if TreeNodeID_TBL[tpath]
1061        if TreeNodeID_TBL[tpath][id]
1062          TreeNodeID_TBL[tpath][id]
1063        else
1064          begin
1065            # self.new(tree, nil, nil, 'node'=>Integer(id))
1066            unless (tk_call(@tpath, 'get', id)).empty?
1067              id = Integer(id)
1068              (obj = self.allocate).instance_eval{
1069                @parent = @tree = tree
1070                @tpath = @parent.path
1071                @path = @id = id
1072                TreeNodeID_TBL[@tpath] ||= {}
1073                TreeNodeID_TBL[@tpath][@id] = self
1074              }
1075              obj
1076            else
1077              id
1078            end
1079          rescue
1080            id
1081          end
1082        end
1083      else
1084        id
1085      end
1086    }
1087  end
1088
1089  def self.new(tree, pos, parent=nil, keys={})
1090    if parent.kind_of?(Hash)
1091      keys = parent
1092      parent = nil
1093    end
1094
1095    keys = _symbolkey2str(keys)
1096    tpath = tree.path
1097
1098    TreeNodeID_TBL.mutex.synchronize{
1099      TreeNodeID_TBL[tpath] ||= {}
1100      if (id = keys['node']) && (obj = TreeNodeID_TBL[tpath][id])
1101        keys.delete('node')
1102        tk_call(tree.path, 'move', id, pos, parent) if parent
1103        return obj
1104      end
1105
1106      #super(tree, pos, parent, keys)
1107      (obj = self.allocate).instance_eval{
1108        initialize(tree, pos, parent, keys)
1109        TreeNodeID_TBL[tpath][@id] = self
1110      }
1111      obj
1112    }
1113  end
1114
1115  def initialize(tree, pos, parent, keys)
1116    @parent = @tree = tree
1117    @tpath = @parent.path
1118
1119    if (id = keys['node'])
1120      # if tk_call(@tpath, 'get', id).empty?
1121      #   fail RuntimeError, "not exist the node '#{id}'"
1122      # end
1123      @path = @id = id
1124      tk_call(@tpath, 'move', @id, pos, tagid(parent)) if parent
1125      configure(keys) if keys && ! keys.empty?
1126    else
1127      name = nil
1128      TreeNode_ID.mutex.synchronize{
1129        name = TreeNode_ID.join(TkCore::INTERP._ip_id_).freeze
1130        TreeNode_ID[1].succ!
1131      }
1132
1133      at = keys.delete['at']
1134
1135      if parent
1136        if parent.kind_of?(Tk::BLT::Treeview::Node) ||
1137            parent.kind_of?(Tk::BLT::Treeview::Tag)
1138          path = [get_full(parent.id)[0], name]
1139          at = nil # ignore 'at' option
1140        else
1141          path = [parent.to_s, name]
1142        end
1143      else
1144        path = name
1145      end
1146
1147      if at
1148        @id = tk_call(@tpath, 'insert', '-at', tagid(at), pos, path, keys)
1149      else
1150        @id = tk_call(@tpath, 'insert', pos, path, keys)
1151      end
1152      @path = @id
1153    end
1154  end
1155
1156  def id
1157    @id
1158  end
1159end
1160
1161######################################
1162
1163class Tk::BLT::Treeview::Tag < TkObject
1164  include Tk::BLT::Treeview::TagOrID_Methods
1165
1166  TreeTagID_TBL = TkCore::INTERP.create_table
1167
1168  (TreeTag_ID = ['blt_treeview_tag'.freeze, TkUtil.untrust('00000')]).instance_eval{
1169    @mutex = Mutex.new
1170    def mutex; @mutex; end
1171    freeze
1172  }
1173
1174  TkCore::INTERP.init_ip_env{
1175    TreeTagID_TBL.mutex.synchronize{ TreeTagID_TBL.clear }
1176  }
1177
1178  def self.id2obj(tree, name)
1179    tpath = tree.path
1180    TreeTagID_TBL.mutex.synchronize{
1181      if TreeTagID_TBL[tpath]
1182        if TreeTagID_TBL[tpath][name]
1183          TreeTagID_TBL[tpath][name]
1184        else
1185          #self.new(tree, name)
1186          (obj = self.allocate).instance_eval{
1187            @parent = @tree = tree
1188            @tpath = @parent.path
1189            @path = @id = name
1190            TreeTagID_TBL[@tpath] = {} unless TreeTagID_TBL[@tpath]
1191            TreeTagID_TBL[@tpath][@id] = self
1192          }
1193          obj
1194        end
1195      else
1196        id
1197      end
1198    }
1199  end
1200
1201  def self.new_by_name(tree, name, *ids)
1202    TreeTagID_TBL.mutex.synchronize{
1203      unless (obj = TreeTagID_TBL[tree.path][name])
1204        (obj = self.allocate).instance_eval{
1205          initialize(tree, name, ids)
1206          TreeTagID_TBL[@tpath] = {} unless TreeTagID_TBL[@tpath]
1207          TreeTagID_TBL[@tpath][@id] = self
1208        }
1209      end
1210      obj
1211    }
1212  end
1213
1214  def self.new(tree, *ids)
1215    TreeTagID_TBL.mutex.synchronize{
1216      (obj = self.allocate).instance_eval{
1217        if tree.kind_of?(Array)
1218          initialize(tree[0], tree[1], ids)
1219        else
1220          initialize(tree, nil, ids)
1221        end
1222        TreeTagID_TBL[@tpath] = {} unless TreeTagID_TBL[@tpath]
1223        TreeTagID_TBL[@tpath][@id] = self
1224      }
1225      obj
1226    }
1227  end
1228
1229  def initialize(tree, name, ids)
1230    @parent = @tree = tree
1231    @tpath = @parent.path
1232
1233    if name
1234      @path = @id = name
1235    else
1236      TreeTag_ID.mutex.synchronize{
1237        @path = @id = TreeTag_ID.join(TkCore::INTERP._ip_id_).freeze
1238        TreeTag_ID[1].succ!
1239      }
1240    end
1241
1242    unless ids.empty?
1243      tk_call(@tpath, 'tag', 'add', @id, *(ids.collect{|id| tagid(id)}))
1244    end
1245  end
1246
1247  def tagid(tag)
1248    if tag.kind_of?(Tk::BLT::Treeview::Node) \
1249      || tag.kind_of?(Tk::BLT::Treeview::Tag)
1250      tag.id
1251    else
1252      tag
1253    end
1254  end
1255  private :tagid
1256
1257  def id
1258    @id
1259  end
1260
1261  def add(*ids)
1262    tk_call(@tpath, 'tag', 'add', @id, *(ids{|id| tagid(id)}))
1263    self
1264  end
1265
1266  def remove(*ids)
1267    tk_call(@tpath, 'tag', 'delete', @id, *(ids{|id| tagid(id)}))
1268    self
1269  end
1270
1271  def forget
1272    tk_call(@tpath, 'tag', 'forget', @id)
1273    self
1274  end
1275
1276  def nodes
1277    simplelist(tk_call(@tpath, 'tag', 'nodes', @id)).collect{|id|
1278      Tk::BLT::Treeview::Node.id2obj(@tree, id)
1279    }
1280  end
1281end
1282
1283class Tk::BLT::Hiertable
1284  TkCommandNames = ['::blt::hiertable'.freeze].freeze
1285  WidgetClassName = 'Hiertable'.freeze
1286  WidgetClassNames[WidgetClassName] ||= self
1287end
1288