From fcbf63e62c627deae76c1b8cb8c0876c536ed811 Mon Sep 17 00:00:00 2001
From: Jari Vetoniemi <jari.vetoniemi@indooratlas.com>
Date: Mon, 16 Mar 2020 18:49:26 +0900
Subject: Fresh start

---
 jni/ruby/ext/tk/lib/tkextlib/blt/barchart.rb       |   79 +
 jni/ruby/ext/tk/lib/tkextlib/blt/bitmap.rb         |  112 +
 jni/ruby/ext/tk/lib/tkextlib/blt/busy.rb           |   83 +
 jni/ruby/ext/tk/lib/tkextlib/blt/component.rb      | 2218 ++++++++++++++++++++
 jni/ruby/ext/tk/lib/tkextlib/blt/container.rb      |   28 +
 jni/ruby/ext/tk/lib/tkextlib/blt/cutbuffer.rb      |   23 +
 jni/ruby/ext/tk/lib/tkextlib/blt/dragdrop.rb       |  269 +++
 jni/ruby/ext/tk/lib/tkextlib/blt/eps.rb            |   32 +
 jni/ruby/ext/tk/lib/tkextlib/blt/graph.rb          |   67 +
 jni/ruby/ext/tk/lib/tkextlib/blt/htext.rb          |  112 +
 jni/ruby/ext/tk/lib/tkextlib/blt/setup.rb          |    8 +
 jni/ruby/ext/tk/lib/tkextlib/blt/spline.rb         |   23 +
 jni/ruby/ext/tk/lib/tkextlib/blt/stripchart.rb     |   74 +
 jni/ruby/ext/tk/lib/tkextlib/blt/table.rb          |  412 ++++
 jni/ruby/ext/tk/lib/tkextlib/blt/tabnotebook.rb    |  110 +
 jni/ruby/ext/tk/lib/tkextlib/blt/tabset.rb         |  504 +++++
 jni/ruby/ext/tk/lib/tkextlib/blt/ted.rb            |   68 +
 jni/ruby/ext/tk/lib/tkextlib/blt/tile.rb           |   25 +
 jni/ruby/ext/tk/lib/tkextlib/blt/tile/button.rb    |   16 +
 .../ext/tk/lib/tkextlib/blt/tile/checkbutton.rb    |   17 +
 jni/ruby/ext/tk/lib/tkextlib/blt/tile/frame.rb     |   16 +
 jni/ruby/ext/tk/lib/tkextlib/blt/tile/label.rb     |   16 +
 .../ext/tk/lib/tkextlib/blt/tile/radiobutton.rb    |   17 +
 jni/ruby/ext/tk/lib/tkextlib/blt/tile/scrollbar.rb |   16 +
 jni/ruby/ext/tk/lib/tkextlib/blt/tile/toplevel.rb  |   16 +
 jni/ruby/ext/tk/lib/tkextlib/blt/tree.rb           | 1058 ++++++++++
 jni/ruby/ext/tk/lib/tkextlib/blt/treeview.rb       | 1287 ++++++++++++
 jni/ruby/ext/tk/lib/tkextlib/blt/unix_dnd.rb       |  141 ++
 jni/ruby/ext/tk/lib/tkextlib/blt/vector.rb         |  256 +++
 jni/ruby/ext/tk/lib/tkextlib/blt/watch.rb          |  175 ++
 jni/ruby/ext/tk/lib/tkextlib/blt/win_printer.rb    |   61 +
 jni/ruby/ext/tk/lib/tkextlib/blt/winop.rb          |  107 +
 32 files changed, 7446 insertions(+)
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/barchart.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/bitmap.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/busy.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/component.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/container.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/cutbuffer.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/dragdrop.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/eps.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/graph.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/htext.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/setup.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/spline.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/stripchart.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/table.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/tabnotebook.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/tabset.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/ted.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/tile.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/tile/button.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/tile/checkbutton.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/tile/frame.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/tile/label.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/tile/radiobutton.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/tile/scrollbar.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/tile/toplevel.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/tree.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/treeview.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/unix_dnd.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/vector.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/watch.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/win_printer.rb
 create mode 100644 jni/ruby/ext/tk/lib/tkextlib/blt/winop.rb

(limited to 'jni/ruby/ext/tk/lib/tkextlib/blt')

diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/barchart.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/barchart.rb
new file mode 100644
index 0000000..a86b91c
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/barchart.rb
@@ -0,0 +1,79 @@
+#
+#  tkextlib/blt/barchart.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+require 'tkextlib/blt/component.rb'
+
+module Tk::BLT
+  class Barchart < TkWindow
+    TkCommandNames = ['::blt::barchart'.freeze].freeze
+    WidgetClassName = 'Barchart'.freeze
+    WidgetClassNames[WidgetClassName] ||= self
+
+    include PlotComponent
+    include GraphCommand
+
+    def __boolval_optkeys
+      ['bufferelements', 'buffergraph', 'invertxy']
+    end
+    private :__boolval_optkeys
+
+    def __strval_optkeys
+      ['text', 'label', 'title', 'file',
+        'background', 'plotbackground']
+    end
+    private :__strval_optkeys
+
+    def __tkvariable_optkeys
+      super() << 'colormap' << 'fontmap'
+    end
+    private :__tkvariable_optkeys
+
+=begin
+    BarElement_ID = ['blt_barchart_bar'.freeze, TkUtil.untrust('00000')].freeze
+
+    def bar(elem=nil, keys={})
+      if elem.kind_of?(Hash)
+        keys = elem
+        elem = nil
+      end
+      unless elem
+        elem = BarElement_ID.join(TkCore::INTERP._ip_id_).freeze
+        BarElement_ID[1].succ!
+      end
+      tk_send('bar', elem, keys)
+      Element.new(self, elem, :without_creating=>true)
+    end
+=end
+
+    def extents(item)
+      num_or_str(tk_send_without_enc('extents', item))
+    end
+
+    def invtransform(x, y)
+      list(tk_send_without_enc('invtransform', x, y))
+    end
+
+    def inside(x, y)
+      bool(tk_send_without_enc('inside', x, y))
+    end
+
+    def metafile(file=None)
+      # Windows only
+      tk_send('metafile', file)
+      self
+    end
+
+    def snap(output, keys={})
+      tk_send_without_enc('snap', *(hash_kv(keys, false) + output))
+      self
+    end
+
+    def transform(x, y)
+      list(tk_send_without_enc('transform', x, y))
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/bitmap.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/bitmap.rb
new file mode 100644
index 0000000..3254b63
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/bitmap.rb
@@ -0,0 +1,112 @@
+#
+#  tkextlib/blt/bitmap.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  class Bitmap < TkObject
+    extend TkCore
+
+    TkCommandNames = ['::blt::bitmap'.freeze].freeze
+
+    BITMAP_ID_TBL = TkCore::INTERP.create_table
+
+    (BITMAP_ID = ['blt_bitmap_id'.freeze, TkUtil.untrust('00000')]).instance_eval{
+      @mutex = Mutex.new
+      def mutex; @mutex; end
+      freeze
+    }
+
+    TkCore::INTERP.init_ip_env{
+      BITMAP_ID_TBL.mutex.synchronize{ BITMAP_ID_TBL.clear }
+    }
+
+    def self.data(name)
+      dat = tk_simple_list(tk_call('::blt::bitmap', 'data', name))
+      [ tk_split_list(dat[0]), tk_simple_list(dat[1]) ]
+    end
+
+    def self.exist?(name)
+      bool(tk_call('::blt::bitmap', 'exists', name))
+    end
+
+    def self.height(name)
+      number(tk_call('::blt::bitmap', 'height', name))
+    end
+
+    def self.width(name)
+      number(tk_call('::blt::bitmap', 'width', name))
+    end
+
+    def self.source(name)
+      tk_simple_list(tk_call('::blt::bitmap', 'source', name))
+    end
+
+    #################################
+
+    class << self
+      alias _new new
+
+      def new(data, keys={})
+        _new(:data, nil, data, keys)
+      end
+      alias define new
+
+      def new_with_name(name, data, keys={})
+        _new(:data, name, data, keys)
+      end
+      alias define_with_name new_with_name
+
+      def compose(text, keys={})
+        _new(:text, nil, text, keys)
+      end
+
+      def compose_with_name(name, text, keys={})
+        _new(:text, name, text, keys)
+      end
+    end
+
+    def initialize(type, name, data, keys = {})
+      if name
+        @id = name
+      else
+        BITMAP_ID.mutex.synchronize{
+          @id = BITMAP_ID.join(TkCore::INTERP._ip_id_)
+          BITMAP_ID[1].succ!
+        }
+        BITMAP_ID_TBL.mutex.synchronize{
+          BITMAP_ID_TBL[@id] = self
+        }
+      end
+
+      @path = @id
+
+      unless bool(tk_call('::blt::bitmap', 'exists', @id))
+        if type == :text
+          tk_call('::blt::bitmap', 'compose', @id, data, *hash_kv(keys))
+        else # :data
+          tk_call('::blt::bitmap', 'define', @id, data, *hash_kv(keys))
+        end
+      end
+    end
+
+    def exist?
+      bool(tk_call('::blt::bitmap', 'exists', @id))
+    end
+
+    def height
+      number(tk_call('::blt::bitmap', 'height', @id))
+    end
+
+    def width
+      number(tk_call('::blt::bitmap', 'width', @id))
+    end
+
+    def source
+      tk_simple_list(tk_call('::blt::bitmap', 'source', @id))
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/busy.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/busy.rb
new file mode 100644
index 0000000..b5287fb
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/busy.rb
@@ -0,0 +1,83 @@
+#
+#  tkextlib/blt/busy.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tk/itemconfig.rb'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  module Busy
+    extend TkCore
+    extend TkItemConfigMethod
+
+    TkCommandNames = ['::blt::busy'.freeze].freeze
+
+    ###########################
+
+    class Shield < TkWindow
+      def self.shield_path(win)
+        win = window(win) unless win.kind_of?(TkWindow)
+        if win.kind_of?(Tk::Toplevel)
+          win.path + '._Busy'
+        else
+          win.path + '_Busy'
+        end
+      end
+
+      def initialize(win)
+        @path = self.class.shield_path(win)
+      end
+    end
+
+    def self.shield_path(win)
+      Tk::BLT::Busy::Shield.shield_path(win)
+    end
+  end
+end
+
+class << Tk::BLT::Busy
+  def __item_config_cmd(win)
+    ['::blt::busy', 'configure', win]
+  end
+  private :__item_config_cmd
+
+  undef itemcget
+  undef itemcget_tkstring
+  alias configure itemconfigure
+  alias configinfo itemconfiginfo
+  alias current_configinfo current_itemconfiginfo
+  private :itemconfigure, :itemconfiginfo, :current_itemconfiginfo
+
+  ##################################
+
+  def hold(win, keys={})
+    tk_call('::blt::busy', 'hold', win, *hash_kv(keys))
+  end
+
+  def release(*wins)
+    tk_call('::blt::busy', 'release', *wins)
+  end
+
+  def forget(*wins)
+    tk_call('::blt::busy', 'forget', *wins)
+  end
+
+  def is_busy(pat=None)
+    tk_split_list(tk_call('::blt::busy', 'isbusy', pat))
+  end
+
+  def names(pat=None)
+    tk_split_list(tk_call('::blt::busy', 'names', pat))
+  end
+  alias windows names
+
+  def check(win)
+    bool(tk_call('::blt::busy', 'check', win))
+  end
+
+  def status(win)
+    bool(tk_call('::blt::busy', 'status', win))
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/component.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/component.rb
new file mode 100644
index 0000000..c7ea213
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/component.rb
@@ -0,0 +1,2218 @@
+#
+#  tkextlib/blt/component.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  module PlotComponent
+    include TkItemConfigMethod
+
+    module OptKeys
+      def __item_font_optkeys(id)
+        ['font', 'tickfont', 'titlefont']
+      end
+      private :__item_font_optkeys
+
+      def __item_numstrval_optkeys(id)
+        ['xoffset', 'yoffset']
+      end
+      private :__item_numstrval_optkeys
+
+      def __item_boolval_optkeys(id)
+        ['hide', 'under', 'descending', 'logscale', 'loose', 'showticks',
+          'titlealternate', 'scalesymbols', 'minor', 'raised',
+          'center', 'decoration', 'landscape', 'maxpect']
+      end
+      private :__item_boolval_optkeys
+
+      def __item_strval_optkeys(id)
+        ['text', 'label', 'limits', 'title',
+          'show', 'file', 'maskdata', 'maskfile',
+          'color', 'titlecolor', 'fill', 'outline', 'offdash']
+      end
+      private :__item_strval_optkeys
+
+      def __item_listval_optkeys(id)
+        ['bindtags']
+      end
+      private :__item_listval_optkeys
+
+      def __item_numlistval_optkeys(id)
+        ['dashes', 'majorticks', 'minorticks']
+      end
+      private :__item_numlistval_optkeys
+
+      def __item_tkvariable_optkeys(id)
+        ['variable', 'textvariable', 'colormap', 'fontmap']
+      end
+      private :__item_tkvariable_optkeys
+    end
+
+    include OptKeys
+
+    def __item_cget_cmd(id)
+      if id.kind_of?(Array)
+        # id := [ type, name ]
+        [self.path, id[0], 'cget', id[1]]
+      else
+        [self.path, id, 'cget']
+      end
+    end
+    private :__item_cget_cmd
+
+    def __item_config_cmd(id)
+      if id.kind_of?(Array)
+        # id := [ type, name, ... ]
+        type, *names = id
+        [self.path, type, 'configure'].concat(names)
+      else
+        [self.path, id, 'configure']
+      end
+    end
+    private :__item_config_cmd
+
+    def __item_pathname(id)
+      if id.kind_of?(Array)
+        id = tagid(id[1])
+      end
+      [self.path, id].join(';')
+    end
+    private :__item_pathname
+
+    def axis_cget_tkstring(id, option)
+      ret = itemcget_tkstring(['axis', tagid(id)], option)
+    end
+    def axis_cget(id, option)
+      ret = itemcget(['axis', tagid(id)], option)
+    end
+    def axis_cget_strict(id, option)
+      ret = itemcget_strict(['axis', tagid(id)], option)
+    end
+    def axis_configure(*args)
+      slot = args.pop
+      if slot.kind_of?(Hash)
+        value = None
+        slot = _symbolkey2str(slot)
+        if cmd = slot.delete('command')
+          slot['command'] = proc{|w, tick|
+            cmd.call(TkComm.window(w), TkComm.num_or_str(tick))
+          }
+        end
+      else
+        value = slot
+        slot = args.pop
+        if slot == :command || slot == 'command'
+          cmd = value
+          value = proc{|w, tick|
+            cmd.call(TkComm.window(w), TkComm.num_or_str(tick))
+          }
+        end
+      end
+      id_list = args.flatten.collect!{|id| tagid(id)}.unshift('axis')
+      itemconfigure(id_list, slot, value)
+    end
+    def axis_configinfo(id, slot=nil)
+      itemconfiginfo(['axis', tagid(id)], slot)
+    end
+    def current_axis_configinfo(id, slot=nil)
+      current_itemconfiginfo(['axis', tagid(id)], slot)
+    end
+
+    def crosshairs_cget_tkstring(option)
+      itemcget_tkstring('crosshairs', option)
+    end
+    def crosshairs_cget(option)
+      itemcget('crosshairs', option)
+    end
+    def crosshairs_cget_strict(option)
+      itemcget_strict('crosshairs', option)
+    end
+    def crosshairs_configure(slot, value=None)
+      itemconfigure('crosshairs', slot, value)
+    end
+    def crosshairs_configinfo(slot=nil)
+      itemconfiginfo('crosshairs', slot)
+    end
+    def current_crosshairs_configinfo(slot=nil)
+      current_itemconfiginfo('crosshairs', slot)
+    end
+
+    def element_cget_tkstring(id, option)
+      itemcget_tkstring(['element', tagid(id)], option)
+    end
+    def element_cget(id, option)
+      itemcget(['element', tagid(id)], option)
+    end
+    def element_cget_strict(id, option)
+      itemcget_strict(['element', tagid(id)], option)
+    end
+    def element_configure(*args)
+      slot = args.pop
+      if slot.kind_of?(Hash)
+        value = None
+      else
+        value = slot
+        slot = args.pop
+      end
+      id_list = args.flatten.collect!{|id| tagid(id)}.unshift('element')
+      itemconfigure(id_list, slot, value)
+    end
+    def element_configinfo(id, slot=nil)
+      itemconfiginfo(['element', tagid(id)], slot)
+    end
+    def current_element_configinfo(id, slot=nil)
+      current_itemconfiginfo(['element', tagid(id)], slot)
+    end
+
+    def bar_cget_tkstring(id, option)
+      itemcget_tkstring(['bar', tagid(id)], option)
+    end
+    def bar_cget(id, option)
+      itemcget(['bar', tagid(id)], option)
+    end
+    def bar_cget_strict(id, option)
+      itemcget_strict(['bar', tagid(id)], option)
+    end
+    def bar_configure(*args)
+      slot = args.pop
+      if slot.kind_of?(Hash)
+        value = None
+      else
+        value = slot
+        slot = args.pop
+      end
+      id_list = args.flatten.collect!{|id| tagid(id)}.unshift('bar')
+      itemconfigure(id_list, slot, value)
+    end
+    def bar_configinfo(id, slot=nil)
+      itemconfiginfo(['bar', tagid(id)], slot)
+    end
+    def current_bar_configinfo(id, slot=nil)
+      current_itemconfiginfo(['bar', tagid(id)], slot)
+    end
+
+    def line_cget_tkstring(id, option)
+      itemcget_tkstring(['line', tagid(id)], option)
+    end
+    def line_cget(id, option)
+      itemcget(['line', tagid(id)], option)
+    end
+    def line_cget_strict(id, option)
+      itemcget_strict(['line', tagid(id)], option)
+    end
+    def line_configure(*args)
+      slot = args.pop
+      if slot.kind_of?(Hash)
+        value = None
+      else
+        value = slot
+        slot = args.pop
+      end
+      id_list = args.flatten.collect!{|id| tagid(id)}.unshift('line')
+      itemconfigure(id_list, slot, value)
+    end
+    def line_configinfo(id, slot=nil)
+      itemconfiginfo(['line', tagid(id)], slot)
+    end
+    def current_line_configinfo(id, slot=nil)
+      current_itemconfiginfo(['line', tagid(id)], slot)
+    end
+
+    def gridline_cget_tkstring(option)
+      itemcget_tkstring('grid', option)
+    end
+    def gridline_cget(option)
+      itemcget('grid', option)
+    end
+    def gridline_cget_strict(option)
+      itemcget_strict('grid', option)
+    end
+    def gridline_configure(slot, value=None)
+      itemconfigure('grid', slot, value)
+    end
+    def gridline_configinfo(slot=nil)
+      itemconfiginfo('grid', slot)
+    end
+    def current_gridline_configinfo(slot=nil)
+      current_itemconfiginfo('grid', slot)
+    end
+
+    def legend_cget_tkstring(option)
+      itemcget_tkstring('legend', option)
+    end
+    def legend_cget(option)
+      itemcget('legend', option)
+    end
+    def legend_cget_strict(option)
+      itemcget_strict('legend', option)
+    end
+    def legend_configure(slot, value=None)
+      itemconfigure('legend', slot, value)
+    end
+    def legend_configinfo(slot=nil)
+      itemconfiginfo('legend', slot)
+    end
+    def current_legend_configinfo(slot=nil)
+      current_itemconfiginfo('legend', slot)
+    end
+
+    def pen_cget_tkstring(id, option)
+      itemcget_tkstring(['pen', tagid(id)], option)
+    end
+    def pen_cget(id, option)
+      itemcget(['pen', tagid(id)], option)
+    end
+    def pen_cget_strict(id, option)
+      itemcget_strict(['pen', tagid(id)], option)
+    end
+    def pen_configure(*args)
+      slot = args.pop
+      if slot.kind_of?(Hash)
+        value = None
+      else
+        value = slot
+        slot = args.pop
+      end
+      id_list = args.flatten.collect!{|id| tagid(id)}.unshift('pen')
+      itemconfigure(id_list, slot, value)
+    end
+    def pen_configinfo(id, slot=nil)
+      itemconfiginfo(['pen', tagid(id)], slot)
+    end
+    def current_pen_configinfo(id, slot=nil)
+      current_itemconfiginfo(['pen', tagid(id)], slot)
+    end
+
+    def postscript_cget_tkstring(option)
+      itemcget_tkstring('postscript', option)
+    end
+    def postscript_cget(option)
+      itemcget('postscript', option)
+    end
+    def postscript_cget_strict(option)
+      itemcget_strict('postscript', option)
+    end
+    def postscript_configure(slot, value=None)
+      itemconfigure('postscript', slot, value)
+    end
+    def postscript_configinfo(slot=nil)
+      itemconfiginfo('postscript', slot)
+    end
+    def current_postscript_configinfo(slot=nil)
+      current_itemconfiginfo('postscript', slot)
+    end
+
+    def marker_cget_tkstring(id, option)
+      itemcget_tkstring(['marker', tagid(id)], option)
+    end
+    def marker_cget(id, option)
+      itemcget(['marker', tagid(id)], option)
+    end
+    def marker_cget_strict(id, option)
+      itemcget_strict(['marker', tagid(id)], option)
+    end
+    def marker_configure(*args)
+      slot = args.pop
+      if slot.kind_of?(Hash)
+        value = None
+      else
+        value = slot
+        slot = args.pop
+      end
+      id_list = args.flatten.collect!{|id| tagid(id)}.unshift('marker')
+      itemconfigure(id_list, slot, value)
+    end
+    def marker_configinfo(id, slot=nil)
+      itemconfiginfo(['marker', tagid(id)], slot)
+    end
+    def current_marker_configinfo(id, slot=nil)
+      current_itemconfiginfo(['marker', tagid(id)], slot)
+    end
+
+    alias __itemcget_tkstring itemcget_tkstring
+    alias __itemcget itemcget
+    alias __itemcget_strict itemcget_strict
+    alias __itemconfiginfo itemconfiginfo
+    alias __current_itemconfiginfo current_itemconfiginfo
+    private :__itemcget_tkstring, :__itemcget, :__itemconfiginfo, :__current_itemconfiginfo
+
+    def itemcget_tkstring(tagOrId, option)
+      __itemcget_tkstring(tagid(tagOrId), option)
+    end
+    def itemcget_strict(tagOrId, option)
+      ret = __itemcget(tagid(tagOrId), option)
+      if option == 'bindtags' || option == :bindtags
+        ret.collect{|tag| TkBindTag.id2obj(tag)}
+      else
+        ret
+      end
+    end
+    def itemcget(tagOrId, option)
+      unless TkItemConfigMethod.__IGNORE_UNKNOWN_CONFIGURE_OPTION__
+        itemcget_strict(tagOrId, option)
+      else
+        begin
+          itemcget_strict(tagOrId, option)
+        rescue => e
+          begin
+            if current_itemconfiginfo(tagOrId).has_key?(option.to_s)
+              # error on known option
+              fail e
+            else
+              # unknown option
+              nil
+            end
+          rescue
+            fail e  # tag error
+          end
+        end
+      end
+    end
+    def itemconfiginfo(tagOrId, slot = nil)
+      ret = __itemconfiginfo(tagid(tagOrId), slot)
+
+      if TkComm::GET_CONFIGINFO_AS_ARRAY
+        if slot
+          if slot == 'bindtags' || slot == :bindtags
+            ret[-2] = ret[-2].collect{|tag| TkBindTag.id2obj(tag)}
+            ret[-1] = ret[-1].collect{|tag| TkBindTag.id2obj(tag)}
+          end
+        else
+          if (inf = ret.assoc('bindtags'))
+            inf[-2] = inf[-2].collect{|tag| TkBindTag.id2obj(tag)}
+            inf[-1] = inf[-1].collect{|tag| TkBindTag.id2obj(tag)}
+          end
+        end
+
+      else # ! TkComm::GET_CONFIGINFO_AS_ARRAY
+        if (inf = ret['bindtags'])
+          inf[-2] = inf[-2].collect{|tag| TkBindTag.id2obj(tag)}
+          inf[-1] = inf[-1].collect{|tag| TkBindTag.id2obj(tag)}
+          ret['bindtags'] = inf
+        end
+      end
+
+      ret
+    end
+    def current_itemconfiginfo(tagOrId, slot = nil)
+      ret = __current_itemconfiginfo(tagid(tagOrId), slot)
+
+      if (val = ret['bindtags'])
+        ret['bindtags'] = val.collect{|tag| TkBindTag.id2obj(tag)}
+      end
+
+      ret
+    end
+
+    private :itemcget_tkstring, :itemcget, :itemcget_strict
+    private :itemconfigure, :itemconfiginfo, :current_itemconfiginfo
+
+    #################
+
+    class Axis < TkObject
+      (OBJ_ID = ['blt_chart_axis'.freeze, TkUtil.untrust('00000')]).instance_eval{
+        @mutex = Mutex.new
+        def mutex; @mutex; end
+        freeze
+      }
+
+      AxisID_TBL = TkCore::INTERP.create_table
+
+      TkCore::INTERP.init_ip_env{
+        AxisID_TBL.mutex.synchronize{ AxisID_TBL.clear }
+      }
+
+      def self.id2obj(chart, id)
+        cpath = chart.path
+        AxisID_TBL.mutex.synchronize{
+          return id unless AxisID_TBL[cpath]
+          AxisID_TBL[cpath][id]? AxisID_TBL[cpath][id]: id
+        }
+      end
+
+      def self.new(chart, axis=nil, keys={})
+        if axis.kind_of?(Hash)
+          keys = axis
+          axis = nil
+        end
+        if keys
+          keys = _symbolkey2str(keys)
+          not_create = keys.delete('without_creating')
+        else
+          not_create = false
+        end
+
+        obj = nil
+        AxisID_TBL.mutex.synchronize{
+          chart_path = chart.path
+          AxisID_TBL[chart_path] ||= {}
+          if axis && AxisID_TBL[chart_path][axis]
+            obj = AxisID_TBL[chart_path][axis]
+          else
+            (obj = self.allocate).instance_eval{
+              if axis
+                @axis = @id = axis.to_s
+              else
+                OBJ_ID.mutex.synchronize{
+                  @axis = @id = OBJ_ID.join(TkCore::INTERP._ip_id_).freeze
+                  OBJ_ID[1].succ!
+                }
+              end
+              @path = @id
+              @parent = @chart = chart
+              @cpath = @chart.path
+              Axis::AxisID_TBL[@cpath][@axis] = self
+              unless not_create
+                tk_call(@chart, 'axis', 'create', @axis, keys)
+                return obj
+              end
+            }
+          end
+        }
+
+        obj.configure(keys) if obj && ! keys.empty?
+        obj
+      end
+
+      def initialize(chart, axis=nil, keys={})
+        # dummy:: not called by 'new' method
+
+        if axis.kind_of?(Hash)
+          keys = axis
+          axis = nil
+        end
+        if axis
+          @axis = @id = axis.to_s
+        else
+          OBJ_ID.mutex.synchronize{
+            @axis = @id = OBJ_ID.join(TkCore::INTERP._ip_id_).freeze
+            OBJ_ID[1].succ!
+          }
+        end
+        @path = @id
+        @parent = @chart = chart
+        @cpath = @chart.path
+        # Axis::AxisID_TBL[@cpath][@axis] = self
+        keys = _symbolkey2str(keys)
+        unless keys.delete('without_creating')
+          # @chart.axis_create(@axis, keys)
+          tk_call(@chart, 'axis', 'create', @axis, keys)
+        end
+      end
+
+      def id
+        @id
+      end
+
+      def to_eval
+        @id
+      end
+
+      def cget_tkstring(option)
+        @chart.axis_cget_tkstring(@id, option)
+      end
+      def cget(option)
+        @chart.axis_cget(@id, option)
+      end
+      def cget_strict(option)
+        @chart.axis_cget_strict(@id, option)
+      end
+      def configure(key, value=None)
+        @chart.axis_configure(@id, key, value)
+        self
+      end
+      def configinfo(key=nil)
+        @chart.axis_configinfo(@id, key)
+      end
+      def current_configinfo(key=nil)
+        @chart.current_axis_configinfo(@id, key)
+      end
+
+      def command(cmd=nil, &b)
+        if cmd
+          configure('command', cmd)
+        elsif b
+          configure('command', Proc.new(&b))
+        else
+          cget('command')
+        end
+      end
+
+      def delete
+        @chart.axis_delete(@id)
+        self
+      end
+
+      def invtransform(val)
+        @chart.axis_invtransform(@id, val)
+      end
+
+      def limits
+        @chart.axis_limits(@id)
+      end
+
+      def name
+        @axis
+      end
+
+      def transform(val)
+        @chart.axis_transform(@id, val)
+      end
+
+      def view
+        @chart.axis_view(@id)
+        self
+      end
+
+      def use(name=None) # if @id == xaxis | x2axis | yaxis | y2axis
+        @chart.axis_use(@id, name)
+      end
+
+      def use_as(axis) # axis := xaxis | x2axis | yaxis | y2axis
+        @chart.axis_use(axis, @id)
+      end
+    end
+
+    #################
+
+    class Crosshairs < TkObject
+      CrosshairsID_TBL = TkCore::INTERP.create_table
+
+      TkCore::INTERP.init_ip_env{
+        CrosshairsID_TBL.mutex.synchronize{ CrosshairsID_TBL.clear }
+      }
+
+      def self.new(chart, keys={})
+        obj = nil
+        CrosshairsID_TBL.mutex.synchronize{
+          unless (obj = CrosshairsID_TBL[chart.path])
+            (obj = self.allocate).instance_eval{
+              @parent = @chart = chart
+              @cpath = @chart.path
+              @path = @id = 'crosshairs'
+              Crosshairs::CrosshairsID_TBL[@cpath] = self
+            }
+          end
+        }
+        chart.crosshair_configure(keys) if obj && ! keys.empty?
+        obj
+      end
+
+      def initialize(chart, keys={})
+        # dummy:: not called by 'new' method
+
+        @parent = @chart = chart
+        @cpath = @chart.path
+        # Crosshairs::CrosshairsID_TBL[@cpath] = self
+        @chart.crosshair_configure(keys) unless keys.empty?
+        @path = @id = 'crosshairs'
+      end
+
+      def id
+        @id
+      end
+
+      def to_eval
+        @id
+      end
+
+      def cget_tkstring(option)
+        @chart.crosshair_cget_tkstring(option)
+      end
+      def cget(option)
+        @chart.crosshair_cget(option)
+      end
+      def cget_strict(option)
+        @chart.crosshair_cget_strict(option)
+      end
+      def configure(key, value=None)
+        @chart.crosshair_configure(key, value)
+        self
+      end
+      def configinfo(key=nil)
+        @chart.crosshair_configinfo(key)
+      end
+      def current_configinfo(key=nil)
+        @chart.current_crosshair_configinfo(key)
+      end
+
+      def off
+        @chart.crosshair_off
+        self
+      end
+      def on
+        @chart.crosshair_on
+        self
+      end
+      def toggle
+        @chart.crosshair_toggle
+        self
+      end
+    end
+
+    #################
+
+    class Element < TkObject
+      extend Tk
+      extend TkItemFontOptkeys
+      extend TkItemConfigOptkeys
+
+      extend Tk::BLT::PlotComponent::OptKeys
+
+      ElementTypeName = 'element'
+      ElementTypeToClass = { ElementTypeName=>self }
+
+      ElementID_TBL = TkCore::INTERP.create_table
+
+      TkCore::INTERP.init_ip_env{
+        ElementID_TBL.mutex.synchronize{ ElementID_TBL.clear }
+      }
+
+      (OBJ_ID = ['blt_chart_element'.freeze, TkUtil.untrust('00000')]).instance_eval{
+        @mutex = Mutex.new
+        def mutex; @mutex; end
+        freeze
+      }
+
+      def Element.type2class(type)
+        ElementTypeToClass[type]
+      end
+
+      def Element.id2obj(chart, id)
+        cpath = chart.path
+        ElementID_TBL.mutex.synchronize{
+          return id unless ElementID_TBL[cpath]
+          ElementID_TBL[cpath][id]? ElementID_TBL[cpath][id]: id
+        }
+      end
+
+      def self.new(chart, element=nil, keys={})
+        if element.kind_of?(Hash)
+          keys = element
+          element = nil
+        end
+        if keys
+          keys = _symbolkey2str(keys)
+          not_create = keys.delete('without_creating')
+        else
+          not_create = false
+        end
+
+        obj = nil
+        ElementID_TBL.mutex.synchronize{
+          chart_path = chart.path
+          ElementID_TBL[chart_path] ||= {}
+          if element && ElementID_TBL[chart_path][element]
+            obj = ElementID_TBL[chart_path][element]
+          else
+            (obj = self.allocate).instance_eval{
+              if element
+                @element = @id = element.to_s
+              else
+                OBJ_ID.mutex.synchronize{
+                  @element = @id = OBJ_ID.join(TkCore::INTERP._ip_id_).freeze
+                  OBJ_ID[1].succ!
+                }
+              end
+              @path = @id
+              @parent = @chart = chart
+              @cpath = @chart.path
+              @typename = self.class::ElementTypeName
+              Element::ElementID_TBL[@cpath][@element] = self
+              unless not_create
+                tk_call(@chart, @typename, 'create', @element, keys)
+                return obj
+              end
+            }
+          end
+        }
+
+        obj.configure(keys) if obj && ! keys.empty?
+        obj
+      end
+
+      def initialize(chart, element=nil, keys={})
+        # dummy:: not called by 'new' method
+
+        if element.kind_of?(Hash)
+          keys = element
+          element = nil
+        end
+        if element
+          @element = @id = element.to_s
+        else
+          OBJ_ID.mutex.synchronize{
+            @element = @id = OBJ_ID.join(TkCore::INTERP._ip_id_).freeze
+            OBJ_ID[1].succ!
+          }
+        end
+        @path = @id
+        @parent = @chart = chart
+        @cpath = @chart.path
+        @typename = self.class::ElementTypeName
+        # Element::ElementID_TBL[@cpath][@element] = self
+        keys = _symbolkey2str(keys)
+        unless keys.delete('without_creating')
+          # @chart.element_create(@element, keys)
+          tk_call(@chart, @typename, 'create', @element, keys)
+        end
+      end
+
+      def id
+        @id
+      end
+
+      def to_eval
+        @id
+      end
+
+      def cget_tkstring(option)
+        # @chart.element_cget(@id, option)
+        @chart.__send__(@typename + '_cget_tkstring', @id, option)
+      end
+      def cget(option)
+        # @chart.element_cget(@id, option)
+        @chart.__send__(@typename + '_cget', @id, option)
+      end
+      def cget_strict(option)
+        @chart.__send__(@typename + '_cget_strict', @id, option)
+      end
+      def configure(key, value=None)
+        # @chart.element_configure(@id, key, value)
+        @chart.__send__(@typename + '_configure', @id, key, value)
+        self
+      end
+      def configinfo(key=nil)
+        # @chart.element_configinfo(@id, key)
+        @chart.__send__(@typename + '_configinfo', @id, key)
+      end
+      def current_configinfo(key=nil)
+        # @chart.current_element_configinfo(@id, key)
+        @chart.__send__('current_' << @typename << '_configinfo', @id, key)
+      end
+
+      def activate(*args)
+        @chart.element_activate(@id, *args)
+      end
+
+      def closest(x, y, var, keys={})
+        # @chart.element_closest(x, y, var, @id, keys)
+        @chart.__send__(@typename + '_closest', x, y, var, @id, keys)
+      end
+
+      def deactivate
+        @chart.element_deactivate(@id)
+        self
+      end
+
+      def delete
+        @chart.element_delete(@id)
+        self
+      end
+
+      def exist?
+        @chart.element_exist?(@id)
+      end
+
+      def name
+        @element
+      end
+
+      def type
+        @chart.element_type(@id)
+      end
+    end
+
+    class Bar < Element
+      ElementTypeName = 'bar'.freeze
+      ElementTypeToClass[ElementTypeName] = self
+    end
+    class Line < Element
+      ElementTypeName = 'line'.freeze
+      ElementTypeToClass[ElementTypeName] = self
+    end
+
+    #################
+
+    class GridLine < TkObject
+      GridLineID_TBL = TkCore::INTERP.create_table
+      TkCore::INTERP.init_ip_env{
+        GridLineID_TBL.mutex.synchronize{ GridLineID_TBL.clear }
+      }
+
+      def self.new(chart, keys={})
+        obj = nil
+        GridLineID_TBL.mutex.synchronize{
+          unless (obj = GridLineID_TBL[chart.path])
+            (obj = self.allocate).instance_eval{
+              @parent = @chart = chart
+              @cpath = @chart.path
+              @path = @id = 'grid'
+              GridLine::GridLineID_TBL[@cpath] = self
+            }
+          end
+        }
+        chart.gridline_configure(keys) if obj && ! keys.empty?
+        obj
+      end
+
+      def initialize(chart, keys={})
+        # dummy:: not called by 'new' method
+
+        @parent = @chart = chart
+        @cpath = @chart.path
+        # GridLine::GridLineID_TBL[@cpath] = self
+        @chart.gridline_configure(keys) unless keys.empty?
+        @path = @id = 'grid'
+      end
+
+      def id
+        @id
+      end
+
+      def to_eval
+        @id
+      end
+
+      def cget_tkstring(option)
+        @chart.gridline_cget_tkstring(option)
+      end
+      def cget(option)
+        @chart.gridline_cget(option)
+      end
+      def cget_strict(option)
+        @chart.gridline_cget_strict(option)
+      end
+      def configure(key, value=None)
+        @chart.gridline_configure(key, value)
+        self
+      end
+      def configinfo(key=nil)
+        @chart.gridline_configinfo(key)
+      end
+      def current_configinfo(key=nil)
+        @chart.current_gridline_configinfo(key)
+      end
+
+      def off
+        @chart.gridline_off
+        self
+      end
+      def on
+        @chart.gridline_on
+        self
+      end
+      def toggle
+        @chart.gridline_toggle
+        self
+      end
+    end
+
+    #################
+
+    class Legend < TkObject
+      LegendID_TBL = TkCore::INTERP.create_table
+
+      TkCore::INTERP.init_ip_env{
+        LegendID_TBL.mutex.synchronize{ LegendID_TBL.clear }
+      }
+
+      def self.new(chart, keys={})
+        obj = nil
+        LegenedID_TBL.mutex.synchronize{
+          unless (obj = LegenedID_TBL[chart.path])
+            (obj = self.allocate).instance_eval{
+              @parent = @chart = chart
+              @cpath = @chart.path
+              @path = @id = 'crosshairs'
+              Legend::LegenedID_TBL[@cpath] = self
+            }
+          end
+        }
+        chart.legend_configure(keys) if obj && ! keys.empty?
+        obj
+      end
+
+      def initialize(chart, keys={})
+        # dummy:: not called by 'new' method
+
+        @parent = @chart = chart
+        @cpath = @chart.path
+        # Legend::LegendID_TBL[@cpath] = self
+        @chart.legend_configure(keys) unless keys.empty?
+        @path = @id = 'legend'
+      end
+
+      def id
+        @id
+      end
+
+      def to_eval
+        @id
+      end
+
+      def cget_tkstring(option)
+        @chart.legend_cget_tkstring(option)
+      end
+      def cget(option)
+        @chart.legend_cget(option)
+      end
+      def cget_strict(option)
+        @chart.legend_cget_strict(option)
+      end
+      def configure(key, value=None)
+        @chart.legend_configure(key, value)
+        self
+      end
+      def configinfo(key=nil)
+        @chart.legend_configinfo(key)
+      end
+      def current_configinfo(key=nil)
+        @chart.current_legend_configinfo(key)
+      end
+
+      def activate(*args)
+        @chart.legend_activate(*args)
+      end
+
+      def deactivate(*args)
+        @chart.legend_deactivate(*args)
+      end
+
+      def get(pos, y=nil)
+        @chart.legend_get(pos, y)
+      end
+    end
+
+    #################
+
+    class Pen < TkObject
+      (OBJ_ID = ['blt_chart_pen'.freeze, TkUtil.untrust('00000')]).instance_eval{
+        @mutex = Mutex.new
+        def mutex; @mutex; end
+        freeze
+      }
+
+      PenID_TBL = TkCore::INTERP.create_table
+
+      TkCore::INTERP.init_ip_env{
+        PenID_TBL.mutex.synchronize{ PenID_TBL.clear }
+      }
+
+      def self.id2obj(chart, id)
+        cpath = chart.path
+        PenID_TBL.mutex.synchronize{
+          return id unless PenID_TBL[cpath]
+          PenID_TBL[cpath][id]? PenID_TBL[cpath][id]: id
+        }
+      end
+
+      def self.new(chart, pen=nil, keys={})
+        if pen.kind_of?(Hash)
+          keys = pen
+          pen = nil
+        end
+        if keys
+          keys = _symbolkey2str(keys)
+          not_create = keys.delete('without_creating')
+        else
+          not_create = false
+        end
+
+        obj = nil
+        PenID_TBL.mutex.synchronize{
+          chart_path = chart.path
+          PenID_TBL[chart_path] ||= {}
+          if pen && PenID_TBL[chart_path][pen]
+            obj = PenID_TBL[chart_path][pen]
+          else
+            (obj = self.allocate).instance_eval{
+              if pen
+                @pen = @id = pen.to_s
+              else
+                OBJ_ID.mutex.synchronize{
+                  @pen = @id = OBJ_ID.join(TkCore::INTERP._ip_id_).freeze
+                  OBJ_ID[1].succ!
+                }
+              end
+              @path = @id
+              @parent = @chart = chart
+              @cpath = @chart.path
+              Pen::PenID_TBL[@cpath][@pen] = self
+              unless not_create
+                tk_call(@chart, 'pen', 'create', @pen, keys)
+                return obj
+              end
+            }
+          end
+        }
+
+        obj.configure(keys) if obj && ! keys.empty?
+        obj
+      end
+
+      def initialize(chart, pen=nil, keys={})
+        if pen.kind_of?(Hash)
+          keys = pen
+          pen = nil
+        end
+        if pen
+          @pen = @id = pen.to_s
+        else
+          OBJ_ID.mutex.synchronize{
+            @pen = @id = OBJ_ID.join(TkCore::INTERP._ip_id_).freeze
+            OBJ_ID[1].succ!
+          }
+        end
+        @path = @id
+        @parent = @chart = chart
+        @cpath = @chart.path
+        Pen::PenID_TBL[@cpath][@pen] = self
+        keys = _symbolkey2str(keys)
+        unless keys.delete('without_creating')
+          # @chart.pen_create(@pen, keys)
+          tk_call(@chart, 'pen', 'create', @pen, keys)
+        end
+      end
+
+      def id
+        @id
+      end
+
+      def to_eval
+        @id
+      end
+
+      def cget_tkstring(option)
+        @chart.pen_cget_tkstring(@id, option)
+      end
+      def cget(option)
+        @chart.pen_cget(@id, option)
+      end
+      def cget_strict(option)
+        @chart.pen_cget_strict(@id, option)
+      end
+      def configure(key, value=None)
+        @chart.pen_configure(@id, key, value)
+        self
+      end
+      def configinfo(key=nil)
+        @chart.pen_configinfo(@id, key)
+      end
+      def current_configinfo(key=nil)
+        @chart.current_pen_configinfo(@id, key)
+      end
+
+      def delete
+        @chart.pen_delete(@id)
+        self
+      end
+
+      def name
+        @pen
+      end
+    end
+
+    #################
+
+    class Postscript < TkObject
+      PostscriptID_TBL = TkCore::INTERP.create_table
+
+      TkCore::INTERP.init_ip_env{
+        PostscriptID_TBL.mutex.synchronize{ PostscriptID_TBL.clear }
+      }
+
+      def self.new(chart, keys={})
+        obj = nil
+        PostscriptID_TBL.mutex.synchronize{
+          unless (obj = PostscriptID_TBL[chart.path])
+            (obj = self.allocate).instance_eval{
+              @parent = @chart = chart
+              @cpath = @chart.path
+              @path = @id = 'postscript'
+              Postscript::PostscriptID_TBL[@cpath] = self
+            }
+          end
+        }
+        chart.postscript_configure(keys) if obj && ! keys.empty?
+        obj
+      end
+
+      def initialize(chart, keys={})
+        # dummy:: not called by 'new' method
+
+        @parent = @chart = chart
+        @cpath = @chart.path
+        # Postscript::PostscriptID_TBL[@cpath] = self
+        @chart.postscript_configure(keys) unless keys.empty?
+        @path = @id = 'postscript'
+      end
+
+      def id
+        @id
+      end
+
+      def to_eval
+        @id
+      end
+
+      def cget_tkstring(option)
+        @chart.postscript_cget_tkstring(option)
+      end
+      def cget(option)
+        @chart.postscript_cget(option)
+      end
+      def cget_strict(option)
+        @chart.postscript_cget_strict(option)
+      end
+      def configure(key, value=None)
+        @chart.postscript_configure(key, value)
+        self
+      end
+      def configinfo(key=nil)
+        @chart.postscript_configinfo(key)
+      end
+      def current_configinfo(key=nil)
+        @chart.current_postscript_configinfo(key)
+      end
+
+      def output(file=nil, keys={})
+        if file.kind_of?(Hash)
+          keys = file
+          file = nil
+        end
+
+        ret = @chart.postscript_output(file, keys)
+
+        if file
+          self
+        else
+          ret
+        end
+      end
+    end
+
+    #################
+    class Marker < TkObject
+      extend Tk
+      extend TkItemFontOptkeys
+      extend TkItemConfigOptkeys
+
+      extend Tk::BLT::PlotComponent::OptKeys
+
+      MarkerTypeName = nil
+      MarkerTypeToClass = {}
+      MarkerID_TBL = TkCore::INTERP.create_table
+
+      TkCore::INTERP.init_ip_env{
+        MarkerID_TBL.mutex.synchronize{ MarkerID_TBL.clear }
+      }
+
+      def Marker.type2class(type)
+        MarkerTypeToClass[type]
+      end
+
+      def Marker.id2obj(chart, id)
+        cpath = chart.path
+        MarkerID_TBL.mutex.synchronize{
+          if MarkerID_TBL[cpath]
+            MarkerID_TBL[cpath][id]? MarkerID_TBL[cpath][id]: id
+          else
+            id
+          end
+        }
+      end
+
+      def self._parse_create_args(keys)
+        fontkeys = {}
+        methodkeys = {}
+        if keys.kind_of? Hash
+          keys = _symbolkey2str(keys)
+
+          __item_font_optkeys(nil).each{|key|
+            fkey = key.to_s
+            fontkeys[fkey] = keys.delete(fkey) if keys.key?(fkey)
+
+            fkey = "kanji#{key}"
+            fontkeys[fkey] = keys.delete(fkey) if keys.key?(fkey)
+
+            fkey = "latin#{key}"
+            fontkeys[fkey] = keys.delete(fkey) if keys.key?(fkey)
+
+            fkey = "ascii#{key}"
+            fontkeys[fkey] = keys.delete(fkey) if keys.key?(fkey)
+          }
+
+          __item_optkey_aliases(nil).each{|alias_name, real_name|
+            alias_name = alias_name.to_s
+            if keys.has_key?(alias_name)
+              keys[real_name.to_s] = keys.delete(alias_name)
+            end
+          }
+
+          __item_methodcall_optkeys(nil).each{|key|
+            key = key.to_s
+            methodkeys[key] = keys.delete(key) if keys.key?(key)
+          }
+
+          __item_ruby2val_optkeys(nil).each{|key, method|
+            key = key.to_s
+            keys[key] = method.call(keys[key]) if keys.has_key?(key)
+          }
+
+          args = itemconfig_hash_kv(nil, keys)
+        else
+          args = []
+        end
+
+        [args, fontkeys, methodkeys]
+      end
+      private_class_method :_parse_create_args
+
+      def self.create(chart, keys={})
+        unless self::MarkerTypeName
+          fail RuntimeError, "#{self} is an abstract class"
+        end
+        args, fontkeys, methodkeys = _parse_create_args(keys)
+        idnum = tk_call_without_enc(chart.path, 'marker', 'create',
+                                    self::MarkerTypeName, *args)
+        chart.marker_configure(idnum, fontkeys) unless fontkeys.empty?
+        chart.marker_configure(idnum, methodkeys) unless methodkeys.empty?
+        idnum.to_i  # 'item id' is an integer number
+      end
+
+      def self.create_type(chart, type, keys={})
+        args, fontkeys, methodkeys = _parse_create_args(keys)
+        idnum = tk_call_without_enc(chart.path, 'marker', 'create',
+                                    type, *args)
+        chart.marker_configure(idnum, fontkeys) unless fontkeys.empty?
+        chart.marker_configure(idnum, methodkeys) unless methodkeys.empty?
+        id = idnum.to_i  # 'item id' is an integer number
+        obj = self.allocate
+        obj.instance_eval{
+          @parent = @chart = chart
+          @cpath = chart.path
+          @id = id
+          Tk::BLT::PlotComponent::Marker::MarkerID_TBL.mutex.synchronize{
+            Tk::BLT::PlotComponent::Marker::MarkerID_TBL[@cpath] ||= {}
+            Tk::BLT::PlotComponent::Marker::MarkerID_TBL[@cpath][@id] = self
+          }
+        }
+        obj
+      end
+
+      def initialize(parent, *args)
+        @parent = @chart = parent
+        @cpath = parent.path
+
+        @path = @id = create_self(*args) # an integer number as 'item id'
+        Tk::BLT::PlotComponent::Marker::MarkerID_TBL.mutex.synchronize{
+          Tk::BLT::PlotComponent::Marker::MarkerID_TBL[@cpath] ||= {}
+          Tk::BLT::PlotComponent::Marker::MarkerID_TBL[@cpath][@id] = self
+        }
+      end
+      def create_self(*args)
+        self.class.create(@chart, *args) # return an integer as 'item id'
+      end
+      private :create_self
+
+      def id
+        @id
+      end
+
+      def to_eval
+        @id
+      end
+
+      def cget_tkstring(option)
+        @chart.marker_cget_tkstring(@id, option)
+      end
+      def cget(option)
+        @chart.marker_cget(@id, option)
+      end
+      def cget_strict(option)
+        @chart.marker_cget_strict(@id, option)
+      end
+      def configure(key, value=None)
+        @chart.marker_configure(@id, key, value)
+        self
+      end
+      def configinfo(key=nil)
+        @chart.marker_configinfo(@id, key)
+      end
+      def current_configinfo(key=nil)
+        @chart.current_marker_configinfo(@id, key)
+      end
+
+      def after(target=None)
+        @chart.marker_after(@id, target)
+      end
+
+      def before(target=None)
+        @chart.marker_before(@id, target)
+      end
+
+      def delete
+        @chart.marker_delete(@id)
+      end
+
+      def exist?
+        @chart.marker_exist(@id)
+      end
+
+      def type
+        @chart.marker_type(@id)
+      end
+    end
+
+    class TextMarker < Marker
+      MarkerTypeName = 'text'.freeze
+      MarkerTypeToClass[MarkerTypeName] = self
+    end
+    class LineMarker < Marker
+      MarkerTypeName = 'line'.freeze
+      MarkerTypeToClass[MarkerTypeName] = self
+    end
+    class BitmapMarker < Marker
+      MarkerTypeName = 'bitmap'.freeze
+      MarkerTypeToClass[MarkerTypeName] = self
+    end
+    class ImageMarker < Marker
+      MarkerTypeName = 'image'.freeze
+      MarkerTypeToClass[MarkerTypeName] = self
+    end
+    class PolygonMarker < Marker
+      MarkerTypeName = 'polygon'.freeze
+      MarkerTypeToClass[MarkerTypeName] = self
+    end
+    class WindowMarker < Marker
+      MarkerTypeName = 'window'.freeze
+      MarkerTypeToClass[MarkerTypeName] = self
+    end
+
+    #################
+
+    def __destroy_hook__
+      Axis::AxisID_TBL.delete(@path)
+      Crosshairs::CrosshairsID_TBL.delete(@path)
+      Element::ElementID_TBL.delete(@path)
+      GridLine::GridLineID_TBL.delete(@path)
+      Legend::LegendID_TBL.delete(@path)
+      Pen::PenID_TBL.delete(@path)
+      Postscript::PostscriptID_TBL.delete(@path)
+      Marker::MarkerID_TBL.delete(@path)
+      super()
+    end
+
+    #################
+
+    def tagid(tag)
+      if tag.kind_of?(Axis) ||
+          tag.kind_of?(Crosshairs) ||
+          tag.kind_of?(Element) ||
+          tag.kind_of?(GridLine) ||
+          tag.kind_of?(Legend) ||
+          tag.kind_of?(Pen) ||
+          tag.kind_of?(Postscript) ||
+          tag.kind_of?(Marker)
+        tag.id
+      else
+        tag  # maybe an Array of configure parameters
+      end
+    end
+
+    def _component_bind(target, tag, context, *args)
+      if TkComm._callback_entry?(args[0]) || !block_given?
+        cmd = args.shift
+      else
+        cmd = Proc.new
+      end
+      _bind([path, target, 'bind', tagid(tag)], context, cmd, *args)
+      self
+    end
+    def _component_bind_append(target, tag, context, *args)
+      if TkComm._callback_entry?(args[0]) || !block_given?
+        cmd = args.shift
+      else
+        cmd = Proc.new
+      end
+      _bind_append([path, target, 'bind', tagid(tag)], context, cmd, *args)
+      self
+    end
+    def _component_bind_remove(target, tag, context)
+      _bind_remove([path, target, 'bind', tagid(tag)], context)
+      self
+    end
+    def _component_bindinfo(target, tag, context=nil)
+      _bindinfo([path, target, 'bind', tagid(tag)], context)
+    end
+    private :_component_bind, :_component_bind_append
+    private :_component_bind_remove, :_component_bindinfo
+
+    def axis_bind(tag, context, *args)
+      _component_bind('axis', tag, context, *args)
+    end
+    def axis_bind_append(tag, context, *args)
+      _component_bind_append('axis', tag, context, *args)
+    end
+    def axis_bind_remove(tag, context)
+      _component_bind_remove('axis', tag, context)
+    end
+    def axis_bindinfo(tag, context=nil)
+      _component_bindinfo('axis', tag, context)
+    end
+
+    def element_bind(tag, context, *args)
+      _component_bind('element', tag, context, *args)
+    end
+    def element_bind_append(tag, context, *args)
+      _component_bind_append('element', tag, context, *args)
+    end
+    def element_bind_remove(tag, context)
+      _component_bind_remove('element', tag, context)
+    end
+    def element_bindinfo(tag, context=nil)
+      _component_bindinfo('element', tag, context)
+    end
+
+    def bar_bind(tag, context, *args)
+      _component_bind('bar', tag, context, *args)
+    end
+    def bar_bind_append(tag, context, *args)
+      _component_bind_append('bar', tag, context, *args)
+    end
+    def bar_bind_remove(tag, context)
+      _component_bind_remove('bar', tag, context)
+    end
+    def bar_bindinfo(tag, context=nil)
+      _component_bindinfo('bar', tag, context)
+    end
+
+    def line_bind(tag, context, *args)
+      _component_bind('line', tag, context, *args)
+    end
+    def line_bind_append(tag, context, *args)
+      _component_bind_append('line', tag, context, *args)
+    end
+    def line_bind_remove(tag, context)
+      _component_bind_remove('line', tag, context)
+    end
+    def line_bindinfo(tag, context=nil)
+      _component_bindinfo('line', tag, context)
+    end
+
+    def legend_bind(tag, context, *args)
+      _component_bind('legend', tag, context, *args)
+    end
+    def legend_bind_append(tag, context, *args)
+      _component_bind_append('legend', tag, context, *args)
+    end
+    def legend_bind_remove(tag, context)
+      _component_bind_remove('legend', tag, context)
+    end
+    def legend_bindinfo(tag, context=nil)
+      _component_bindinfo('legend', tag, context)
+    end
+
+    def marker_bind(tag, context, *args)
+      _component_bind('marker', tag, context, *args)
+    end
+    def marker_bind_append(tag, context, *args)
+      _component_bind_append('marker', tag, context, *args)
+    end
+    def marker_bind_remove(tag, context)
+      _component_bind_remove('marker', tag, context)
+    end
+    def marker_bindinfo(tag, context=nil)
+      _component_bindinfo('marker', tag, context)
+    end
+
+    ###################
+
+    def axis_create(id=nil, keys={})
+      # tk_send('axis', 'create', tagid(id), keys)
+      Tk::BLT::PlotComponent::Axis.new(self, tagid(id), keys)
+    end
+    def axis_delete(*ids)
+      tk_send('axis', 'delete', *(ids.collect{|id| tagid(id)}))
+      self
+    end
+    def axis_invtransform(id, val)
+      list(tk_send('axis', 'invtransform', tagid(id), val))
+    end
+    def axis_limits(id)
+      list(tk_send('axis', 'limits', tagid(id)))
+    end
+    def axis_names(*pats)
+      simplelist(tk_send('axis', 'names',
+                         *(pats.collect{|pat| tagid(pat)}))).collect{|axis|
+        Tk::BLT::PlotComponent::Axis.id2obj(self, axis)
+      }
+    end
+    def axis_transform(id, val)
+      list(tk_send('axis', 'transform', tagid(id), val))
+    end
+    def axis_view(id)
+      tk_send('axis', 'view', tagid(id))
+      self
+    end
+    def axis_use(id, target=nil)
+      if target
+        Tk::BLT::PlotComponent::Axis.id2obj(self,
+                                            tk_send('axis', 'use',
+                                                    tagid(id), tagid(target)))
+      else
+        Tk::BLT::PlotComponent::Axis.id2obj(self,
+                                            tk_send('axis', 'use', tagid(id)))
+      end
+    end
+
+    ###################
+
+    def crosshairs_off
+      tk_send_without_enc('crosshairs', 'off')
+      self
+    end
+    def crosshairs_on
+      tk_send_without_enc('crosshairs', 'on')
+      self
+    end
+    def crosshairs_toggle
+      tk_send_without_enc('crosshairs', 'toggle')
+      self
+    end
+
+    ###################
+
+    def element_create(id=nil, keys={})
+      # tk_send('element', 'create', tagid(id), keys)
+      Tk::BLT::PlotComponent::Element.new(self, tagid(id), keys)
+    end
+    def element_activate(*args)
+      if args.empty?
+        list(tk_send('element', 'activate')).collect{|elem|
+          Tk::BLT::PlotComponent::Element.id2obj(self, elem)
+        }
+      else
+        # id, *indices
+        id = args.shift
+        tk_send('element', 'activate', tagid(id), *args)
+      end
+    end
+    def element_closest(x, y, var, *args)
+      if args[-1].kind_of?(Hash)
+        keys = args.pop
+        bool(tk_send('element', 'closest', x, y, var,
+                     *(hash_kv(keys).concat(args.collect{|id| tagid(id)}))))
+      else
+        bool(tk_send('element', 'closest', x, y, var,
+                     *(args.collect{|id| tagid(id)})))
+      end
+    end
+    def element_deactivate(*ids)
+      tk_send('element', 'deactivate', *(ids.collect{|id| tagid(id)}))
+      self
+    end
+    def element_delete(*ids)
+      tk_send('element', 'delete', *(ids.collect{|id| tagid(id)}))
+      self
+    end
+    def element_exist?(id)
+      bool(tk_send('element', 'exists', tagid(id)))
+    end
+    def element_names(*pats)
+      simplelist(tk_send('element', 'names',
+                         *(pats.collect{|pat| tagid(pat)}))).collect{|elem|
+        Tk::BLT::PlotComponent::Element.id2obj(self, elem)
+      }
+    end
+    def element_show(*names)
+      if names.empty?
+        simplelist(tk_send('element', 'show'))
+      else
+        tk_send('element', 'show', *(names.collect{|n| tagid(n)}))
+        self
+      end
+    end
+    def element_type(id)
+      tk_send('element', 'type', tagid(id))
+    end
+
+    ###################
+
+    def bar_create(id=nil, keys={})
+      # tk_send('bar', 'create', tagid(id), keys)
+      Tk::BLT::PlotComponent::Bar.new(self, tagid(id), keys)
+    end
+    alias bar bar_create
+    def bar_activate(*args)
+      if args.empty?
+        list(tk_send('bar', 'activate')).collect{|elem|
+          Tk::BLT::PlotComponent::Element.id2obj(self, elem)
+        }
+      else
+        # id, *indices
+        id = args.shift
+        tk_send('bar', 'activate', tagid(id), *args)
+      end
+    end
+    def bar_closest(x, y, var, *args)
+      if args[-1].kind_of?(Hash)
+        keys = args.pop
+        bool(tk_send('bar', 'closest', x, y, var,
+                     *(hash_kv(keys).concat(args.collect{|id| tagid(id)}))))
+      else
+        bool(tk_send('bar', 'closest', x, y, var,
+                     *(args.collect{|id| tagid(id)})))
+      end
+    end
+    def bar_deactivate(*ids)
+      tk_send('bar', 'deactivate', *(ids.collect{|id| tagid(id)}))
+      self
+    end
+    def bar_delete(*ids)
+      tk_send('bar', 'delete', *(ids.collect{|id| tagid(id)}))
+      self
+    end
+    def bar_exist?(id)
+      bool(tk_send('bar', 'exists', tagid(id)))
+    end
+    def bar_names(*pats)
+      simplelist(tk_send('bar', 'names',
+                         *(pats.collect{|pat| tagid(pat)}))).collect{|elem|
+        Tk::BLT::PlotComponent::Element.id2obj(self, elem)
+      }
+    end
+    def bar_show(*names)
+      if names.empty?
+        simplelist(tk_send('bar', 'show'))
+      else
+        tk_send('bar', 'show', *(names.collect{|n| tagid(n)}))
+        self
+      end
+    end
+    def bar_type(id)
+      tk_send('bar', 'type', tagid(id))
+    end
+
+    ###################
+
+    def line_create(id=nil, keys={})
+      # tk_send('line', 'create', tagid(id), keys)
+      Tk::BLT::PlotComponent::Line.new(self, tagid(id), keys)
+    end
+    alias bar line_create
+    def line_activate(*args)
+      if args.empty?
+        list(tk_send('line', 'activate')).collect{|elem|
+          Tk::BLT::PlotComponent::Element.id2obj(self, elem)
+        }
+      else
+        # id, *indices
+        id = args.shift
+        tk_send('line', 'activate', tagid(id), *args)
+      end
+    end
+    def line_closest(x, y, var, *args)
+      if args[-1].kind_of?(Hash)
+        keys = args.pop
+        bool(tk_send('line', 'closest', x, y, var,
+                     *(hash_kv(keys).concat(args.collect{|id| tagid(id)}))))
+      else
+        bool(tk_send('line', 'closest', x, y, var,
+                     *(args.collect{|id| tagid(id)})))
+      end
+    end
+    def line_deactivate(*ids)
+      tk_send('line', 'deactivate', *(ids.collect{|id| tagid(id)}))
+      self
+    end
+    def line_delete(*ids)
+      tk_send('line', 'delete', *(ids.collect{|id| tagid(id)}))
+      self
+    end
+    def line_exist?(id)
+      bool(tk_send('line', 'exists', tagid(id)))
+    end
+    def line_names(*pats)
+      simplelist(tk_send('line', 'names',
+                         *(pats.collect{|pat| tagid(pat)}))).collect{|elem|
+        Tk::BLT::PlotComponent::Element.id2obj(self, elem)
+      }
+    end
+    def line_show(*names)
+      if names.empty?
+        simplelist(tk_send('line', 'show'))
+      else
+        tk_send('line', 'show', *(names.collect{|n| tagid(n)}))
+        self
+      end
+    end
+    def line_type(id)
+      tk_send('line', 'type', tagid(id))
+    end
+
+    ###################
+
+    def gridline_off
+      tk_send_without_enc('grid', 'off')
+      self
+    end
+    def gridline_on
+      tk_send_without_enc('grid', 'on')
+      self
+    end
+    def gridline_toggle
+      tk_send_without_enc('grid', 'toggle')
+      self
+    end
+
+    ###################
+
+    def legend_window_create(parent=nil, keys=nil)
+      if parent.kind_of?(Hash)
+        keys = _symbolkey2str(parent)
+        parent = keys.delete('parent')
+        widgetname = keys.delete('widgetname')
+        keys.delete('without_creating')
+      elsif keys
+        keys = _symbolkey2str(keys)
+        widgetname = keys.delete('widgetname')
+        keys.delete('without_creating')
+      end
+
+      legend = self.class.new(parent, :without_creating=>true,
+                              :widgetname=>widgetname)
+      class << legend
+        def __destroy_hook__
+          TkCore::INTERP.tk_windows.delete(@path)
+        end
+      end
+
+      if keys
+        self.legend_configure(keys.update('position'=>legend))
+      else
+        self.legend_configure('position'=>legend)
+      end
+      legend
+    end
+
+    def legend_activate(*pats)
+      list(tk_send('legend', 'activate',
+                   *(pats.collect{|pat| tagid(pat)}))).collect{|elem|
+        Tk::BLT::PlotComponent::Element.id2obj(self, elem)
+      }
+    end
+    def legend_deactivate(*pats)
+      list(tk_send('legend', 'deactivate',
+                   *(pats.collect{|pat| tagid(pat)}))).collect{|elem|
+        Tk::BLT::PlotComponent::Element.id2obj(self, elem)
+      }
+    end
+    def legend_get(pos, y=nil)
+      if y
+        Tk::BLT::PlotComponent::Element.id2obj(self,
+                                               tk_send('legend', 'get',
+                                                       _at(pos, y)))
+      else
+        Tk::BLT::PlotComponent::Element.id2obj(self,
+                                               tk_send('legend', 'get', pos))
+      end
+    end
+
+    ###################
+
+    def pen_create(id=nil, keys={})
+      # tk_send('pen', 'create', tagid(id), keys)
+      Tk::BLT::PlotComponent::Pen.new(self, tagid(id), keys)
+    end
+    def pen_delete(*ids)
+      tk_send('pen', 'delete', *(ids.collect{|id| tagid(id)}))
+      self
+    end
+    def pen_names(*pats)
+      simplelist(tk_send('pen', 'names',
+                         *(pats.collect{|pat| tagid(pat)}))).collect{|pen|
+        Tk::BLT::PlotComponent::Pen.id2obj(self, pen)
+      }
+    end
+
+    ###################
+
+    def postscript_output(file=nil, keys={})
+      if file.kind_of?(Hash)
+        keys = file
+        file = nil
+      end
+
+      if file
+        tk_send('postscript', 'output', file, keys)
+        self
+      else
+        tk_send('postscript', 'output', keys)
+      end
+    end
+
+    ###################
+
+    def marker_create(type, keys={})
+      case type
+      when :text, 'text'
+        Tk::BLT::PlotComponent::TextMarker.new(self, keys)
+      when :line, 'line'
+        Tk::BLT::PlotComponent::LineMarker.new(self, keys)
+      when :bitmap, 'bitmap'
+        Tk::BLT::PlotComponent::BitmapMarker.new(self, keys)
+      when :image, 'image'
+        Tk::BLT::PlotComponent::ImageMarker.new(self, keys)
+      when :polygon, 'polygon'
+        Tk::BLT::PlotComponent::PolygonMarker.new(self, keys)
+      when :window, 'window'
+        Tk::BLT::PlotComponent::WindowMarker.new(self, keys)
+      else
+        if type.kind_of?(Tk::BLT::PlotComponent::Marker)
+          type.new(self, keys)
+        else
+          Tk::BLT::PlotComponent::Marker.create_type(self, type, keys)
+        end
+      end
+    end
+    def marker_after(id, target=nil)
+      if target
+        tk_send_without_enc('marker', 'after', tagid(id), tagid(target))
+      else
+        tk_send_without_enc('marker', 'after', tagid(id))
+      end
+      self
+    end
+    def marker_before(id, target=None)
+      if target
+        tk_send_without_enc('marker', 'before', tagid(id), tagid(target))
+      else
+        tk_send_without_enc('marker', 'before', tagid(id))
+      end
+      self
+    end
+    def marker_delete(*ids)
+      tk_send('marker', 'delete', *(ids.collect{|id| tagid(id)}))
+      self
+    end
+    def marker_exist?(id)
+      bool(tk_send('marker', 'exists', tagid(id)))
+    end
+    def marker_names(*pats)
+      simplelist(tk_send('marker', 'names',
+                         *(pats.collect{|pat| tagid(pat)}))).collect{|id|
+        Tk::BLT::PlotComponent::Marker.id2obj(self, id)
+      }
+    end
+    def marker_type(id)
+      tk_send('marker', 'type', tagid(id))
+    end
+
+    ###################
+
+    def xaxis_cget_tkstring(option)
+      itemcget_tkstring('xaxis', option)
+    end
+    def xaxis_cget(option)
+      itemcget('xaxis', option)
+    end
+    def xaxis_cget_strict(option)
+      itemcget_strict('xaxis', option)
+    end
+    def xaxis_configure(slot, value=None)
+      if slot.kind_of?(Hash)
+        slot = _symbolkey2str(slot)
+        if cmd = slot.delete('command')
+          slot['command'] = proc{|w, tick|
+            cmd.call(TkComm.window(w), TkComm.num_or_str(tick))
+          }
+        end
+      elsif slot == :command || slot == 'command'
+        cmd = value
+        value = proc{|w, tick|
+          cmd.call(TkComm.window(w), TkComm.num_or_str(tick))
+        }
+      end
+      itemconfigure('xaxis', slot, value)
+    end
+    def xaxis_configinfo(slot=nil)
+      itemconfiginfo('xaxis', slot)
+    end
+    def current_xaxis_configinfo(slot=nil)
+      current_itemconfiginfo('xaxis', slot)
+    end
+    def xaxis_bind(context, *args)
+      if TkComm._callback_entry?(args[0]) || !block_given?
+        cmd = args.shift
+      else
+        cmd = Proc.new
+      end
+      _bind([path, 'xaxis', 'bind'], context, cmd, *args)
+      self
+    end
+    def xaxis_bind_append(context, *args)
+      if TkComm._callback_entry?(args[0]) || !block_given?
+        cmd = args.shift
+      else
+        cmd = Proc.new
+      end
+      _bind_append([path, 'xaxis', 'bind'], context, cmd, *args)
+      self
+    end
+    def xaxis_bind_remove(context)
+      _bind_remove([path, 'xaxis', 'bind'], context)
+      self
+    end
+    def xaxis_bindinfo(context=nil)
+      _bindinfo([path, 'xaxis', 'bind'], context)
+    end
+    def xaxis_invtransform(val)
+      list(tk_send('xaxis', 'invtransform', val))
+    end
+    def xaxis_limits
+      list(tk_send('xaxis', 'limits'))
+    end
+    def xaxis_transform(val)
+      list(tk_send('xaxis', 'transform', val))
+    end
+    def xaxis_use(target=nil)
+      if target
+        Tk::BLT::PlotComponent::Axis.id2obj(self,
+                                            tk_send('xaxis', 'use',
+                                                    tagid(target)))
+      else
+        Tk::BLT::PlotComponent::Axis.id2obj(self, tk_send('xaxis', 'use'))
+      end
+    end
+
+    def x2axis_cget_tkstring(option)
+      itemcget_tkstring('x2axis', option)
+    end
+    def x2axis_cget(option)
+      itemcget('x2axis', option)
+    end
+    def x2axis_cget_strict(option)
+      itemcget_strict('x2axis', option)
+    end
+    def x2axis_configure(slot, value=None)
+      if slot.kind_of?(Hash)
+        slot = _symbolkey2str(slot)
+        if cmd = slot.delete('command')
+          slot['command'] = proc{|w, tick|
+            cmd.call(TkComm.window(w), TkComm.num_or_str(tick))
+          }
+        end
+      elsif slot == :command || slot == 'command'
+        cmd = value
+        value = proc{|w, tick|
+          cmd.call(TkComm.window(w), TkComm.num_or_str(tick))
+        }
+      end
+      itemconfigure('x2axis', slot, value)
+    end
+    def x2axis_configinfo(slot=nil)
+      itemconfiginfo('x2axis', slot)
+    end
+    def current_x2axis_configinfo(slot=nil)
+      current_itemconfiginfo('x2axis', slot)
+    end
+    def x2axis_bind(context, *args)
+      if TkComm._callback_entry?(args[0]) || !block_given?
+        cmd = args.shift
+      else
+        cmd = Proc.new
+      end
+      _bind([path, 'x2axis', 'bind'], context, cmd, *args)
+      self
+    end
+    def x2axis_bind_append(context, *args)
+      if TkComm._callback_entry?(args[0]) || !block_given?
+        cmd = args.shift
+      else
+        cmd = Proc.new
+      end
+      _bind_append([path, 'x2axis', 'bind'], context, cmd, *args)
+      self
+    end
+    def x2axis_bind_remove(context)
+      _bind_remove([path, 'x2axis', 'bind'], context)
+      self
+    end
+    def x2axis_bindinfo(context=nil)
+      _bindinfo([path, 'x2axis', 'bind'], context)
+    end
+    def x2axis_invtransform(val)
+      list(tk_send('x2axis', 'invtransform', val))
+    end
+    def x2axis_limits
+      list(tk_send('x2axis', 'limits'))
+    end
+    def x2axis_transform(val)
+      list(tk_send('x2axis', 'transform', val))
+    end
+    def x2axis_use(target=nil)
+      if target
+        Tk::BLT::PlotComponent::Axis.id2obj(self,
+                                            tk_send('x2axis', 'use',
+                                                    tagid(target)))
+      else
+        Tk::BLT::PlotComponent::Axis.id2obj(self, tk_send('x2axis', 'use'))
+      end
+    end
+
+    def yaxis_cget_tkstring(option)
+      itemcget_tkstring('yaxis', option)
+    end
+    def yaxis_cget(option)
+      itemcget('yaxis', option)
+    end
+    def yaxis_cget_strict(option)
+      itemcget_strict('yaxis', option)
+    end
+    def yaxis_configure(slot, value=None)
+      if slot.kind_of?(Hash)
+        slot = _symbolkey2str(slot)
+        if cmd = slot.delete('command')
+          slot['command'] = proc{|w, tick|
+            cmd.call(TkComm.window(w), TkComm.num_or_str(tick))
+          }
+        end
+      elsif slot == :command || slot == 'command'
+        cmd = value
+        value = proc{|w, tick|
+          cmd.call(TkComm.window(w), TkComm.num_or_str(tick))
+        }
+      end
+      itemconfigure('yaxis', slot, value)
+    end
+    def yaxis_configinfo(slot=nil)
+      itemconfiginfo('yaxis', slot)
+    end
+    def current_yaxis_configinfo(slot=nil)
+      current_itemconfiginfo('yaxis', slot)
+    end
+    def yaxis_bind(context, *args)
+      if TkComm._callback_entry?(args[0]) || !block_given?
+        cmd = args.shift
+      else
+        cmd = Proc.new
+      end
+      _bind([path, 'yaxis', 'bind'], context, cmd, *args)
+      self
+    end
+    def yaxis_bind_append(context, *args)
+      if TkComm._callback_entry?(args[0]) || !block_given?
+        cmd = args.shift
+      else
+        cmd = Proc.new
+      end
+      _bind_append([path, 'yaxis', 'bind'], context, cmd, *args)
+      self
+    end
+    def yaxis_bind_remove(context)
+      _bind_remove([path, 'yaxis', 'bind'], context)
+      self
+    end
+    def yaxis_bindinfo(context=nil)
+      _bindinfo([path, 'yaxis', 'bind'], context)
+    end
+    def yaxis_invtransform(val)
+      list(tk_send('yaxis', 'invtransform', val))
+    end
+    def yaxis_limits
+      list(tk_send('yaxis', 'limits'))
+    end
+    def yaxis_transform(val)
+      list(tk_send('yaxis', 'transform', val))
+    end
+    def yaxis_use(target=nil)
+      if target
+        Tk::BLT::PlotComponent::Axis.id2obj(self,
+                                            tk_send('yaxis', 'use',
+                                                    tagid(target)))
+      else
+        Tk::BLT::PlotComponent::Axis.id2obj(self, tk_send('yaxis', 'use'))
+      end
+    end
+
+    def y2axis_cget_tkstring(option)
+      itemcget_tkstring('y2axis', option)
+    end
+    def y2axis_cget(option)
+      itemcget('y2axis', option)
+    end
+    def y2axis_cget_strict(option)
+      itemcget_strict('y2axis', option)
+    end
+    def y2axis_configure(slot, value=None)
+      if slot.kind_of?(Hash)
+        slot = _symbolkey2str(slot)
+        if cmd = slot.delete('command')
+          slot['command'] = proc{|w, tick|
+            cmd.call(TkComm.window(w), TkComm.num_or_str(tick))
+          }
+        end
+      elsif slot == :command || slot == 'command'
+        cmd = value
+        value = proc{|w, tick|
+          cmd.call(TkComm.window(w), TkComm.num_or_str(tick))
+        }
+      end
+      itemconfigure('y2axis', slot, value)
+    end
+    def y2axis_configinfo(slot=nil)
+      axis_configinfo('y2axis', slot)
+    end
+    def current_y2axis_configinfo(slot=nil)
+      current_itemconfiginfo('y2axis', slot)
+    end
+    def y2axis_bind(context, *args)
+      if TkComm._callback_entry?(args[0]) || !block_given?
+        cmd = args.shift
+      else
+        cmd = Proc.new
+      end
+      _bind([path, 'y2axis', 'bind'], context, cmd, *args)
+      self
+    end
+    def y2axis_bind_append(context, *args)
+      if TkComm._callback_entry?(args[0]) || !block_given?
+        cmd = args.shift
+      else
+        cmd = Proc.new
+      end
+      _bind_append([path, 'y2axis', 'bind'], context, cmd, *args)
+      self
+    end
+    def y2axis_bind_remove(context)
+      _bind_remove([path, 'y2axis', 'bind'], context)
+      self
+    end
+    def y2axis_bindinfo(context=nil)
+      _bindinfo([path, 'y2axis', 'bind'], context)
+    end
+    def y2axis_invtransform(val)
+      list(tk_send('y2axis', 'invtransform', val))
+    end
+    def y2axis_limits
+      list(tk_send('y2axis', 'limits'))
+    end
+    def y2axis_transform(val)
+      list(tk_send('y2axis', 'transform', val))
+    end
+    def y2axis_use(target=nil)
+      if target
+        Tk::BLT::PlotComponent::Axis.id2obj(self,
+                                            tk_send('y2axis', 'use',
+                                                    tagid(target)))
+      else
+        Tk::BLT::PlotComponent::Axis.id2obj(self, tk_send('y2axis', 'use'))
+      end
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/container.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/container.rb
new file mode 100644
index 0000000..be05828
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/container.rb
@@ -0,0 +1,28 @@
+#
+#  tkextlib/blt/container.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  class Container < TkWindow
+    TkCommandNames = ['::blt::container'.freeze].freeze
+    WidgetClassName = 'Container'.freeze
+    WidgetClassNames[WidgetClassName] ||= self
+
+    def __strval_optkeys
+      super() << 'name'
+    end
+    private :__strval_optkeys
+
+    def find_command(pat)
+      Hash[*simplelist(tk_send_without_enc('find', '-command', pat))]
+    end
+
+    def find_name(pat)
+      Hash[*simplelist(tk_send_without_enc('find', '-name', pat))]
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/cutbuffer.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/cutbuffer.rb
new file mode 100644
index 0000000..1cc39df
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/cutbuffer.rb
@@ -0,0 +1,23 @@
+#
+#  tkextlib/blt/cutbuffer.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  module CutBuffer
+    TkCommandNames = ['::blt::cutbuffer'.freeze].freeze
+
+    def self.get(num = 0)
+      Tk.tk_call('::blt::cutbuffer', 'get', num)
+    end
+    def self.rotate(count = 1)
+      Tk.tk_call('::blt::cutbuffer', 'rotate', count)
+    end
+    def self.set(val, num = 0)
+      Tk.tk_call('::blt::cutbuffer', 'set', val, num)
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/dragdrop.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/dragdrop.rb
new file mode 100644
index 0000000..aa5c565
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/dragdrop.rb
@@ -0,0 +1,269 @@
+#
+#  tkextlib/blt/dragdrop.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tk/itemconfig'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  module DragDrop
+    extend TkCore
+
+    TkCommandNames = ['::blt::drag&drop'.freeze].freeze
+
+    class Token < TkWindow
+      WidgetClassName = 'DragDropToken'.freeze
+      WidgetClassNames[WidgetClassName] ||= self
+
+      def initialize(arg)
+        if arg.kind_of?(Hash) # arg is a hash includes the widgetpath of token
+          arg = _symbolkey2str(arg)
+          install_win(nil, arg['widgetname'])
+        else  # arg is a drag&drop source
+          tk_call('::blt::drag&drop', 'source', arg)
+          install_win(nil, tk_call('::blt::drag&drop', 'token', arg))
+        end
+      end
+    end
+
+    ###################################
+
+    extend TkItemConfigMethod
+    extend Tk::ValidateConfigure
+
+    class << self
+      def __item_config_cmd(id)  # id := ['source'|'target', win]
+        ['::blt::drag&drop', id[0], id[1]]
+      end
+      private :__item_config_cmd
+
+      def __item_boolval_optkeys(id)
+        super(id) << 'selftarget'
+      end
+      private :__item_boolval_optkeys
+
+      def __item_listval_optkeys(id)
+        super(id) << 'send'
+      end
+      private :__item_listval_optkeys
+
+      def __item_strval_optkeys(id)
+        super(id) << 'rejectbg' << 'rejectfg' << 'tokenbg'
+      end
+      private :__item_strval_optkeys
+
+      undef itemcget
+      undef itemcget_tkstring
+      private :itemconfigure, :itemconfiginfo, :current_itemconfiginfo
+
+      def source_configure(win, slot, value=None)
+        itemconfigure(['source', win], slot, value)
+      end
+      def source_configinfo(win, slot=nil)
+        itemconfiginfo(['source', win], slot)
+      end
+      def current_source_configinfo(win, slot=nil)
+        current_itemconfiginfo(['source', win], slot)
+      end
+    end
+
+    class PackageCommand < TkValidateCommand
+      class ValidateArgs < TkUtil::CallbackSubst
+        KEY_TBL = [
+          [ ?t, ?w, :token ],
+          [ ?W, ?w, :widget ],
+          nil
+        ]
+
+        PROC_TBL = [
+          [ ?w, TkComm.method(:window) ],
+          nil
+        ]
+
+=begin
+        # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
+        KEY_TBL.map!{|inf|
+          if inf.kind_of?(Array)
+            inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
+            inf[1] = inf[1].getbyte(0) if inf[1].kind_of?(String)
+          end
+          inf
+        }
+
+        PROC_TBL.map!{|inf|
+          if inf.kind_of?(Array)
+            inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
+          end
+          inf
+        }
+=end
+
+        _setup_subst_table(KEY_TBL, PROC_TBL)
+
+        def self.ret_val(val)
+          val
+        end
+      end
+
+      def self._config_keys
+        ['packagecmd']
+      end
+    end
+
+    class SiteCommand < TkValidateCommand
+      class ValidateArgs < TkUtil::CallbackSubst
+        KEY_TBL = [
+          [ ?s, ?b, :compatible ],
+          [ ?t, ?w, :token ],
+          nil
+        ]
+
+        PROC_TBL = [
+          [ ?b, TkComm.method(:bool) ],
+          [ ?w, TkComm.method(:window) ],
+          nil
+        ]
+
+=begin
+        # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
+        KEY_TBL.map!{|inf|
+          if inf.kind_of?(Array)
+            inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
+            inf[1] = inf[1].getbyte(0) if inf[1].kind_of?(String)
+          end
+          inf
+        }
+
+        PROC_TBL.map!{|inf|
+          if inf.kind_of?(Array)
+            inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
+          end
+          inf
+        }
+=end
+
+        _setup_subst_table(KEY_TBL, PROC_TBL)
+
+        def self.ret_val(val)
+          val
+        end
+      end
+
+      def self._config_keys
+        ['sitecmd']
+      end
+    end
+
+    def self.__validation_class_list
+      super() << PackageCommand << SiteCommand
+    end
+
+    class << self
+      Tk::ValidateConfigure.__def_validcmd(binding, PackageCommand)
+      Tk::ValidateConfigure.__def_validcmd(binding, SiteCommand)
+    end
+
+    ###################################
+
+    class DnD_Handle < TkUtil::CallbackSubst
+      KEY_TBL = [
+        [ ?i, ?s, :ip_name ],
+        [ ?v, ?v, :value ],
+        [ ?W, ?w, :widget ],
+        nil
+      ]
+
+      PROC_TBL = [
+        [ ?i, TkComm.method(:string) ],
+        [ ?v, TkComm.method(:tk_tcl2ruby) ],
+        [ ?w, TkComm.method(:window) ],
+        nil
+      ]
+
+=begin
+      # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
+      KEY_TBL.map!{|inf|
+        if inf.kind_of?(Array)
+          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
+          inf[1] = inf[1].getbyte(0) if inf[1].kind_of?(String)
+        end
+        inf
+      }
+
+      PROC_TBL.map!{|inf|
+        if inf.kind_of?(Array)
+          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
+        end
+        inf
+      }
+=end
+
+      _setup_subst_table(KEY_TBL, PROC_TBL)
+    end
+
+    def self.source_handler(win, datatype, cmd=Proc.new, *args)
+      _bind_for_event_class(DnD_Handle,
+                            ['::blt::drag&drop', 'source', win, 'handler'],
+                            cmd, *args)
+    end
+
+    def self.target_handler(win, datatype, cmd=Proc.new, *args)
+      _bind_for_event_class(DnD_Handle,
+                            ['::blt::drag&drop', 'target', win, 'handler'],
+                            cmd, *args)
+    end
+
+    ###################################
+
+    def self.init_source(win)
+      tk_call('::blt::drag&drop', 'source', win)
+    end
+
+    def self.source()
+      list(tk_call('::blt::drag&drop', 'source'))
+    end
+
+    def self.source_handler_list(win)
+      simplelist(tk_call('::blt::drag&drop', 'source', win, 'handler'))
+    end
+    def self.source_handler_info(win, type)
+      tk_tcl2ruby(tk_call('::blt::drag&drop', 'source', win, 'handler', type))
+    end
+
+    def self.target
+      list(tk_call('::blt::drag&drop', 'target'))
+    end
+    def self.target_handler_list(win)
+      simplelist(tk_call('::blt::drag&drop', 'target', win, 'handler'))
+    end
+
+    def self.handle_target(win, type, val=None)
+      tk_call('::blt::drag&drop', 'target', win, 'handle', type, val)
+    end
+
+    def self.token(win)
+      window(tk_call('::blt::drag&drop', 'token', win))
+    end
+
+    def self.drag(win, x, y)
+      tk_call('::blt::drag&drop', 'drag', win, x, y)
+    end
+    def self.drop(win, x, y)
+      tk_call('::blt::drag&drop', 'drop', win, x, y)
+    end
+
+    def self.errors(cmd=Proc.new)
+      tk_call('::blt::drag&drop', 'errors', cmd)
+    end
+
+    def self.active
+      bool(tk_call('::blt::drag&drop', 'active'))
+    end
+
+    def self.location(x=None, y=None)
+      list(tk_call('::blt::drag&drop', 'location', x, y))
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/eps.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/eps.rb
new file mode 100644
index 0000000..0dba87a
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/eps.rb
@@ -0,0 +1,32 @@
+#
+#  tkextlib/blt/eps.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tk/canvas'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  class EPS < TkcItem
+    CItemTypeName = 'eps'.freeze
+    CItemTypeToClass[CItemTypeName] = self
+  end
+end
+
+class Tk::Canvas
+  alias __BLT_EPS_item_strval_optkeys __item_strval_optkeys
+  def __item_strval_optkeys(id)
+    __BLT_EPS_item_strval_optkeys(id) + [
+      'shadowcolor', 'title', 'titlecolor'
+    ]
+  end
+  private :__item_strval_optkeys
+
+  alias __BLT_EPS_item_boolval_optkeys __item_boolval_optkeys
+  def __item_boolval_optkeys(id)
+    __BLT_EPS_item_boolval_optkeys(id) + ['showimage']
+  end
+  private :__item_boolval_optkeys
+end
+
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/graph.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/graph.rb
new file mode 100644
index 0000000..6bd4424
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/graph.rb
@@ -0,0 +1,67 @@
+#
+#  tkextlib/blt/graph.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+require 'tkextlib/blt/component.rb'
+
+module Tk::BLT
+  class Graph < TkWindow
+    TkCommandNames = ['::blt::graph'.freeze].freeze
+    WidgetClassName = 'Graph'.freeze
+    WidgetClassNames[WidgetClassName] ||= self
+
+    include PlotComponent
+    include GraphCommand
+
+    def __boolval_optkeys
+      ['bufferelements', 'invertxy']
+    end
+    private :__boolval_optkeys
+
+    def __strval_optkeys
+      ['text', 'label', 'title', 'file', 'plotbackground']
+    end
+    private :__strval_optkeys
+
+=begin
+    BarElement_ID = ['blt_graph_bar'.freeze, TkUtil.untrust('00000')].freeze
+
+    def bar(elem=nil, keys={})
+      if elem.kind_of?(Hash)
+        keys = elem
+        elem = nil
+      end
+      unless elem
+        elem = BarElement_ID.join(TkCore::INTERP._ip_id_).freeze
+        BarElement_ID[1].succ!
+      end
+      tk_send('bar', elem, keys)
+      Element.new(self, elem, :without_creating=>true)
+    end
+=end
+
+    def extents(item)
+      num_or_str(tk_send_without_enc('extents', item))
+    end
+
+    def invtransform(x, y)
+      list(tk_send_without_enc('invtransform', x, y))
+    end
+
+    def inside(x, y)
+      bool(tk_send_without_enc('inside', x, y))
+    end
+
+    def snap(output, keys={})
+      tk_send_without_enc('snap', *(hash_kv(keys, false) + output))
+      self
+    end
+
+    def transform(x, y)
+      list(tk_send_without_enc('transform', x, y))
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/htext.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/htext.rb
new file mode 100644
index 0000000..878bd99
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/htext.rb
@@ -0,0 +1,112 @@
+#
+#  tkextlib/blt/htext.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tk/itemconfig.rb'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  class Htext<TkWindow
+    Htext_Var = TkVarAccess.new_hash('htext')
+    Htext_Widget = TkVarAccess.new('htext(widget)', :window)
+    Htext_File = TkVarAccess.new('htext(file)')
+    Htext_Line = TkVarAccess.new('htext(line)')
+
+    include TkItemConfigMethod
+    include Scrollable
+
+    TkCommandNames = ['::blt::htext'.freeze].freeze
+    WidgetClassName = 'Htext'.freeze
+    WidgetClassNames[WidgetClassName] ||= self
+
+    alias window_cget_tkstring itemcget_tkstring
+    alias window_cget itemcget
+    alias window_cget_strict itemcget_strict
+    alias window_configure itemconfigure
+    alias window_configuinfo itemconfiginfo
+    alias current_window_configuinfo current_itemconfiginfo
+
+    def __strval_optkeys
+      super() << 'filename'
+    end
+  private :__strval_optkeys
+
+    def append(win, keys={})
+      tk_send('append', _epath(win), keys)
+      self
+    end
+
+    def goto_line(idx)
+      tk_send_without_enc('gotoline', idx)
+      self
+    end
+    def current_line
+      number(tk_send_without_enc('gotoline'))
+    end
+
+    def index(str)
+      number(tk_send('index', str))
+    end
+
+    def line_pos(str)
+      tk_send('linepos', str)
+    end
+
+    def range(from=None, to=None)
+      tk_send_without_enc('range', from, to)
+    end
+
+    def scan_mark(pos)
+      tk_send_without_enc('scan', 'mark', pos)
+      self
+    end
+
+    def scan_dragto(pos)
+      tk_send_without_enc('scan', 'dragto', pos)
+      self
+    end
+
+    def search(pat, from=None, to=None)
+      num = number(tk_send('search', pat, from, to))
+      (num < 0)? nil: num
+    end
+
+    def selection_adjust(index)
+      tk_send_without_enc('selection', 'adjust', index)
+      self
+    end
+    def selection_clear()
+      tk_send_without_enc('selection', 'clear')
+      self
+    end
+    def selection_from(index)
+      tk_send_without_enc('selection', 'from', index)
+      self
+    end
+    def selection_line(index)
+      tk_send_without_enc('selection', 'line', index)
+      self
+    end
+    def selection_present()
+      bool(tk_send_without_enc('selection', 'present'))
+    end
+    def selection_range(first, last)
+      tk_send_without_enc('selection', 'range', first, last)
+      self
+    end
+    def selection_to(index)
+      tk_send_without_enc('selection', 'to', index)
+      self
+    end
+    def selection_word(index)
+      tk_send_without_enc('selection', 'word', index)
+      self
+    end
+
+    def windows(pat=None)
+      list(tk_send('windows', pat))
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/setup.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/setup.rb
new file mode 100644
index 0000000..ee406c6
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/setup.rb
@@ -0,0 +1,8 @@
+#
+#  setup.rb   --   setup script before calling TkPackage.require()
+#
+#    If you need some setup operations (for example, add a library path
+#    to the library search path) before using Tcl/Tk library packages
+#    wrapped by Ruby scripts in this directory, please write the setup
+#    operations in this file.
+#
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/spline.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/spline.rb
new file mode 100644
index 0000000..9f75a0b
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/spline.rb
@@ -0,0 +1,23 @@
+#
+#  tkextlib/blt/spline.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  module Spline
+    extend TkCore
+
+    TkCommandNames = ['::blt::spline'.freeze].freeze
+
+    def self.natural(x, y, sx, sy)
+      tk_call('::blt::spline', 'natural', x, y, sx, sy)
+    end
+
+    def self.quadratic(x, y, sx, sy)
+      tk_call('::blt::spline', 'quadratic', x, y, sx, sy)
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/stripchart.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/stripchart.rb
new file mode 100644
index 0000000..74093f1
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/stripchart.rb
@@ -0,0 +1,74 @@
+#
+#  tkextlib/blt/stripchart.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+require 'tkextlib/blt/component.rb'
+
+module Tk::BLT
+  class Stripchart < TkWindow
+    TkCommandNames = ['::blt::stripchart'.freeze].freeze
+    WidgetClassName = 'Stripchart'.freeze
+    WidgetClassNames[WidgetClassName] ||= self
+
+    include PlotComponent
+    include GraphCommand
+
+    def __boolval_optkeys
+      ['bufferelements', 'buffergraph', 'invertxy']
+    end
+    private :__boolval_optkeys
+
+    def __strval_optkeys
+      ['text', 'label', 'title', 'file',
+        'background', 'plotbackground']
+    end
+    private :__strval_optkeys
+
+=begin
+    BarElement_ID = ['blt_stripchart_bar'.freeze, TkUtil.untrust('00000')].freeze
+
+    def bar(elem=nil, keys={})
+      if elem.kind_of?(Hash)
+        keys = elem
+        elem = nil
+      end
+      unless elem
+        elem = BarElement_ID.join(TkCore::INTERP._ip_id_).freeze
+        BarElement_ID[1].succ!
+      end
+      tk_send('bar', elem, keys)
+      Element.new(self, elem, :without_creating=>true)
+    end
+=end
+
+    def extents(item)
+      num_or_str(tk_send_without_enc('extents', item))
+    end
+
+    def invtransform(x, y)
+      list(tk_send_without_enc('invtransform', x, y))
+    end
+
+    def inside(x, y)
+      bool(tk_send_without_enc('inside', x, y))
+    end
+
+    def metafile(file=None)
+      # Windows only
+      tk_send('metafile', file)
+      self
+    end
+
+    def snap(output, keys={})
+      tk_send_without_enc('snap', *(hash_kv(keys, false) + output))
+      self
+    end
+
+    def transform(x, y)
+      list(tk_send_without_enc('transform', x, y))
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/table.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/table.rb
new file mode 100644
index 0000000..205e29e
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/table.rb
@@ -0,0 +1,412 @@
+#
+#  tkextlib/blt/table.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tk/itemconfig.rb'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  module Table
+    include Tk
+    extend Tk
+    extend TkItemConfigMethod
+
+    TkCommandNames = ['::blt::table'.freeze].freeze
+
+    module TableContainer
+      def blt_table_add(*args)
+        Tk::BLT::Table.add(self, *args)
+        self
+      end
+
+      def blt_table_arrange()
+        Tk::BLT::Table.arrange(self)
+        self
+      end
+
+      def blt_table_cget_tkstring(*args)
+        Tk::BLT::Table.cget_tkstring(self, *args)
+      end
+      def blt_table_cget(*args)
+        Tk::BLT::Table.cget(self, *args)
+      end
+      def blt_table_cget_strict(*args)
+        Tk::BLT::Table.cget_strict(self, *args)
+      end
+
+      def blt_table_configure(*args)
+        Tk::BLT::Table.configure(self, *args)
+        self
+      end
+
+      def blt_table_configinfo(*args)
+        Tk::BLT::Table.configinfo(self, *args)
+      end
+
+      def blt_table_current_configinfo(*args)
+        Tk::BLT::Table.current_configinfo(self, *args)
+      end
+
+      def blt_table_locate(x, y)
+        Tk::BLT::Table.locate(self, x, y)
+      end
+
+      def blt_table_delete(*args)
+        Tk::BLT::Table.delete(self, *args)
+        self
+      end
+
+      def blt_table_extents(item)
+        Tk::BLT::Table.extents(self, item)
+      end
+
+      def blt_table_insert(*args)
+        Tk::BLT::Table.insert(self, *args)
+        self
+      end
+
+      def blt_table_insert_before(*args)
+        Tk::BLT::Table.insert_before(self, *args)
+        self
+      end
+
+      def blt_table_insert_after(*args)
+        Tk::BLT::Table.insert_after(self, *args)
+        self
+      end
+
+      def blt_table_join(first, last)
+        Tk::BLT::Table.join(self, first, last)
+        self
+      end
+
+      def blt_table_save()
+        Tk::BLT::Table.save(self)
+      end
+
+      def blt_table_search(*args)
+        Tk::BLT::Table.search(self, *args)
+      end
+
+      def blt_table_split(*args)
+        Tk::BLT::Table.split(self, *args)
+        self
+      end
+
+      def blt_table_itemcget_tkstring(*args)
+        Tk::BLT::Table.itemcget_tkstring(self, *args)
+      end
+      def blt_table_itemcget(*args)
+        Tk::BLT::Table.itemcget(self, *args)
+      end
+      def blt_table_itemcget_strict(*args)
+        Tk::BLT::Table.itemcget_strict(self, *args)
+      end
+
+      def blt_table_itemconfigure(*args)
+        Tk::BLT::Table.itemconfigure(self, *args)
+        self
+      end
+
+      def blt_table_itemconfiginfo(*args)
+        Tk::BLT::Table.itemconfiginfo(self, *args)
+      end
+
+      def blt_table_current_itemconfiginfo(*args)
+        Tk::BLT::Table.current_itemconfiginfo(self, *args)
+      end
+
+      def blt_table_iteminfo(item)
+        Tk::BLT::Table.iteminfo(self, item)
+      end
+    end
+  end
+end
+
+
+############################################
+class << Tk::BLT::Table
+  def __item_cget_cmd(id) # id := [ container, item ]
+    win = (id[0].kind_of?(TkWindow))? id[0].path: id[0].to_s
+    ['::blt::table', 'cget', win, id[1]]
+  end
+  private :__item_cget_cmd
+
+  def __item_config_cmd(id) # id := [ container, item, ... ]
+    container, *items = id
+    win = (container.kind_of?(TkWindow))? container.path: container.to_s
+    ['::blt::table', 'configure', win, *items]
+  end
+  private :__item_config_cmd
+
+  def __item_pathname(id)
+    win = (id[0].kind_of?(TkWindow))? id[0].path: id[0].to_s
+    win + ';'
+  end
+  private :__item_pathname
+
+  alias __itemcget_tkstring itemcget_tkstring
+  alias __itemcget itemcget
+  alias __itemcget_strict itemcget_strict
+  alias __itemconfigure itemconfigure
+  alias __itemconfiginfo itemconfiginfo
+  alias __current_itemconfiginfo current_itemconfiginfo
+
+  private :__itemcget_tkstring, :__itemcget, :__itemcget_strict
+  private :__itemconfigure, :__itemconfiginfo, :__current_itemconfiginfo
+
+  def __boolval_optkeys
+    super() << 'propagate'
+  end
+  private :__boolval_optkeys
+
+  def tagid(tag)
+    if tag.kind_of?(Array)
+      case tag[0]
+      when Integer
+        # [row, col]
+        tag.join(',')
+      when :c, :C, 'c', 'C', :r, :R, 'r', 'R'
+        # c0 or r1 or C*, and so on
+        tag.collect{|elem| elem.to_s}.join('')
+      else
+        tag
+      end
+    elsif tag.kind_of?(TkWindow)
+      _epath(tag)
+    else
+      tag
+    end
+  end
+
+  def tagid2obj(tagid)
+    tagid
+  end
+
+  ############################################
+
+  def cget_tkstring(container, option)
+    __itemcget_tkstring([container], option)
+  end
+  def cget(container, option)
+    __itemcget([container], option)
+  end
+  def cget_strict(container, option)
+    __itemcget_strict([container], option)
+  end
+
+  def configure(container, *args)
+    __itemconfigure([container], *args)
+  end
+
+  def configinfo(container, *args)
+    __itemconfiginfo([container], *args)
+  end
+
+  def current_configinfo(container, *args)
+    __current_itemconfiginfo([container], *args)
+  end
+
+  def itemcget_tkstring(container, item, option)
+    __itemcget_tkstring([container, tagid(item)], option)
+  end
+  def itemcget(container, item, option)
+    __itemcget([container, tagid(item)], option)
+  end
+  def itemcget_strict(container, item, option)
+    __itemcget_strict([container, tagid(item)], option)
+  end
+
+  def itemconfigure(container, *args)
+    if args[-1].kind_of?(Hash)
+      # container, item, item, ... , hash_optkeys
+      keys = args.pop
+      fail ArgumentError, 'no item is given' if args.empty?
+      id = [container]
+      args.each{|item| id << tagid(item)}
+      __itemconfigure(id, keys)
+    else
+      # container, item, item, ... , option, value
+      val = args.pop
+      opt = args.pop
+      fail ArgumentError, 'no item is given' if args.empty?
+      id = [container]
+      args.each{|item| id << tagid(item)}
+      __itemconfigure(id, opt, val)
+    end
+    container
+  end
+
+  def itemconfiginfo(container, *args)
+    slot = args[-1]
+    if slot.kind_of?(String) || slot.kind_of?(Symbol)
+      slot = slot.to_s
+      if slot[0] == ?. || slot =~ /^\d+,\d+$/ || slot =~ /^(c|C|r|R)(\*|\d+)/
+        #   widget     ||    row,col          ||    Ci or Ri
+        slot = nil
+      else
+        # option
+        slot = args.pop
+      end
+    else
+      slot = nil
+    end
+
+    fail ArgumentError, 'no item is given' if args.empty?
+
+    id = [container]
+    args.each{|item| id << tagid(item)}
+    __itemconfiginfo(id, slot)
+  end
+
+  def current_itemconfiginfo(container, *args)
+    slot = args[-1]
+    if slot.kind_of?(String) || slot.kind_of?(Symbol)
+      slot = slot.to_s
+      if slot[0] == ?. || slot =~ /^\d+,\d+$/ || slot =~ /^(c|C|r|R)(\*|\d+)/
+        #   widget     ||    row,col          ||    Ci or Ri
+        slot = nil
+      else
+        # option
+        slot = args.pop
+      end
+    else
+      slot = nil
+    end
+
+    fail ArgumentError, 'no item is given' if args.empty?
+
+    id = [container]
+    args.each{|item| id << tagid(item)}
+    __current_itemconfiginfo(id, slot)
+  end
+
+  def info(container)
+    ret = {}
+    inf = list(tk_call('::blt::table', 'info', container))
+    until inf.empty?
+      opt = inf.slice!(0..1)
+      ret[opt[1..-1]] = opt[1]
+    end
+    ret
+  end
+
+  def iteminfo(container, item)
+    inf = list(tk_call('::blt::table', 'info', container, tagid(item)).chomp)
+
+    ret = []
+    until inf.empty? || (inf[0].kind_of?(String) && inf[0] =~ /^-/)
+      ret << inf.shift
+    end
+
+    if inf.length > 1
+      keys = {}
+      while inf.length > 1
+        opt = inf.slice!(0..1)
+        keys[opt[0][1..-1]] = opt[1]
+      end
+      ret << keys
+    end
+
+    ret
+  end
+
+  ############################################
+
+  def create_container(container)
+    tk_call('::blt::table', container)
+    begin
+      class << container
+        include Tk::BLT::Table::TableContainer
+      end
+    rescue
+      warn('fail to include TableContainer methods (frozen object?)')
+    end
+    container
+  end
+
+  def add(container, *args)
+    if args.empty?
+      tk_call('::blt::table', container)
+    else
+      args = args.collect{|arg|
+        if arg.kind_of?(TkWindow)
+          _epath(arg)
+        elsif arg.kind_of?(Array)  # index
+          arg.join(',')
+        else
+          arg
+        end
+      }
+      tk_call('::blt::table', container, *args)
+    end
+    container
+  end
+
+  def arrange(container)
+    tk_call('::blt::table', 'arrange', container)
+    container
+  end
+
+  def delete(container, *args)
+    tk_call('::blt::table', 'delete', container, *args)
+  end
+
+  def extents(container, item)
+    ret = []
+    inf = list(tk_call('::blt::table', 'extents', container, item))
+    ret << inf.slice!(0..4) until inf.empty?
+    ret
+  end
+
+  def forget(*wins)
+    wins = wins.collect{|win| _epath(win)}
+    tk_call('::blt::table', 'forget', *wins)
+  end
+
+  def insert(container, *args)
+    tk_call('::blt::table', 'insert', container, *args)
+  end
+
+  def insert_before(container, *args)
+    tk_call('::blt::table', 'insert', container, '-before', *args)
+  end
+
+  def insert_after(container, *args)
+    tk_call('::blt::table', 'insert', container, '-after', *args)
+  end
+
+  def join(container, first, last)
+    tk_call('::blt::table', 'join', container, first, last)
+  end
+
+  def locate(container, x, y)
+    tk_call('::blt::table', 'locate', container, x, y)
+  end
+
+  def containers(arg={})
+    list(tk_call('::blt::table', 'containers', *hash_kv(arg)))
+  end
+
+  def containers_pattern(pat)
+    list(tk_call('::blt::table', 'containers', '-pattern', pat))
+  end
+
+  def containers_slave(win)
+    list(tk_call('::blt::table', 'containers', '-slave', win))
+  end
+
+  def save(container)
+    tk_call('::blt::table', 'save', container)
+  end
+
+  def search(container, keys={})
+    list(tk_call('::blt::table', 'containers', *hash_kv(keys)))
+  end
+
+  def split(container, *args)
+    tk_call('::blt::table', 'split', container, *args)
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/tabnotebook.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/tabnotebook.rb
new file mode 100644
index 0000000..82936c6
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/tabnotebook.rb
@@ -0,0 +1,110 @@
+#
+#  tkextlib/blt/tabnotebook.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+require 'tkextlib/blt/tabset.rb'
+
+module Tk::BLT
+  class Tabnotebook < Tabset
+    TkCommandNames = ['::blt::tabnotebook'.freeze].freeze
+    WidgetClassName = 'Tabnotebook'.freeze
+    WidgetClassNames[WidgetClassName] ||= self
+
+    class Tab < Tk::BLT::Tabset::Tab
+      def self.new(parent, pos=nil, name=nil, keys={})
+        if pos.kind_of?(Hash)
+          keys = pos
+          name = nil
+          pos  = nil
+        end
+        if name.kind_of?(Hash)
+          keys = name
+          name = nil
+        end
+        obj = nil
+        TabID_TBL.mutex.synchronize{
+          if name && TabID_TBL[parent.path] && TabID_TBL[parent.path][name]
+            obj = TabID_TBL[parent.path][name]
+            if pos
+              if pos.to_s == 'end'
+                obj.move_after('end')
+              else
+                obj.move_before(pos)
+              end
+            end
+            obj.configure if keys && ! keys.empty?
+          else
+            (obj = self.allocate).instance_eval{
+              initialize(parent, pos, name, keys)
+              TabID_TBL[@tpath] = {} unless TabID_TBL[@tpath]
+              TabID_TBL[@tpath][@id] = self
+            }
+          end
+        }
+        obj
+      end
+
+      def initialize(parent, pos, name, keys)
+        @t = parent
+        @tpath = parent.path
+        if name
+          @path = @id = name
+          unless (list(tk_call(@tpath, 'tab', 'names', @id)).empty?)
+            if pos
+              idx = tk_call(@tpath, 'index', @id)
+              if pos.to_s == 'end'
+                tk_call(@tpath, 'move', idx, 'after', 'end')
+              else
+                tk_call(@tpath, 'move', idx, 'before', pos)
+              end
+            end
+            tk_call(@tpath, 'tab', 'configure', @id, keys)
+          else
+            fail ArgumentError, "can't find tab \"#{@id}\" in #{@t}"
+          end
+        else
+          pos = 'end' unless pos
+          @path = @id = tk_call(@tpath, 'insert', pos, keys)
+        end
+      end
+    end
+
+    #######################################
+
+    def get_tab(index)
+      if (idx = tk_send_without_enc('id', tagindex(index))).empty?
+        nil
+      else
+        Tk::BLT::Tabset::Tab.id2obj(self, idx)
+      end
+    end
+    alias get_id get_tab
+
+    def get_tabobj(index)
+      if (idx = tk_send_without_enc('id', tagindex(index))).empty?
+        nil
+      else
+        Tk::BLT::Tabnotebook::Tab.new(self, nil, idx)
+      end
+    end
+
+    alias index_name index
+
+    def insert(pos=nil, keys={})
+      if pos.kind_of?(Hash)
+        keys = pos
+        pos = nil
+      end
+      pos = 'end' if pos.nil?
+      Tk::BLT::Tabnotebook::Tab.new(self, nil,
+                                    tk_send('insert', tagindex(pos), keys))
+
+    end
+    undef :insert_tabs
+
+    undef :tab_pageheight, :tab_pagewidth
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/tabset.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/tabset.rb
new file mode 100644
index 0000000..c4716c7
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/tabset.rb
@@ -0,0 +1,504 @@
+#
+#  tkextlib/blt/tabset.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  class Tabset < TkWindow
+    class Tab < TkObject
+      include TkTreatItemFont
+
+      TabID_TBL = TkCore::INTERP.create_table
+
+      (TabsetTab_ID = ['blt_tabset_tab'.freeze, TkUtil.untrust('00000')]).instance_eval{
+        @mutex = Mutex.new
+        def mutex; @mutex; end
+        freeze
+      }
+
+      TkCore::INTERP.init_ip_env{
+        TabID_TBL.mutex.synchronize{ TabID_TBL.clear }
+      }
+
+      def self.id2obj(tabset, id)
+        tpath = tabset.path
+        TabID_TBL.mutex.synchronize{
+          if TabID_TBL[tpath]
+            TabID_TBL[tpath][id]? TabID_TBL[tpath][id]: id
+          else
+            id
+          end
+        }
+      end
+
+      def self.new(parent, pos=nil, name=nil, keys={})
+        if pos.kind_of?(Hash)
+          keys = pos
+          name = nil
+          pos  = nil
+        end
+        if name.kind_of?(Hash)
+          keys = name
+          name = nil
+        end
+        obj = nil
+        TabID_TBL.mutex.synchronize{
+          if name && TabID_TBL[parent.path] && TabID_TBL[parent.path][name]
+            obj = TabID_TBL[parent.path][name]
+            if pos
+              if pos.to_s == 'end'
+                obj.move_after('end')
+              else
+                obj.move_before(pos)
+              end
+            end
+            obj.configure if keys && ! keys.empty?
+          else
+            (obj = self.allocate).instance_eval{
+              initialize(parent, pos, name, keys)
+              TabID_TBL[@tpath] = {} unless TabID_TBL[@tpath]
+              TabID_TBL[@tpath][@id] = self
+            }
+          end
+        }
+        obj
+      end
+
+      def initialize(parent, pos, name, keys)
+        @t = parent
+        @tpath = parent.path
+        if name
+          @path = @id = name
+          unless (list(tk_call(@tpath, 'tab', 'names', @id)).empty?)
+            if pos
+              idx = tk_call(@tpath, 'index', '-name', @id)
+              if pos.to_s == 'end'
+                tk_call(@tpath, 'move', idx, 'after', 'end')
+              else
+                tk_call(@tpath, 'move', idx, 'before', pos)
+              end
+            end
+            tk_call(@tpath, 'tab', 'configure', @id, keys)
+          else
+            pos = 'end' unless pos
+            tk_call(@tpath, 'insert', pos, @id, keys)
+          end
+        else
+          pos = 'end' unless pos
+          TabsetTab_ID.mutex.synchronize{
+            @path = @id = TabsetTab_ID.join(TkCore::INTERP._ip_id_)
+            TabsetTab_ID[1].succ!
+          }
+          tk_call(@tpath, 'insert', pos, @id, keys)
+        end
+      end
+
+      #def bind(context, cmd=Proc.new, *args)
+      #  @t.tab_bind(@id, context, cmd, *args)
+      #  self
+      #end
+      def bind(context, *args)
+        # if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
+        if TkComm._callback_entry?(args[0]) || !block_given?
+          cmd = args.shift
+        else
+          cmd = Proc.new
+        end
+        @t.tab_bind(@id, context, cmd, *args)
+        self
+      end
+      #def bind_append(context, cmd=Proc.new, *args)
+      #  @t.tab_bind_append(@id, context, cmd, *args)
+      #  self
+      #end
+      def bind_append(context, *args)
+        # if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
+        if TkComm._callback_entry?(args[0]) || !block_given?
+          cmd = args.shift
+        else
+          cmd = Proc.new
+        end
+        @t.tab_bind_append(@id, context, cmd, *args)
+        self
+      end
+      def bind_remove(context)
+        @t.tab_bind_remove(@id, context)
+        self
+      end
+      def bindinfo(context=nil)
+        @t.tab_bindinfo(@id, context)
+      end
+
+      def cget_tkstring(*args)
+        @t.tab_cget_tkstring(@id, *args)
+      end
+      def cget(*args)
+        @t.tab_cget(@id, *args)
+      end
+      def cget_strict(*args)
+        @t.tab_cget_strict(@id, *args)
+      end
+      def configure(*args)
+        @t.tab_configure(@id, *args)
+      end
+      def configinfo(*args)
+        @t.tab_configinfo(@id, *args)
+      end
+      def current_configinfo(*args)
+        @t.current_tab_configinfo(@id, *args)
+      end
+
+      def delete()
+        @t.delete(@id)
+        TabID_TBL.mutex.synchronize{
+          TabID_TBL[@tpath].delete(@id)
+        }
+        self
+      end
+
+      def get_name()
+        @id.dup
+      end
+
+      def focus()
+        @t.focus(self.index)
+      end
+
+      def index()
+        @t.index_name(@id)
+      end
+
+      def invoke()
+        @t.invoke(self.index)
+      end
+
+      def move_before(idx)
+        @t.move_before(self.index, idx)
+      end
+      def move_after(idx)
+        @t.move_after(self.index, idx)
+      end
+
+      def perforation_highlight(mode)
+        @t.perforation_highlight(self.index, mode)
+      end
+      def perforation_invoke()
+        @t.perforation_invoke(self.index)
+      end
+
+      def see()
+        @t.see(self.index)
+      end
+
+      def tearoff(name=None)
+        @t.tab_tearoff(self.index, *args)
+      end
+    end
+
+    ########################################
+
+    class NamedTab < Tab
+      def self.new(parent, name)
+        super(parent, nil, name, {})
+      end
+    end
+
+    ########################################
+
+    include X_Scrollable
+    include TkItemConfigMethod
+
+    TkCommandNames = ['::blt::tabset'.freeze].freeze
+    WidgetClassName = 'Tabset'.freeze
+    WidgetClassNames[WidgetClassName] ||= self
+
+    def __destroy_hook__
+      Tk::BLT::Tabset::Tab::TabID_TBL.mutex.synchronize{
+        Tk::BLT::Tabset::Tab::TabID_TBL.delete(@path)
+      }
+    end
+
+    ########################################
+
+    def __boolval_optkeys
+      super() << 'samewidth' << 'tearoff'
+    end
+    private :__strval_optkeys
+
+    def __strval_optkeys
+      super() << 'tabbackground' << 'tabforeground'
+    end
+    private :__strval_optkeys
+
+    def __item_cget_cmd(id)
+      [self.path, 'tab', 'cget', id]
+    end
+    private :__item_cget_cmd
+
+    def __item_config_cmd(id)
+      [self.path, 'tab', 'configure', id]
+    end
+    private :__item_config_cmd
+
+    def __item_pathname(tagOrId)
+      if tagOrId.kind_of?(Tk::BLT::Tabset::Tab)
+        self.path + ';' + tagOrId.id.to_s
+      else
+        self.path + ';' + tagOrId.to_s
+      end
+    end
+    private :__item_pathname
+
+    alias tab_cget_tkstring itemcget_tkstring
+    alias tab_cget itemcget
+    alias tab_cget_strict itemcget_strict
+    alias tab_configure itemconfigure
+    alias tab_configinfo itemconfiginfo
+    alias current_tab_configinfo current_itemconfiginfo
+
+    def __item_strval_optkeys(id)
+      super(id) << 'shadow'
+    end
+    private :__item_strval_optkeys
+
+    def tagid(tab)
+      if tab.kind_of?(Tk::BLT::Tabset::Tab)
+        tab.id
+      else
+        tab
+      end
+    end
+
+    def tagindex(tab)
+      if tab.kind_of?(Tk::BLT::Tabset::Tab)
+        tab.index
+      else
+        tab
+      end
+    end
+
+    ########################################
+
+    def activate(index)
+      tk_send('activate', tagindex(index))
+      self
+    end
+    alias highlight activate
+
+    #def tabbind(tag, context, cmd=Proc.new, *args)
+    #  _bind([path, "bind", tagid(tag)], context, cmd, *args)
+    #  self
+    #end
+    def tabbind(tag, context, *args)
+      # if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
+      if TkComm._callback_entry?(args[0]) || !block_given?
+        cmd = args.shift
+      else
+        cmd = Proc.new
+      end
+      _bind([path, "bind", tagid(tag)], context, cmd, *args)
+      self
+    end
+    #def tabbind_append(tag, context, cmd=Proc.new, *args)
+    #  _bind_append([path, "bind", tagid(tag)], context, cmd, *args)
+    #  self
+    #end
+    def tabbind_append(tag, context, *args)
+      # if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
+      if TkComm._callback_entry?(args[0]) || !block_given?
+        cmd = args.shift
+      else
+        cmd = Proc.new
+      end
+      _bind_append([path, "bind", tagid(tag)], context, cmd, *args)
+      self
+    end
+    def tabbind_remove(tag, context)
+      _bind_remove([path, "bind", tagid(tag)], context)
+      self
+    end
+    def tabbindinfo(tag, context=nil)
+      _bindinfo([path, "bind", tagid(tag)], context)
+    end
+
+    def delete(first, last=None)
+      tk_send('delete', tagindex(first), tagindex(last))
+      if first.kind_of?(Tk::BLT::Tabset::Tab)
+        TabID_TBL.mutex.synchronize{
+          TabID_TBL[@path].delete(first.id)
+        }
+      end
+      # middle tabs of the range are unknown
+      if last.kind_of?(Tk::BLT::Tabset::Tab)
+        TabID_TBL.mutex.synchronize{
+          TabID_TBL[@path].delete(last.id)
+        }
+      end
+      self
+    end
+
+    def focus(index)
+      tk_send('focus', tagindex(index))
+      self
+    end
+
+    def get_tab(index)
+      if (idx = tk_send_without_enc('get', tagindex(index))).empty?
+        nil
+      else
+        Tk::BLT::Tabset::Tab.id2obj(self, idx)
+      end
+    end
+    def get_tabobj(index)
+      if (idx = tk_send_without_enc('get', tagindex(index))).empty?
+        nil
+      else
+       Tk::BLT::Tabset::Tab.new(self, nil, name, {})
+      end
+    end
+
+    def index(str)
+      num_or_str(tk_send('index', str))
+    end
+    def index_name(tab)
+      num_or_str(tk_send('index', '-name', tagid(tab)))
+    end
+
+    def insert(pos, tab, keys={})
+      pos = 'end' if pos.nil?
+      Tk::BLT::Tabset::Tab.new(self, tagindex(pos), tagid(tab), keys)
+    end
+    def insert_tabs(pos, *tabs)
+      pos = 'end' if pos.nil?
+      if tabs[-1].kind_of?(Hash)
+        keys = tabs.pop
+      else
+        keys = {}
+      end
+      fail ArgumentError, 'no tabs is given' if tabs.empty?
+      tabs.map!{|tab| tagid(tab)}
+      tk_send('insert', tagindex(pos), *(tabs + [keys]))
+      tabs.collect{|tab| Tk::BLT::Tabset::Tab.new(self, nil, tagid(tab))}
+    end
+
+    def invoke(index)
+      tk_send('invoke', tagindex(index))
+    end
+
+    def move_before(index, base_idx)
+      tk_send('move', tagindex(index), 'before', tagindex(base_idx))
+      self
+    end
+    def move_after(index, base_idx)
+      tk_send('move', tagindex(index), 'after', tagindex(base_idx))
+      self
+    end
+
+    def nearest(x, y)
+      Tk::BLT::Tabset::Tab.id2obj(self, num_or_str(tk_send_without_enc('nearest', x, y)))
+    end
+
+    def perforation_activate(mode)
+      tk_send('perforation', 'activate', mode)
+      self
+    end
+    def perforation_highlight(index, *args)
+      if args.empty?
+        # index --> mode
+        tk_send('perforation', 'highlight', index)
+      elsif args.size == 1
+        # args[0] --> mode
+        tk_send('perforation', 'highlight', tagindex(index), args[0])
+      else # Error: call to get Tcl's error message
+        tk_send('perforation', 'highlight', tagindex(index), *args)
+      end
+      self
+    end
+    def perforation_invoke(index=nil)
+      if index
+        tk_send('perforation', 'invoke', tagindex(index))
+      else
+        tk_send('perforation', 'invoke')
+      end
+    end
+
+    def scan_mark(x, y)
+      tk_send_without_enc('scan', 'mark', x, y)
+      self
+    end
+    def scan_dragto(x, y)
+      tk_send_without_enc('scan', 'dragto', x, y)
+      self
+    end
+
+    def see(index)
+      tk_send('see', tagindex(index))
+      self
+    end
+
+    def size()
+      number(tk_send_without_enc('size'))
+    end
+
+    def select(index)
+      tk_send('select', tagindex(index))
+      self
+    end
+
+    def tab_dockall
+      tk_send('tab', 'dockall')
+      self
+    end
+
+    def tab_names(pat=None)
+      simplelist(tk_send('tab', 'names', pat)).collect{|name|
+        Tk::BLT::Tabset::Tab.id2obj(self, name)
+      }
+    end
+
+    def tab_objs(pat=None)
+      simplelist(tk_send('tab', 'names', pat)).collect{|name|
+        Tk::BLT::Tabset::Tab.new(self, nil, name, {})
+      }
+    end
+
+    def tab_ids(pat=None)
+      simplelist(tk_send('tab', 'names', pat))
+    end
+
+    def tab_pageheight
+      number(tk_send('tab', 'pageheight'))
+    end
+
+    def tab_pagewidth
+      number(tk_send('tab', 'pagewidth'))
+    end
+
+    def tab_tearoff(index, parent=None)
+      window(tk_send('tab', 'tearoff', tagindex(index), parent))
+    end
+
+    def xscrollcommand(cmd=Proc.new)
+      configure_cmd 'scrollcommand', cmd
+      self
+    end
+    alias scrollcommand xscrollcommand
+
+    def xview(*index)
+      if index.empty?
+        list(tk_send_without_enc('view'))
+      else
+        tk_send_without_enc('view', *index)
+        self
+      end
+    end
+    alias view xview
+    alias view_moveto xview_moveto
+    alias view_scroll xview_scroll
+
+    alias scrollbar xscrollbar
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/ted.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/ted.rb
new file mode 100644
index 0000000..53ab9ac
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/ted.rb
@@ -0,0 +1,68 @@
+#
+#  tkextlib/blt/ted.rb
+#
+#    *** This is alpha version, because there is no document on BLT. ***
+#
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  module Ted
+    extend TkCore
+
+    TkCommandNames = ['::blt::ted'.freeze].freeze
+
+    ##############################
+
+    extend TkItemConfigMethod
+
+    class << self
+      def __item_cget_cmd(id)
+        ['::blt::ted', 'cget', id]
+      end
+      private :__item_cget_cmd
+
+      def __item_config_cmd(id)
+        ['::blt::ted', 'configure', id]
+      end
+      private :__item_config_cmd
+
+      private :itemcget_tkstring, :itemcget, :itemcget_strict
+      private :itemconfigure, :itemconfiginfo, :current_itemconfiginfo
+
+      def cget_tkstring(master, option)
+        itemcget_tkstring(master, option)
+      end
+      def cget(master, option)
+        itemcget(master, option)
+      end
+      def cget_strict(master, option)
+        itemcget_strict(master, option)
+      end
+      def configure(master, slot, value=None)
+        itemconfigure(master, slot, value)
+      end
+      def configinfo(master, slot=nil)
+        itemconfiginfo(master, slot)
+      end
+      def current_configinfo(master, slot=nil)
+        current_itemconfiginfo(master, slot)
+      end
+    end
+
+    ##############################
+
+    def self.edit(master, *args)
+      tk_call('::blt::ted', 'edit', master, *args)
+    end
+    def self.rep(master, *args)
+      tk_call('::blt::ted', 'rep', master, *args)
+    end
+    def self.select(master, *args)
+      tk_call('::blt::ted', 'select', master, *args)
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/tile.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/tile.rb
new file mode 100644
index 0000000..c67cafd
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/tile.rb
@@ -0,0 +1,25 @@
+#
+#  tkextlib/blt/tile.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  module Tile
+    TkComm::TkExtlibAutoloadModule.unshift(self)
+    # Require autoload-symbols which is a same name as widget classname.
+    # Those are used at  TkComm._genobj_for_tkwidget method.
+
+    autoload :Button,      'tkextlib/blt/tile/button.rb'
+    autoload :CheckButton, 'tkextlib/blt/tile/checkbutton.rb'
+    autoload :Checkbutton, 'tkextlib/blt/tile/checkbutton.rb'
+    autoload :Radiobutton, 'tkextlib/blt/tile/radiobutton.rb'
+    autoload :RadioButton, 'tkextlib/blt/tile/radiobutton.rb'
+    autoload :Frame,       'tkextlib/blt/tile/frame.rb'
+    autoload :Label,       'tkextlib/blt/tile/label.rb'
+    autoload :Scrollbar,   'tkextlib/blt/tile/scrollbar.rb'
+    autoload :Toplevel,    'tkextlib/blt/tile/toplevel.rb'
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/tile/button.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/tile/button.rb
new file mode 100644
index 0000000..2e0863c
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/tile/button.rb
@@ -0,0 +1,16 @@
+#
+#  tkextlib/blt/tile/button.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tk/button'
+require 'tkextlib/blt/tile.rb'
+
+module Tk::BLT
+  module Tile
+    class Button < Tk::Button
+      TkCommandNames = ['::blt::tile::button'.freeze].freeze
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/tile/checkbutton.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/tile/checkbutton.rb
new file mode 100644
index 0000000..da230b5
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/tile/checkbutton.rb
@@ -0,0 +1,17 @@
+#
+#  tkextlib/blt/tile/checkbutton.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tk/checkbutton'
+require 'tkextlib/blt/tile.rb'
+
+module Tk::BLT
+  module Tile
+    class CheckButton < Tk::CheckButton
+      TkCommandNames = ['::blt::tile::checkbutton'.freeze].freeze
+    end
+    Checkbutton = CheckButton
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/tile/frame.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/tile/frame.rb
new file mode 100644
index 0000000..5434af4
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/tile/frame.rb
@@ -0,0 +1,16 @@
+#
+#  tkextlib/blt/tile/frame.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tk/frame'
+require 'tkextlib/blt/tile.rb'
+
+module Tk::BLT
+  module Tile
+    class Frame < Tk::Frame
+      TkCommandNames = ['::blt::tile::frame'.freeze].freeze
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/tile/label.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/tile/label.rb
new file mode 100644
index 0000000..f370c14
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/tile/label.rb
@@ -0,0 +1,16 @@
+#
+#  tkextlib/blt/tile/label.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tk/label'
+require 'tkextlib/blt/tile.rb'
+
+module Tk::BLT
+  module Tile
+    class Label < Tk::Label
+      TkCommandNames = ['::blt::tile::label'.freeze].freeze
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/tile/radiobutton.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/tile/radiobutton.rb
new file mode 100644
index 0000000..814f9a5
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/tile/radiobutton.rb
@@ -0,0 +1,17 @@
+#
+#  tkextlib/blt/tile/radiobutton.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tk/radiobutton'
+require 'tkextlib/blt/tile.rb'
+
+module Tk::BLT
+  module Tile
+    class RadioButton < Tk::RadioButton
+      TkCommandNames = ['::blt::tile::radiobutton'.freeze].freeze
+    end
+    Radiobutton = RadioButton
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/tile/scrollbar.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/tile/scrollbar.rb
new file mode 100644
index 0000000..2ae871d
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/tile/scrollbar.rb
@@ -0,0 +1,16 @@
+#
+#  tkextlib/blt/tile/scrollbar.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tk/scrollbar'
+require 'tkextlib/blt/tile.rb'
+
+module Tk::BLT
+  module Tile
+    class Scrollbar < Tk::Scrollbar
+      TkCommandNames = ['::blt::tile::scrollbar'.freeze].freeze
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/tile/toplevel.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/tile/toplevel.rb
new file mode 100644
index 0000000..76d5f86
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/tile/toplevel.rb
@@ -0,0 +1,16 @@
+#
+#  tkextlib/blt/tile/toplevel.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tk/toplevel'
+require 'tkextlib/blt/tile.rb'
+
+module Tk::BLT
+  module Tile
+    class Toplevel < Tk::Toplevel
+      TkCommandNames = ['::blt::tile::toplevel'.freeze].freeze
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/tree.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/tree.rb
new file mode 100644
index 0000000..1a3563e
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/tree.rb
@@ -0,0 +1,1058 @@
+#
+#  tkextlib/blt/tree.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  class Tree < TkObject
+    TkCommandNames = ['::blt::tree'.freeze].freeze
+
+    ###################################
+
+    class Node < TkObject
+      TreeNodeID_TBL = TkCore::INTERP.create_table
+
+      TkCore::INTERP.init_ip_env{
+        TreeNodeID_TBL.mutex.synchronize{ TreeNodeID_TBL.clear }
+      }
+
+      def self.id2obj(tree, id)
+        tpath = tree.path
+        TreeNodeID_TBL.mutex.synchronize{
+          if TreeNodeID_TBL[tpath]
+            if TreeNodeID_TBL[tpath][id]
+              TreeNodeID_TBL[tpath][id]
+            else
+              begin
+                # self.new(tree, nil, 'node'=>Integer(id))
+                id = Integer(id)
+                if bool(tk_call(@tpath, 'exists', id))
+                  (obj = self.allocate).instance_eval{
+                    @parent = @tree = tree
+                    @tpath = tpath
+                    @path = @id = id
+                    TreeNodeID_TBL[@tpath] = {} unless TreeNodeID_TBL[@tpath]
+                    TreeNodeID_TBL[@tpath][@id] = self
+                  }
+                  obj
+                else
+                  id
+                end
+              rescue
+                id
+              end
+            end
+          else
+            id
+          end
+        }
+      end
+
+      def self.new(tree, parent, keys={})
+        keys = _symbolkey2str(keys)
+        tpath = tree.path
+
+        TreeNodeID_TBL.mutex.synchronize{
+          TreeNodeID_TBL[tpath] ||= {}
+          if (id = keys['node']) && (obj = TreeNodeID_TBL[tpath][id])
+            keys.delete('node')
+            tk_call(tree.path, 'move', id, parent, keys) if parent
+            return obj
+          end
+
+          (obj = self.allocate).instance_eval{
+            initialize(tree, parent, keys)
+            TreeNodeID_TBL[tpath][@id] = self
+          }
+          obj
+        }
+      end
+
+      def initialize(tree, parent, keys={})
+        @parent = @tree = tree
+        @tpath = @parent.path
+
+        if (id = keys['node']) && bool(tk_call(@tpath, 'exists', id))
+          @path = @id = id
+          keys.delete('node')
+          tk_call(@tpath, 'move', @id, parent, keys) if parent
+        else
+          parent = tk_call(@tpath, 'root') unless parent
+          @path = @id = tk_call(@tpath, 'insert', parent, keys)
+        end
+      end
+
+      def id
+        @id
+      end
+
+      def apply(keys={})
+        @tree.apply(@id, keys)
+        self
+      end
+
+      def children()
+        @tree.children(@id)
+      end
+
+      def copy(parent, keys={})
+        @tree.copy(@id, parent, keys)
+      end
+      def copy_to(dest_tree, parent, keys={})
+        @tree.copy_to(@id, dest_tree, parent, keys)
+      end
+
+      def degree()
+        @tree.degree(@id)
+      end
+
+      def delete()
+        @tree.delete(@id)
+        self
+      end
+
+      def depth()
+        @tree.depth(@id)
+      end
+
+      def dump()
+        @tree.dump(@id)
+      end
+
+      def dump_to_file(file)
+        @tree.dump_to_file(@id, file)
+        self
+      end
+
+      def exist?(keys={})
+        @tree.exist?(@id, keys)
+      end
+
+      def find(keys={})
+        @tree.find(@id, keys)
+      end
+
+      def find_child(label)
+        @tree.find_child(@id, label)
+      end
+
+      def first_child()
+        @tree.first_child(@id)
+      end
+
+      def get()
+        @tree.get(@id)
+      end
+      def get_value(key, default_val=None)
+        @tree.get_value(@id, key, default_val)
+      end
+
+      def index()
+        @tree.index(@id)
+      end
+
+      def leaf?()
+        @tree.leaf?(@id)
+      end
+      def link?()
+        @tree.link?(@id)
+      end
+      def root?()
+        @tree.root?(@id)
+      end
+
+      def keys()
+        @tree.keys(@id)
+      end
+
+      def label(text = nil)
+        @tree.label(@id, nil)
+      end
+      def label=(text)
+        @tree.label(@id, text)
+      end
+
+      def last_child()
+        @tree.last_child(@id)
+      end
+
+      def move(dest, keys={})
+        @tree.keys(@id, dest, keys)
+        self
+      end
+
+      def next()
+        @tree.next(@id)
+      end
+
+      def next_sibling()
+        @tree.next_sibling(@id)
+      end
+
+      def parent()
+        @tree.parent(@id)
+      end
+
+      def fullpath()
+        @tree.fullpath(@id)
+      end
+
+      def position()
+        @tree.position(@id)
+      end
+
+      def previous()
+        @tree.previous(@id)
+      end
+
+      def prev_sibling()
+        @tree.prev_sibling(@id)
+      end
+
+      def restore(str, keys={})
+        @tree.restore(@id, str, keys)
+        self
+      end
+      def restore_overwrite(str, keys={})
+        @tree.restore_overwrite(@id, str, keys)
+        self
+      end
+
+      def restore_from_file(file, keys={})
+        @tree.restore_from_file(@id, file, keys)
+        self
+      end
+      def restore_overwrite_from_file(file, keys={})
+        @tree.restore_overwrite_from_file(@id, file, keys)
+        self
+      end
+
+      def root()
+        @tree.root(@id)
+        self
+      end
+
+      def set(data)
+        @tree.set(@id, data)
+        self
+      end
+
+      def size()
+        @tree.size(@id)
+      end
+
+      def sort(keys={})
+        @tree.sort(@id, keys)
+        self
+      end
+
+      def type(key)
+        @tree.type(@id, key)
+      end
+
+      def unset(*keys)
+        @tree.unset(@id, *keys)
+        self
+      end
+
+      def values(key=None)
+        @tree.values(@id, key)
+      end
+    end
+
+    ###################################
+
+    class Tag < TkObject
+      TreeTagID_TBL = TkCore::INTERP.create_table
+
+      TkCore::INTERP.init_ip_env{
+        TreeTagID_TBL.mutex.synchronize{ TreeTagID_TBL.clear }
+      }
+
+      (TreeTag_ID = ['blt_tree_tag'.freeze, TkUtil.untrust('00000')]).instance_eval{
+        @mutex = Mutex.new
+        def mutex; @mutex; end
+        freeze
+      }
+
+      def self.id2obj(tree, id)
+        tpath = tree.path
+        TreeTagID_TBL.mutex.synchronize{
+          if TreeTagID_TBL[tpath]
+            if TreeTagID_TBL[tpath][id]
+              TreeTagID_TBL[tpath][id]
+            else
+              begin
+                # self.new(tree, id)
+                (obj = self.allocate).instance_eval{
+                  @parent = @tree = tree
+                  @tpath = @parent.path
+                  @path = @id = id.dup.freeze if id
+                  TreeTagID_TBL[@tpath] = {} unless TreeTagID_TBL[@tpath]
+                  TreeTagID_TBL[@tpath][@id] = self
+                }
+                obj
+              rescue
+                id
+              end
+            end
+          else
+            id
+          end
+        }
+      end
+
+      def initialize(tree, tag_str = nil)
+        @parent = @tree = tree
+        @tpath = @parent.path
+
+        if tag_str
+          @path = @id = tag_str.dup.freeze
+        else
+          TreeTag_ID.mutex.synchronize{
+            @path = @id = TreeTag_ID.join(TkCore::INTERP._ip_id_)
+            TreeTag_ID[1].succ!
+          }
+        end
+        TreeTagID_TBL.mutex.synchronize{
+          TreeTagID_TBL[@tpath] = {} unless TreeTagID_TBL[@tpath]
+          TreeTagID_TBL[@tpath][@id] = self
+        }
+      end
+
+      def id
+        @id
+      end
+
+      def add(*nodes)
+        tk_call(@tpath, 'tag', 'add', @id, *nodes)
+        self
+      end
+
+      def delete(*nodes)
+        tk_call(@tpath, 'tag', 'delete', @id, *nodes)
+        self
+      end
+
+      def forget()
+        tk_call(@tpath, 'tag', 'forget', @id)
+        TreeTagID_TBL.mutex.synchronize{
+          TreeTagID_TBL[@tpath].delete(@id)
+        }
+        self
+      end
+
+      def nodes()
+        simplelist(tk_call(@tpath, 'tag', 'nodes', @id)).collect{|node|
+          Tk::BLT::Tree::Node.id2obj(@path, node)
+        }
+      end
+
+      def set(node)
+        tk_call(@tpath, 'tag', 'set', node, @id)
+        self
+      end
+
+      def unset(node)
+        tk_call(@tpath, 'tag', 'unset', node, @id)
+        self
+      end
+    end
+
+    ###################################
+
+    class Notify < TkObject
+      NotifyID_TBL = TkCore::INTERP.create_table
+
+      TkCore::INTERP.init_ip_env{
+        NotifyID_TBL.mutex.synchronize{ NotifyID_TBL.clear }
+      }
+
+      def self.id2obj(tree, id)
+        tpath = tree.path
+        NotifyID_TBL.mutex.synchronize{
+          if NotifyID_TBL[tpath]
+            if NotifyID_TBL[tpath][id]
+              NotifyID_TBL[tpath][id]
+            else
+              (obj = self.allocate).instance_eval{
+                @parent = @tree = tree
+                @tpath = @parent.path
+                @path = @id = id
+                NotifyID_TBL[@tpath] ||= {}
+                NotifyID_TBL[@tpath][@id] = self
+              }
+              obj
+            end
+          else
+            return id
+          end
+        }
+      end
+
+      def self.new(tree, *args, &b)
+        NotifyID_TBL.mutex.synchronize{
+          if tree.kind_of?(Array)
+            # not create
+            tpath = tree[0].path
+            NotifyID_TBL[tpath] ||= {}
+            unless (obj = NotifyID_TBL[tpath][tree[1]])
+              (NotifyID_TBL[tpath][tree[1]] =
+                 obj = self.allocate).instance_eval{
+                @parent = @tree = tree[0]
+                @tpath = @parent.path
+                @path = @id = tree[1]
+              }
+            end
+            return obj
+          end
+
+          (obj = self.allocate).instance_eval{
+            initialize(tree, *args, &b)
+            NotifyID_TBL[@tpath] ||= {}
+            NotifyID_TBL[@tpath][@id] = self
+          }
+          return obj
+        }
+      end
+
+      def initialize(tree, *args, &b)
+        @parent = @tree = tree
+        @tpath = @parent.path
+
+        # if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
+        if TkComm._callback_entry?(args[0])
+          cmd = args.shift
+        # elsif args[-1].kind_of?(Proc) || args[-1].kind_of?(Method)
+        elsif TkComm._callback_entry?(args[-1])
+          cmd = args.pop
+        elsif b
+          cmd = Proc.new(&b)
+        else
+          fail ArgumentError, "lack of 'command' argument"
+        end
+
+        args = args.collect{|arg| '-' << arg.to_s}
+
+        args << proc{|id, type|
+          cmd.call(Tk::BLT::Tree::Node.id2obj(@tree, id),
+                   ((type[0] == ?-)? type[1..-1]: type))
+        }
+
+        @path = @id = tk_call(@tpath, 'notify', 'create', *args)
+      end
+
+      def id
+        @id
+      end
+
+      def delete()
+        tk_call(@tpath, 'notify', 'delete', @id)
+        NotifyID_TBL.mutex.synchronize{
+          NotifyID_TBL[@tpath].delete(@id)
+        }
+        self
+      end
+
+      def info()
+        lst = simplelist(tk_call(@tpath, 'notify', 'info', id))
+        lst[0] = Tk::BLT::Tree::Notify.id2obj(@tree, lst[0])
+        lst[1] = simplelist(lst[1]).collect{|flag| flag[1..-1]}
+        lst[2] = tk_tcl2ruby(lst[2])
+        lst
+      end
+    end
+
+    ###################################
+
+    class Trace < TkObject
+      TraceID_TBL = TkCore::INTERP.create_table
+
+      TkCore::INTERP.init_ip_env{
+        TraceID_TBL.mutex.synchronize{ TraceID_TBL.clear }
+      }
+
+      def self.id2obj(tree, id)
+        tpath = tree.path
+        TraceID_TBL.mutex.synchronize{
+          if TraceID_TBL[tpath]
+            if TraceID_TBL[tpath][id]
+              TraceID_TBL[tpath][id]
+            else
+              begin
+                # self.new([tree, id])
+                (obj = self.allocate).instance_eval{
+                  @parent = @tree = tree
+                  @tpath = @parent.path
+                  @path = @id = node  # == traceID
+                  TraceID_TBL[@tpath] ||= {}
+                  TraceID_TBL[@tpath][@id] = self
+                }
+                obj
+              rescue
+                id
+              end
+            end
+          else
+            id
+          end
+        }
+      end
+
+      def self.new(tree, *args, &b)
+        TraceID_TBL.mutex.synchronize{
+          if tree.kind_of?(Array)
+            # not create
+            tpath = tree[0].path
+            TraceID_TBL[tpath] ||= {}
+            unless (obj = TraceID_TBL[tpath][tree[1]])
+              (TraceID_TBL[tpath][tree[1]] =
+                 obj = self.allocate).instance_eval{
+                @parent = @tree = tree
+                @tpath = @parent.path
+                @path = @id = tree[1]  # == traceID
+              }
+            end
+            return obj
+          end
+
+          # super(true, tree, *args, &b)
+          (obj = self.allocate).instance_eval{
+            initialize(tree, *args, &b)
+            TraceID_TBL[@tpath] ||= {}
+            TraceID_TBL[@tpath][@id] = self
+          }
+          return obj
+        }
+      end
+
+      def initialize(tree, node, key, opts, cmd=nil, &b)
+        @parent = @tree = tree
+        @tpath = @parent.path
+
+        if !cmd
+          if b
+            cmd = Proc.new(&b)
+          else
+            fail ArgumentError, "lack of 'command' argument"
+          end
+        end
+
+        @path = @id = tk_call(@tpath, 'trace', 'create', node, key, opts,
+                              proc{|t, id, k, ops|
+                                tobj = Tk::BLT::Tree.id2obj(t)
+                                if tobj.kind_of?(Tk::BLT::Tree)
+                                  nobj = Tk::BLT::Tree::Node.id2obj(tobj, id)
+                                else
+                                  nobj = id
+                                end
+                                cmd.call(tobj, nobj, k, ops)
+                              })
+      end
+
+      def id
+        @id
+      end
+
+      def delete()
+        tk_call(@tpath, 'trace', 'delete', @id)
+        TraceID_TBL.mutex.synchronize{
+          TraceID_TBL[tpath].delete(@id)
+        }
+        self
+      end
+
+      def info()
+        lst = simplelist(tk_call(@tpath, 'trace', 'info', id))
+        lst[0] = Tk::BLT::Tree::Trace.id2obj(@tree, lst[0])
+        lst[2] = simplelist(lst[2])
+        lst[3] = tk_tcl2ruby(lst[3])
+        lst
+      end
+    end
+
+    ###################################
+
+    TreeID_TBL = TkCore::INTERP.create_table
+
+    (Tree_ID = ['blt_tree'.freeze, TkUtil.untrust('00000')]).instance_eval{
+      @mutex = Mutex.new
+      def mutex; @mutex; end
+      freeze
+    }
+
+    def __keyonly_optkeys
+      {
+        # apply / find  command
+        'invert'=>nil, 'leafonly'=>nil, 'nocase'=>nil,
+
+        # apply / find / sort command
+        'path'=>nil,
+
+        # copy / restore / restorefile command
+        'overwrite'=>nil,
+
+        # copy command
+        'recurse'=>nil, 'tags'=>nil,
+
+        # sort command
+        'ascii'=>nil, 'decreasing'=>nil, 'disctionary'=>nil,
+        'integer'=>nil, 'real'=>nil, 'recurse'=>nil, 'reorder'=>nil,
+      }
+    end
+
+    def self.id2obj(id)
+      TreeID_TBL.mutex.synchronize{
+        TreeID_TBL[id]? TreeID_TBL[id]: id
+      }
+    end
+
+    def self.names(pat = None)
+      simplelist(tk_call('::blt::tree', 'names', pat)).collect{|name|
+        id2obj(name)
+      }
+    end
+
+    def self.destroy(*names)
+      tk_call('::blt::tree', 'destroy',
+              *(names.collect{|n| (n.kind_of?(Tk::BLT::Tree))? n.id: n }) )
+    end
+
+    def self.new(name = nil)
+      TreeID_TBL.mutex.synchronize{
+        if name && TreeID_TBL[name]
+          TreeID_TBL[name]
+        else
+          (obj = self.allocate).instance_eval{
+            initialize(name)
+            TreeID_TBL[@id] = self
+          }
+          obj
+        end
+      }
+    end
+
+    def initialzie(name = nil)
+      if name
+        @path = @id = name
+      else
+        Tree_ID.mutex.synchronize{
+          @path = @id = Tree_ID.join(TkCore::INTERP._ip_id_)
+          Tree_ID[1].succ!
+        }
+      end
+
+      tk_call('::blt::tree', 'create', @id)
+    end
+
+    def __destroy_hook__
+      Tk::BLT::Tree::Node::TreeNodeID_TBL.mutex.synchronize{
+        Tk::BLT::Tree::Node::TreeNodeID_TBL.delete(@path)
+      }
+      Tk::BLT::Tree::Tag::TreeTagID_TBL.mutex.synchronize{
+        Tk::BLT::Tree::Tag::TreeTagID_TBL.delete(@path)
+      }
+      Tk::BLT::Tree::Notify::NotifyID_TBL.mutex.synchronize{
+        Tk::BLT::Tree::Notify::NotifyID_TBL.delete(@path)
+      }
+      Tk::BLT::Tree::Trace::TraceID_TBL.mutex.synchronize{
+        Tk::BLT::Tree::Trace::TraceID_TBL.delete(@path)
+      }
+    end
+
+    def tagid(tag)
+      if tag.kind_of?(Tk::BLT::Tree::Node) ||
+          tag.kind_of?(Tk::BLT::Tree::Tag) ||
+          tag.kind_of?(Tk::BLT::Tree::Notify) ||
+          tag.kind_of?(Tk::BLT::Tree::Trace)
+        tag.id
+      else
+        tag  # maybe an Array of configure parameters
+      end
+    end
+
+    def destroy()
+      tk_call('::blt::tree', 'destroy', @id)
+      self
+    end
+
+    def ancestor(node1, node2)
+      Tk::BLT::Tree::Node.id2obj(self, tk_call('::blt::tree', 'ancestor',
+                                               tagid(node1), tagid(node2)))
+    end
+
+    def apply(node, keys={})
+      tk_call('::blt::tree', 'apply', tagid(node), __conv_keyonly_opts(keys))
+      self
+    end
+
+    def attach(tree_obj)
+      tk_call('::blt::tree', 'attach', tree_obj)
+      self
+    end
+
+    def children(node)
+      simplelist(tk_call('::blt::tree', 'children', tagid(node))).collect{|n|
+        Tk::BLT::Tree::Node.id2obj(self, n)
+      }
+    end
+
+    def copy(src, parent, keys={})
+      id = tk_call('::blt::tree', 'copy', tagid(src), tagid(parent),
+                   __conv_keyonly_opts(keys))
+      Tk::BLT::Tree::Node.new(self, nil, 'node'=>id)
+    end
+    def copy_to(src, dest_tree, parent, keys={})
+      return copy(src, parent, keys={}) unless dest_tree
+
+      id = tk_call('::blt::tree', 'copy', tagid(src), dest_tree,
+                   tagid(parent), __conv_keyonly_opts(keys))
+      Tk::BLT::Tree::Node.new(dest_tree, nil, 'node'=>id)
+    end
+
+    def degree(node)
+      number(tk_call('::blt::tree', 'degree', tagid(node)))
+    end
+
+    def delete(*nodes)
+      tk_call('::blt::tree', 'delete', *(nodes.collect{|node| tagid(node)}))
+      Tk::BLT::Tree::Node::TreeNodeID_TBL.mutex.synchronize{
+        nodes.each{|node|
+          if node.kind_of?(Tk::BLT::Tree::Node)
+            Tk::BLT::Tree::Node::TreeNodeID_TBL[@path].delete(node.id)
+          else
+            Tk::BLT::Tree::Node::TreeNodeID_TBL[@path].delete(node.to_s)
+          end
+        }
+      }
+      self
+    end
+
+    def depth(node)
+      number(tk_call('::blt::tree', 'depth', tagid(node)))
+    end
+
+    def dump(node)
+      simplelist(tk_call('::blt::tree', 'dump', tagid(node))).collect{|n|
+        simplelist(n)
+      }
+    end
+
+    def dump_to_file(node, file)
+      tk_call('::blt::tree', 'dumpfile', tagid(node), file)
+      self
+    end
+
+    def exist?(node, key=None)
+      bool(tk_call('::blt::tree', 'exists', tagid(node), key))
+    end
+
+    def find(node, keys={})
+      simplelist(tk_call('::blt::tree', 'find', tagid(node),
+                         __conv_keyonly_opts(keys))).collect{|n|
+        Tk::BLT::Tree::Node.id2obj(self, n)
+      }
+    end
+
+    def find_child(node, label)
+      ret = tk_call('::blt::tree', 'findchild', tagid(node), label)
+      (ret == '-1')? nil: Tk::BLT::Tree::Node.id2obj(self, ret)
+    end
+
+    def first_child(node)
+      ret = tk_call('::blt::tree', 'firstchild', tagid(node))
+      (ret == '-1')? nil: Tk::BLT::Tree::Node.id2obj(self, ret)
+    end
+
+    def get(node)
+      Hash[*simplelist(tk_call('::blt::tree', 'get', tagid(node)))]
+    end
+    def get_value(node, key, default_val=None)
+      tk_call('::blt::tree', 'get', tagid(node), key, default_val)
+    end
+
+    def index(node)
+      Tk::BLT::Tree::Node.id2obj(self,
+                                 tk_call('::blt::tree', 'index', tagid(node)))
+    end
+
+    def insert(parent, keys={})
+      id = tk_call('::blt::tree', 'insert', tagid(parent), keys)
+      Tk::BLT::Tree::Node.new(self, nil, 'node'=>id)
+    end
+
+    def ancestor?(node1, node2)
+      bool(tk_call('::blt::tree', 'is', 'ancestor',
+                   tagid(node1), tagid(node2)))
+    end
+    def before?(node1, node2)
+      bool(tk_call('::blt::tree', 'is', 'before',
+                   tagid(node1), tagid(node2)))
+    end
+    def leaf?(node)
+      bool(tk_call('::blt::tree', 'is', 'leaf', tagid(node)))
+    end
+    def link?(node)
+      bool(tk_call('::blt::tree', 'is', 'link', tagid(node)))
+    end
+    def root?(node)
+      bool(tk_call('::blt::tree', 'is', 'root', tagid(node)))
+    end
+
+    def keys(node, *nodes)
+      if nodes.empty?
+        simplelist(tk_call('blt::tree', 'keys', tagid(node)))
+      else
+        simplelist(tk_call('blt::tree', 'keys', tagid(node),
+                           *(nodes.collect{|n| tagid(n)}))).collect{|lst|
+          simplelist(lst)
+        }
+      end
+    end
+
+    def label(node, text=nil)
+      if text
+        tk_call('::blt::tree', 'label', tagid(node), text)
+        text
+      else
+        tk_call('::blt::tree', 'label', tagid(node))
+      end
+    end
+
+    def last_child(node)
+      ret = tk_call('::blt::tree', 'lastchild', tagid(node))
+      (ret == '-1')? nil: Tk::BLT::Tree::Node.id2obj(self, ret)
+    end
+
+    def link(parent, node, keys={})
+      ret = tk_call('::blt::tree', 'link', tagid(parent), tagid(node),
+                    __conv_keyonly_opts(keys))
+      (ret == '-1')? nil: Tk::BLT::Tree::Node.id2obj(self, ret)
+    end
+
+    def move(node, dest, keys={})
+      tk_call('::blt::tree', 'move', tagid(node), tagid(dest), keys)
+      self
+    end
+
+    def next(node)
+      ret = tk_call('::blt::tree', 'next', tagid(node))
+      (ret == '-1')? nil: Tk::BLT::Tree::Node.id2obj(self, ret)
+    end
+
+    def next_sibling(node)
+      ret = tk_call('::blt::tree', 'nextsibling', tagid(node))
+      (ret == '-1')? nil: Tk::BLT::Tree::Node.id2obj(self, ret)
+    end
+
+    def notify_create(*args, &b)
+      Tk::BLT::Tree::Notify.new(self, *args, &b)
+    end
+
+    def notify_delete(id)
+      if id.kind_of?(Tk::BLT::Tree::Notify)
+        id.delete
+      else
+        tk_call(@path, 'notify', 'delete', id)
+        Tk::BLT::Tree::Notify::NotifyID_TBL.mutex.synchronize{
+          Tk::BLT::Tree::Notify::NotifyID_TBL[@path].delete(id.to_s)
+        }
+      end
+      self
+    end
+
+    def notify_info(id)
+      lst = simplelist(tk_call(@path, 'notify', 'info', tagid(id)))
+      lst[0] = Tk::BLT::Tree::Notify.id2obj(self, lst[0])
+      lst[1] = simplelist(lst[1]).collect{|flag| flag[1..-1]}
+      lst[2] = tk_tcl2ruby(lst[2])
+      lst
+    end
+
+    def notify_names()
+      tk_call(@path, 'notify', 'names').collect{|id|
+        Tk::BLT::Tree::Notify.id2obj(self, id)
+      }
+    end
+
+    def parent(node)
+      ret = tk_call('::blt::tree', 'parent', tagid(node))
+      (ret == '-1')? nil: Tk::BLT::Tree::Node.id2obj(self, ret)
+    end
+
+    def fullpath(node)
+      tk_call('::blt::tree', 'path', tagid(node))
+    end
+
+    def position(node)
+      number(tk_call('::blt::tree', 'position', tagid(node)))
+    end
+
+    def previous(node)
+      ret = tk_call('::blt::tree', 'previous', tagid(node))
+      (ret == '-1')? nil: Tk::BLT::Tree::Node.id2obj(self, ret)
+    end
+
+    def prev_sibling(node)
+      ret = tk_call('::blt::tree', 'prevsibling', tagid(node))
+      (ret == '-1')? nil: Tk::BLT::Tree::Node.id2obj(self, ret)
+    end
+
+    def restore(node, str, keys={})
+      tk_call('::blt::tree', 'restore', tagid(node), str,
+              __conv_keyonly_opts(keys))
+      self
+    end
+    def restore_overwrite(node, str, keys={})
+      keys = __conv_keyonly_opts(keys)
+      keys.delete('overwrite')
+      keys.delete(:overwrite)
+      tk_call('::blt::tree', 'restore', tagid(node), str, '-overwrite', keys)
+      self
+    end
+
+    def restore_from_file(node, file, keys={})
+      tk_call('::blt::tree', 'restorefile', tagid(node), file,
+              __conv_keyonly_opts(keys))
+      self
+    end
+    def restore_overwrite_from_file(node, file, keys={})
+      keys = __conv_keyonly_opts(keys)
+      keys.delete('overwrite')
+      keys.delete(:overwrite)
+      tk_call('::blt::tree', 'restorefile', tagid(node), file,
+              '-overwrite', keys)
+      self
+    end
+
+    def root(node=None)
+      Tk::BLT::Tree::Node.id2obj(self, tk_call('::blt::tree', 'root',
+                                               tagid(node)))
+    end
+
+    def set(node, data)
+      unless data.kind_of?(Hash)
+        fail ArgumentError, 'Hash is expected for data'
+      end
+      args = []
+      data.each{|k, v|  args << k << v}
+      tk_call('::blt::tree', 'set', tagid(node), *args)
+      self
+    end
+
+    def size(node)
+      number(tk_call('::blt::tree', 'size', tagid(node)))
+    end
+
+    def sort(node, keys={})
+      tk_call('::blt::tree', 'sort', tagid(node), __conv_keyonly_opts(keys))
+      self
+    end
+
+    def tag_add(tag, *nodes)
+      tk_call(@path, 'tag', 'add', tagid(tag), *(nodes.collect{|n| tagid(n)}))
+      self
+    end
+
+    def tag_delete(tag, *nodes)
+      tk_call(@path, 'tag', 'delete', tagid(tag),
+              *(nodes.collect{|n| tagid(n)}))
+      self
+    end
+
+    def tag_forget(tag)
+      tag = tag.id if tag.kind_of?(Tk::BLT::Tree::Tag)
+      tk_call(@path, 'tag', 'forget', tag)
+      TreeTagID_TBL.mutex.synchronize{
+        TreeTagID_TBL[@path].delete(tag)
+      }
+      self
+    end
+
+    def tag_get(node, *patterns)
+      simplelist(tk_call(@tpath, 'tag', 'get', tagid(node),
+                         *(patterns.collect{|pat| tagid(pat)}))).collect{|str|
+        Tk::BLT::Tree::Tag.id2obj(self, str)
+      }
+    end
+
+    def tag_names(node = None)
+      simplelist(tk_call(@tpath, 'tag', 'names', tagid(node))).collect{|str|
+        Tk::BLT::Tree::Tag.id2obj(self, str)
+      }
+    end
+
+    def tag_nodes(tag)
+      simplelist(tk_call(@tpath, 'tag', 'nodes', tagid(tag))).collect{|node|
+        Tk::BLT::Tree::Node.id2obj(self, node)
+      }
+    end
+
+    def tag_set(node, *tags)
+      tk_call(@path, 'tag', 'set', tagid(node), *(tags.collect{|t| tagid(t)}))
+      self
+    end
+
+    def tag_unset(node, *tags)
+      tk_call(@path, 'tag', 'unset', tagid(node),
+              *(tags.collect{|t| tagid(t)}))
+      self
+    end
+
+    def trace_create(*args, &b)
+      Tk::BLT::Tree::Trace.new(self, *args, &b)
+    end
+
+=begin
+    def trace_delete(*args)
+      args.each{|id|
+        if id.kind_of?(Tk::BLT::Tree::Trace)
+          id.delete
+        else
+          tk_call(@path, 'trace', 'delete', id)
+          Tk::BLT::Tree::Trace::TraceID_TBL[@path].delete(id.to_s)
+        end
+        self
+      }
+    end
+=end
+    def trace_delete(*args)
+      args = args.collect{|id| tagid(id)}
+      tk_call(@path, 'trace', 'delete', *args)
+      Tk::BLT::Tree::Trace::TraceID_TBL.mutex.synchronize{
+        args.each{|id| Tk::BLT::Tree::Trace::TraceID_TBL[@path].delete(id.to_s)}
+      }
+      self
+    end
+
+    def trace_info(id)
+      lst = simplelist(tk_call(@path, 'trace', 'info', tagid(id)))
+      lst[0] = Tk::BLT::Tree::Trace.id2obj(self, lst[0])
+      lst[2] = simplelist(lst[2])
+      lst[3] = tk_tcl2ruby(lst[3])
+      lst
+    end
+
+    def trace_names()
+      tk_call(@path, 'trace', 'names').collect{|id|
+        Tk::BLT::Tree::Trace.id2obj(self, id)
+      }
+    end
+
+    def type(node, key)
+      tk_call('::blt::tree', 'type', tagid(node), key)
+    end
+
+    def unset(node, *keys)
+      tk_call('::blt::tree', 'unset', tagid(node), *keys)
+      self
+    end
+
+    def values(node, key=None)
+      simplelist(tk_call('::blt::tree', 'values', tagid(node), key))
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/treeview.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/treeview.rb
new file mode 100644
index 0000000..38d9041
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/treeview.rb
@@ -0,0 +1,1287 @@
+#
+#  tkextlib/blt/treeview.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+require 'tk/validation.rb'
+
+module Tk::BLT
+  class Treeview < TkWindow
+    module ConfigMethod
+    end
+
+    module TagOrID_Methods
+    end
+
+    class Node < TkObject
+    end
+
+    class Tag < TkObject
+    end
+  end
+
+  class Hiertable < Treeview
+  end
+end
+
+######################################
+
+module Tk::BLT::Treeview::ConfigMethod
+  include TkItemConfigMethod
+
+  def __item_boolval_optkeys(id)
+    case id
+    when Array
+      # id := [ 'column', name ]
+      ['edit', 'hide']
+    when 'sort'
+      ['decreasing']
+    else
+      []
+    end
+  end
+  private :__item_boolval_optkeys
+
+  def __item_strval_optkeys(id)
+    case id
+    when Array
+      # id := [ 'column', name ]
+      super() << 'titleforeground' << 'titleshadow'
+    when 'sort'
+      ['decreasing']
+    else
+      []
+    end
+  end
+  private :__item_strval_optkeys
+
+  def __item_listval_optkeys(id)
+    case id
+    when 'entry'
+      ['bindtags']
+    else
+      []
+    end
+  end
+  private :__item_listval_optkeys
+
+  def __item_cget_cmd(id)
+    if id.kind_of?(Array)
+      # id := [ type, name ]
+      [self.path, id[0], 'cget', id[1]]
+    else
+      [self.path, id, 'cget']
+    end
+  end
+  private :__item_cget_cmd
+
+  def __item_config_cmd(id)
+    if id.kind_of?(Array)
+      # id := [ type, name ]
+      [self.path, id[0], 'configure', id[1]]
+    else
+      [self.path, id, 'configure']
+    end
+  end
+  private :__item_config_cmd
+
+  def __item_pathname(id)
+    if id.kind_of?(Array)
+      id = tagid(id[1])
+    end
+    [self.path, id].join(';')
+  end
+  private :__item_pathname
+
+  def column_cget_tkstring(name, option)
+    itemcget_tkstring(['column', name], option)
+  end
+  def column_cget(name, option)
+    itemcget(['column', name], option)
+  end
+  def column_cget_strict(name, option)
+    itemcget_strict(['column', name], option)
+  end
+  def column_configure(name, slot, value=None)
+    itemconfigure(['column', name], slot, value)
+  end
+  def column_configinfo(name, slot=nil)
+    itemconfiginfo(['column', name], slot)
+  end
+  def current_column_configinfo(name, slot=nil)
+    current_itemconfiginfo(['column', name], slot)
+  end
+
+  def button_cget_tkstring(option)
+    itemcget_tkstring('button', option)
+  end
+  def button_cget(option)
+    itemcget('button', option)
+  end
+  def button_cget_strict(option)
+    itemcget_strict('button', option)
+  end
+  def button_configure(slot, value=None)
+    itemconfigure('button', slot, value)
+  end
+  def button_configinfo(slot=nil)
+    itemconfiginfo('button', slot)
+  end
+  def current_button_configinfo(slot=nil)
+    current_itemconfiginfo('button', slot)
+  end
+
+  def entry_cget_tkstring(option)
+    itemcget_tkstring('entry', option)
+  end
+  def entry_cget(option)
+    ret = itemcget('entry', option)
+    if option == 'bindtags' || option == :bindtags
+      ret.collect{|tag| TkBindTag.id2obj(tag)}
+    else
+      ret
+    end
+  end
+  def entry_cget_strict(option)
+    ret = itemcget_strict('entry', option)
+    if option == 'bindtags' || option == :bindtags
+      ret.collect{|tag| TkBindTag.id2obj(tag)}
+    else
+      ret
+    end
+  end
+  def entry_configure(slot, value=None)
+    itemconfigure('entry', slot, value)
+  end
+  def entry_configinfo(slot=nil)
+    ret = itemconfiginfo('entry', slot)
+
+    if TkComm::GET_CONFIGINFO_AS_ARRAY
+      if slot
+        if slot == 'bindtags' || slot == :bindtags
+          ret[-2] = ret[-2].collect{|tag| TkBindTag.id2obj(tag)}
+          ret[-1] = ret[-1].collect{|tag| TkBindTag.id2obj(tag)}
+        end
+      else
+        inf = ret.assoc('bindtags')
+        inf[-2] = inf[-2].collect{|tag| TkBindTag.id2obj(tag)}
+        inf[-1] = inf[-1].collect{|tag| TkBindTag.id2obj(tag)}
+      end
+
+    else # ! TkComm::GET_CONFIGINFO_AS_ARRAY
+      if (inf = ret['bindtags'])
+        inf[-2] = inf[-2].collect{|tag| TkBindTag.id2obj(tag)}
+        inf[-1] = inf[-1].collect{|tag| TkBindTag.id2obj(tag)}
+        ret['bindtags'] = inf
+      end
+    end
+
+    ret
+  end
+  def current_entry_configinfo(slot=nil)
+    ret = current_itemconfiginfo('entry', slot)
+
+    if (val = ret['bindtags'])
+      ret['bindtags'] = val.collect{|tag| TkBindTag.id2obj(tag)}
+    end
+
+    ret
+  end
+
+  def sort_cget_tkstring(option)
+    itemcget_tkstring('sort', option)
+  end
+  def sort_cget(option)
+    itemcget('sort', option)
+  end
+  def sort_cget_strict(option)
+    itemcget_strict('sort', option)
+  end
+  def sort_configure(slot, value=None)
+    itemconfigure('sort', slot, value)
+  end
+  def sort_configinfo(slot=nil)
+    itemconfiginfo('sort', slot)
+  end
+  def current_sort_configinfo(slot=nil)
+    current_itemconfiginfo('sort', slot)
+  end
+
+  def text_cget_tkstring(option)
+    itemcget_tkstring('text', option)
+  end
+  def text_cget(option)
+    itemcget('text', option)
+  end
+  def text_cget_strict(option)
+    itemcget_strict('text', option)
+  end
+  def text_configure(slot, value=None)
+    itemconfigure('text', slot, value)
+  end
+  def text_configinfo(slot=nil)
+    itemconfiginfo('text', slot)
+  end
+  def current_text_configinfo(slot=nil)
+    current_itemconfiginfo('text', slot)
+  end
+
+  private :itemcget_tkstring, :itemcget, :itemcget_strict
+  private :itemconfigure, :itemconfiginfo, :current_itemconfiginfo
+end
+
+class Tk::BLT::Treeview
+  TkCommandNames = ['::blt::treeview'.freeze].freeze
+  WidgetClassName = 'TreeView'.freeze
+  WidgetClassNames[WidgetClassName] ||= self
+
+  include Scrollable
+  include ValidateConfigure
+  include ItemValidateConfigure
+  include Tk::BLT::Treeview::ConfigMethod
+
+  ########################
+
+  def __boolval_optkeys
+    ['autocreate', 'allowduplicates', 'exportselection', 'flat', 'hideroot',
+      'newtags', 'showtitles', 'sortselection']
+  end
+  private :__boolval_optkeys
+
+  def __strval_optkeys
+    super() + ['focusforeground', 'linecolor', 'separator', 'trim']
+  end
+  private :__strval_optkeys
+
+  ########################
+
+  class OpenCloseCommand < TkValidateCommand
+    class ValidateArgs < TkUtil::CallbackSubst
+      KEY_TBL = [
+        [ ?W, ?w, :widget ],
+        [ ?p, ?s, :name ],
+        [ ?P, ?s, :fullpath ],
+        [ ?#, ?x, :node_id ],
+        nil
+      ]
+
+      PROC_TBL = [
+        [ ?x, TkComm.method(:num_or_str) ],
+        [ ?s, TkComm.method(:string) ],
+        [ ?w, TkComm.method(:window) ],
+        nil
+      ]
+
+=begin
+      # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
+      KEY_TBL.map!{|inf|
+        if inf.kind_of?(Array)
+          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
+          inf[1] = inf[1].getbyte(0) if inf[1].kind_of?(String)
+        end
+        inf
+      }
+
+      PROC_TBL.map!{|inf|
+        if inf.kind_of?(Array)
+          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
+        end
+        inf
+      }
+=end
+
+      _setup_subst_table(KEY_TBL, PROC_TBL);
+
+      def self.ret_val(val)
+        val
+      end
+    end
+
+    def self._config_keys
+      ['opencommand', 'closecomand']
+    end
+  end
+
+  def __validation_class_list
+    super() << OpenCloseCommand
+  end
+
+  Tk::ValidateConfigure.__def_validcmd(binding, OpenCloseCommand)
+
+  ########################
+
+  def __item_validation_class_list(id)
+    case id
+    when 'entry'
+      super(id) << OpenCloseCommand
+    else
+      super(id)
+    end
+  end
+
+  Tk::ItemValidateConfigure.__def_validcmd(binding, OpenCloseCommand)
+
+  ########################
+
+  def __destroy_hook__
+    Tk::BLT::Treeview::Node::TreeNodeID_TBL.mutex.synchronize{
+      Tk::BLT::Treeview::Node::TreeNodeID_TBL.delete(@path)
+    }
+    Tk::BLT::Treeview::Tag::TreeTagID_TBL.mutex.synchronize{
+      Tk::BLT::Treeview::Tag::TreeTagID_TBL.delete(@path)
+    }
+  end
+
+  def tagid(tag)
+    if tag.kind_of?(Tk::BLT::Treeview::Node) \
+      || tag.kind_of?(Tk::BLT::Treeview::Tag)
+      tag.id
+    else
+      tag  # maybe an Array of configure parameters
+    end
+  end
+  private :tagid
+
+  def tagid2obj(tagid)
+    if tagid.kind_of?(Integer)
+      Tk::BLT::Treeview::Node.id2obj(self, tagid.to_s)
+    elsif tagid.kind_of?(String)
+      if tagid =~ /^\d+$/
+        Tk::BLT::Treeview::Node.id2obj(self, tagid)
+      else
+        Tk::BLT::Treeview::Tag.id2obj(self, tagid)
+      end
+    else
+      tagid
+    end
+  end
+
+  def bbox(*tags)
+    list(tk_send('bbox', *(tags.collect{|tag| tagid(tag)})))
+  end
+
+  def screen_bbox(*tags)
+    list(tk_send('bbox', '-screen', *(tags.collect{|tag| tagid(tag)})))
+  end
+
+  def tag_bind(tag, seq, *args)
+    if TkComm._callback_entry?(args[0]) || !block_given?
+      cmd = args.shift
+    else
+      cmd = Proc.new
+    end
+    _bind([@path, 'bind', tagid(tag)], seq, cmd, *args)
+    self
+  end
+  def tag_bind_append(tag, seq, *args)
+    if TkComm._callback_entry?(args[0]) || !block_given?
+      cmd = args.shift
+    else
+      cmd = Proc.new
+    end
+    _bind_append([@path, 'bind', tagid(tag)], seq, cmd, *args)
+    self
+  end
+  def tag_bind_remove(tag, seq)
+    _bind_remove([@path, 'bind', tagid(tag)], seq)
+    self
+  end
+  def tag_bindinfo(tag, seq=nil)
+    _bindinfo([@path, 'bind', tagid(tag)], seq)
+  end
+
+  def button_activate(tag)
+    tk_send('button', 'activate', tagid(tag))
+    self
+  end
+
+  def button_bind(tag, seq, *args)
+    if TkComm._callback_entry?(args[0]) || !block_given?
+      cmd = args.shift
+    else
+      cmd = Proc.new
+    end
+    _bind([@path, 'button', 'bind', tagid(tag)], seq, cmd, *args)
+    self
+  end
+  def button_bind_append(tag, seq, *args)
+    if TkComm._callback_entry?(args[0]) || !block_given?
+      cmd = args.shift
+    else
+      cmd = Proc.new
+    end
+    _bind_append([@path, 'button', 'bind', tagid(tag)], seq, cmd, *args)
+    self
+  end
+  def button_bind_remove(tag, seq)
+    _bind_remove([@path, 'button', 'bind', tagid(tag)], seq)
+    self
+  end
+  def button_bindinfo(tag, seq=nil)
+    _bindinfo([@path, 'button', 'bind', tagid(tag)], seq)
+  end
+
+  def close(*tags)
+    tk_send('close', *(tags.collect{|tag| tagid(tag)}))
+    self
+  end
+  def close_recurse(*tags)
+    tk_send('close', '-recurse', *(tags.collect{|tag| tagid(tag)}))
+    self
+  end
+
+  def column_activate(column=None)
+    if column == None
+      tk_send('column', 'activate')
+    else
+      tk_send('column', 'activate', column)
+      self
+    end
+  end
+
+  def column_delete(*fields)
+    tk_send('column', 'delete', *fields)
+    self
+  end
+  def column_insert(pos, field, *opts)
+    tk_send('column', 'insert', pos, field, *opts)
+    self
+  end
+  def column_invoke(field)
+    tk_send('column', 'invoke', field)
+    self
+  end
+  def column_move(name, dest)
+    tk_send('column', 'move', name, dest)
+    self
+  end
+  def column_names()
+    simplelist(tk_send('column', 'names'))
+  end
+  def column_nearest(x, y=None)
+    tk_send('column', 'nearest', x, y)
+  end
+
+  def curselection
+    simplelist(tk_send('curselection')).collect{|id| tagid2obj(id)}
+  end
+
+  def delete(*tags)
+    tk_send('delete', *(tags.collect{|tag| tagid(tag)}))
+    self
+  end
+
+  def entry_activate(tag)
+    tk_send('entry', 'activate', tagid(tag))
+    self
+  end
+  def entry_children(tag, first=None, last=None)
+    simplelist(tk_send('entry', 'children', tagid(tag),
+                       first, last)).collect{|id| tagid2obj(id)}
+  end
+  def entry_delete(tag, first=None, last=None)
+    tk_send('entry', 'delete', tagid(tag), first, last)
+  end
+  def entry_before?(tag1, tag2)
+    bool(tk_send('entry', 'isbefore', tagid(tag1), tagid(tag2)))
+  end
+  def entry_hidden?(tag)
+    bool(tk_send('entry', 'ishidden', tagid(tag)))
+  end
+  def entry_open?(tag)
+    bool(tk_send('entry', 'isopen', tagid(tag)))
+  end
+
+  def entry_size(tag)
+    number(tk_send('entry', 'size', tagid(tag)))
+  end
+  def entry_size_recurse(tag)
+    number(tk_send('entry', 'size', '-recurse', tagid(tag)))
+  end
+
+  def _search_flags(keys)
+    keys = _symbolkey2str(keys)
+    keys['exact'] = None if keys.delete('exact')
+    keys['glob'] = None if keys.delete('glob')
+    keys['regexp'] = None if keys.delete('regexp')
+    keys['nonmatching'] = None if keys.delete('nonmatching')
+  end
+  private :_search_flags
+
+  ################################
+
+  class FindExecFlagValue < TkValidateCommand
+    class ValidateArgs < TkUtil::CallbackSubst
+      KEY_TBL = [
+        [ ?W, ?w, :widget ],
+        [ ?p, ?s, :name ],
+        [ ?P, ?s, :fullpath ],
+        [ ?#, ?x, :node_id ],
+        nil
+      ]
+
+      PROC_TBL = [
+        [ ?x, TkComm.method(:num_or_str) ],
+        [ ?s, TkComm.method(:string) ],
+        [ ?w, TkComm.method(:window) ],
+        nil
+      ]
+
+=begin
+      # for Ruby m17n :: ?x --> String --> char-code ( getbyte(0) )
+      KEY_TBL.map!{|inf|
+        if inf.kind_of?(Array)
+          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
+          inf[1] = inf[1].getbyte(0) if inf[1].kind_of?(String)
+        end
+        inf
+      }
+
+      PROC_TBL.map!{|inf|
+        if inf.kind_of?(Array)
+          inf[0] = inf[0].getbyte(0) if inf[0].kind_of?(String)
+        end
+        inf
+      }
+=end
+
+      _setup_subst_table(KEY_TBL, PROC_TBL);
+
+      def self.ret_val(val)
+        val
+      end
+    end
+
+    def self._config_keys
+      []
+    end
+  end
+
+  def _find_exec_flag_value(val)
+    if val.kind_of?(Array)
+      cmd, *args = val
+      #FindExecFlagValue.new(cmd, args.join(' '))
+      FindExecFlagValue.new(cmd, *args)
+    elsif TkComm._callback_entry?(val)
+      FindExecFlagValue.new(val)
+    else
+      val
+    end
+  end
+
+  ################################
+
+  def find(first, last, keys={})
+    keys = _search_flags(keys)
+    keys['exec'] = _find_exec_flag_value(keys['exec']) if keys.key?('exec')
+    args = hash_kv(keys) << '--' << tagid(first) << tagid(last)
+    simplelist(tk_send('find', *args)).collect{|id| tagid2obj(id)}
+  end
+
+  def tag_focus(tag)
+    tk_send('focus', tagid(tag))
+    self
+  end
+  def get(*tags)
+    simplelist(tk_send('get', *(tags.collect{|tag| tagid(tag)})))
+  end
+  def get_full(*tags)
+    simplelist(tk_send('get', '-full', *(tags.collect{|tag| tagid(tag)})))
+  end
+
+  def hide(*tags)
+    if tags[-1].kind_of?(Hash)
+      keys = tags.pop
+    else
+      keys = {}
+    end
+    keys = _search_flags(keys)
+    args = hash_kv(keys) << '--'
+    args.concat(tags.collect{|t| tagid(t)})
+    tk_send('hide', *args)
+    self
+  end
+
+  def index(str)
+    tagid2obj(tk_send('index', str))
+  end
+  def index_at(tag, str)
+    tagid2obj(tk_send('index', '-at', tagid(tag), str))
+  end
+  def index_at_path(tag, str)
+    tagid2obj(tk_send('index', '-at', tagid(tag), '-path', str))
+  end
+
+  def insert(pos, parent=nil, keys={})
+    Tk::BLT::Treeview::Node.new(pos, parent, keys)
+  end
+  def insert_at(tag, pos, parent=nil, keys={})
+    if parent.kind_of?(Hash)
+      keys = parent
+      parent = nil
+    end
+
+    keys = _symbolkey2str(keys)
+    keys['at'] = tagid(tag)
+
+    Tk::BLT::Treeview::Node.new(pos, parent, keys)
+  end
+
+  def move_before(tag, dest)
+    tk_send('move', tagid(tag), 'before', tagid(dest))
+    self
+  end
+  def move_after(tag, dest)
+    tk_send('move', tagid(tag), 'after', tagid(dest))
+    self
+  end
+  def move_into(tag, dest)
+    tk_send('move', tagid(tag), 'into', tagid(dest))
+    self
+  end
+
+  def nearest(x, y, var=None)
+    tagid2obj(tk_send('nearest', x, y, var))
+  end
+
+  def open(*tags)
+    tk_send('open', *(tags.collect{|tag| tagid(tag)}))
+    self
+  end
+  def open_recurse(*tags)
+    tk_send('open', '-recurse', *(tags.collect{|tag| tagid(tag)}))
+    self
+  end
+
+  def range(first, last)
+    simplelist(tk_send('range', tagid(first), tagid(last))).collect{|id|
+      tagid2obj(id)
+    }
+  end
+  def range_open(first, last)
+    simplelist(tk_send('range', '-open',
+                       tagid(first), tagid(last))).collect{|id|
+      tagid2obj(id)
+    }
+  end
+
+  def scan_mark(x, y)
+    tk_send_without_enc('scan', 'mark', x, y)
+    self
+  end
+  def scan_dragto(x, y)
+    tk_send_without_enc('scan', 'dragto', x, y)
+    self
+  end
+
+  def see(tag)
+    tk_send_without_enc('see', tagid(tag))
+    self
+  end
+  def see_anchor(anchor, tag)
+    tk_send_without_enc('see', '-anchor', anchor, tagid(tag))
+    self
+  end
+
+  def selection_anchor(tag)
+    tk_send_without_enc('selection', 'anchor', tagid(tag))
+    self
+  end
+  def selection_cancel()
+    tk_send_without_enc('selection', 'cancel')
+    self
+  end
+  def selection_clear(first, last=None)
+    tk_send_without_enc('selection', 'clear', tagid(first), tagid(last))
+    self
+  end
+  def selection_clear_all()
+    tk_send_without_enc('selection', 'clearall')
+    self
+  end
+  def selection_mark(tag)
+    tk_send_without_enc('selection', 'mark', tagid(tag))
+    self
+  end
+  def selection_include?(tag)
+    bool(tk_send('selection', 'include', tagid(tag)))
+  end
+  def selection_present?()
+    bool(tk_send('selection', 'present'))
+  end
+  def selection_set(first, last=None)
+    tk_send_without_enc('selection', 'set', tagid(first), tagid(last))
+    self
+  end
+  def selection_toggle(first, last=None)
+    tk_send_without_enc('selection', 'toggle', tagid(first), tagid(last))
+    self
+  end
+
+  def show(*tags)
+    if tags[-1].kind_of?(Hash)
+      keys = tags.pop
+    else
+      keys = {}
+    end
+    keys = _search_flags(keys)
+    args = hash_kv(keys) << '--'
+    args.concat(tags.collect{|t| tagid(t)})
+    tk_send('show', *args)
+    self
+  end
+
+  def sort_auto(mode)
+    tk_send('sort', 'auto', mode)
+    self
+  end
+  def sort_auto=(mode)
+    tk_send('sort', 'auto', mode)
+    mode
+  end
+  def sort_auto?
+    bool(tk_send('sort', 'auto'))
+  end
+  def sort_once(*tags)
+    tk_send('sort', 'once', *(tags.collect{|tag| tagid(tag)}))
+    self
+  end
+  def sort_once_recurse(*tags)
+    tk_send('sort', 'once', '-recurse', *(tags.collect{|tag| tagid(tag)}))
+    self
+  end
+
+  def tag_add(tag, *ids)
+    tk_send('tag', 'add', tagid(tag), *ids)
+    self
+  end
+  def tag_delete(tag, *ids)
+    tk_send('tag', 'delete', tagid(tag), *ids)
+    self
+  end
+  def tag_forget(tag)
+    tk_send('tag', 'forget', tagid(tag))
+    self
+  end
+  def tag_names(id=nil)
+    id = (id)? tagid(id): None
+
+    simplelist(tk_send('tag', 'nodes', id)).collect{|tag|
+      Tk::BLT::Treeview::Tag.id2obj(self, tag)
+    }
+  end
+  def tag_nodes(tag)
+    simplelist(tk_send('tag', 'nodes', tagid(tag))).collect{|id|
+      Tk::BLT::Treeview::Node.id2obj(self, id)
+    }
+  end
+
+  def text_apply
+    tk_send('text', 'apply')
+    self
+  end
+  def text_cancel
+    tk_send('text', 'cancel')
+    self
+  end
+
+  def text_delete(first, last)
+    tk_send('text', 'delete', first, last)
+    self
+  end
+  def text_get(x, y)
+    tk_send('text', 'get', x, y)
+  end
+  def text_get_root(x, y)
+    tk_send('text', 'get', '-root', x, y)
+  end
+  def text_icursor(idx)
+    tk_send('text', 'icursor', idx)
+    self
+  end
+  def text_index(idx)
+    num_or_str(tk_send('text', 'index', idx))
+  end
+  def text_insert(idx, str)
+    tk_send('text', 'insert', idx, str)
+    self
+  end
+
+  def text_selection_adjust(idx)
+    tk_send('text', 'selection', 'adjust', idx)
+    self
+  end
+  def text_selection_clear
+    tk_send('text', 'selection', 'clear')
+    self
+  end
+  def text_selection_from(idx)
+    tk_send('text', 'selection', 'from', idx)
+    self
+  end
+  def text_selection_present
+    num_or_str(tk_send('text', 'selection', 'present'))
+  end
+  def text_selection_range(start, last)
+    tk_send('text', 'selection', 'range', start, last)
+    self
+  end
+  def text_selection_to(idx)
+    tk_send('text', 'selection', 'to', idx)
+    self
+  end
+
+  def toggle(tag)
+    tk_send('toggle', tagid(tag))
+    self
+  end
+end
+
+######################################
+
+module Tk::BLT::Treeview::TagOrID_Methods
+  def bbox
+    @tree.bbox(self)
+  end
+  def screen_bbox
+    @tree.screen_bbox(self)
+  end
+
+  def bind(seq, *args)
+    @tree.tag_bind(self, seq, *args)
+    self
+  end
+  def bind_append(seq, *args)
+    @tree.tag_bind_append(self, seq, *args)
+    self
+  end
+  def bind_remove(seq)
+    @tree.tag_bind_remove(self, seq)
+    self
+  end
+  def bindinfo(seq=nil)
+    @tree.tag_bindinfo(self, seq)
+  end
+
+  def button_activate
+    @tree.button_activate(self)
+    self
+  end
+
+  def button_bind(seq, *args)
+    @tree.button_bind(self, seq, *args)
+    self
+  end
+  def button_bind_append(seq, *args)
+    @tree.button_bind_append(self, seq, *args)
+    self
+  end
+  def button_bind_remove(seq)
+    @tree.button_bind_remove(self, seq)
+    self
+  end
+  def button_bindinfo(seq=nil)
+    @tree.button_bindinfo(self, seq)
+  end
+
+  def close
+    @tree.close(self)
+    self
+  end
+  def close_recurse
+    @tree.close_recurse(self)
+    self
+  end
+
+  def delete
+    @tree.delete(self)
+    self
+  end
+
+  def entry_activate
+    @tree.entry_activate(self)
+    self
+  end
+  def entry_children(first=None, last=None)
+    @tree.entry_children(self, first, last)
+  end
+  def entry_delete(first=None, last=None)
+    @tree.entry_delete(self, first, last)
+  end
+  def entry_before?(tag)
+    @tree.entry_before?(self, tag)
+  end
+  def entry_hidden?
+    @tree.entry_before?(self)
+  end
+  def entry_open?
+    @tree.entry_open?(self)
+  end
+
+  def entry_size
+    @tree.entry_size(self)
+  end
+  def entry_size_recurse
+    @tree.entry_size_recurse(self)
+  end
+
+  def focus
+    @tree.tag_focus(self)
+    self
+  end
+
+  def get
+    @tree.get(self)
+  end
+  def get_full
+    @tree.get_full(self)
+  end
+
+  def hide
+    @tree.hide(self)
+    self
+  end
+
+  def index(str)
+    @tree.index_at(self, str)
+  end
+  def index_path(str)
+    @tree.index_at_path(self, str)
+  end
+
+  def insert(pos, parent=nil, keys={})
+    @tree.insert_at(self, pos, parent, keys)
+  end
+
+  def move_before(dest)
+    @tree.move_before(self, dest)
+    self
+  end
+  def move_after(dest)
+    @tree.move_after(self, dest)
+    self
+  end
+  def move_into(dest)
+    @tree.move_into(self, dest)
+    self
+  end
+
+  def open
+    @tree.open(self)
+    self
+  end
+  def open_recurse
+    @tree.open_recurse(self)
+    self
+  end
+
+  def range_to(tag)
+    @tree.range(self, tag)
+  end
+  def range_open_to(tag)
+    @tree.range(self, tag)
+  end
+
+  def see
+    @tree.see(self)
+    self
+  end
+  def see_anchor(anchor)
+    @tree.see_anchor(anchor, self)
+    self
+  end
+
+  def selection_anchor
+    @tree.selection_anchor(self)
+    self
+  end
+  def selection_clear
+    @tree.selection_clear(self)
+    self
+  end
+  def selection_mark
+    @tree.selection_mark(self)
+    self
+  end
+  def selection_include?
+    @tree.selection_include?(self)
+  end
+  def selection_set
+    @tree.selection_set(self)
+    self
+  end
+  def selection_toggle
+    @tree.selection_toggle(self)
+    self
+  end
+
+  def show
+    @tree.show(self)
+    self
+  end
+
+  def sort_once
+    @tree.sort_once(self)
+    self
+  end
+  def sort_once_recurse
+    @tree.sort_once_recurse(self)
+    self
+  end
+
+  def toggle
+    @tree.toggle(self)
+    self
+  end
+end
+
+######################################
+
+class Tk::BLT::Treeview::Node < TkObject
+  include Tk::BLT::Treeview::TagOrID_Methods
+
+  TreeNodeID_TBL = TkCore::INTERP.create_table
+
+  (TreeNode_ID = ['blt_treeview_node'.freeze, TkUtil.untrust('00000')]).instance_eval{
+    @mutex = Mutex.new
+    def mutex; @mutex; end
+    freeze
+  }
+
+  TkCore::INTERP.init_ip_env{
+    TreeNodeID_TBL.mutex.synchronize{ TreeNodeID_TBL.clear }
+  }
+
+  def self.id2obj(tree, id)
+    tpath = tree.path
+    TreeNodeID_TBL.mutex.synchronize{
+      if TreeNodeID_TBL[tpath]
+        if TreeNodeID_TBL[tpath][id]
+          TreeNodeID_TBL[tpath][id]
+        else
+          begin
+            # self.new(tree, nil, nil, 'node'=>Integer(id))
+            unless (tk_call(@tpath, 'get', id)).empty?
+              id = Integer(id)
+              (obj = self.allocate).instance_eval{
+                @parent = @tree = tree
+                @tpath = @parent.path
+                @path = @id = id
+                TreeNodeID_TBL[@tpath] ||= {}
+                TreeNodeID_TBL[@tpath][@id] = self
+              }
+              obj
+            else
+              id
+            end
+          rescue
+            id
+          end
+        end
+      else
+        id
+      end
+    }
+  end
+
+  def self.new(tree, pos, parent=nil, keys={})
+    if parent.kind_of?(Hash)
+      keys = parent
+      parent = nil
+    end
+
+    keys = _symbolkey2str(keys)
+    tpath = tree.path
+
+    TreeNodeID_TBL.mutex.synchronize{
+      TreeNodeID_TBL[tpath] ||= {}
+      if (id = keys['node']) && (obj = TreeNodeID_TBL[tpath][id])
+        keys.delete('node')
+        tk_call(tree.path, 'move', id, pos, parent) if parent
+        return obj
+      end
+
+      #super(tree, pos, parent, keys)
+      (obj = self.allocate).instance_eval{
+        initialize(tree, pos, parent, keys)
+        TreeNodeID_TBL[tpath][@id] = self
+      }
+      obj
+    }
+  end
+
+  def initialize(tree, pos, parent, keys)
+    @parent = @tree = tree
+    @tpath = @parent.path
+
+    if (id = keys['node'])
+      # if tk_call(@tpath, 'get', id).empty?
+      #   fail RuntimeError, "not exist the node '#{id}'"
+      # end
+      @path = @id = id
+      tk_call(@tpath, 'move', @id, pos, tagid(parent)) if parent
+      configure(keys) if keys && ! keys.empty?
+    else
+      name = nil
+      TreeNode_ID.mutex.synchronize{
+        name = TreeNode_ID.join(TkCore::INTERP._ip_id_).freeze
+        TreeNode_ID[1].succ!
+      }
+
+      at = keys.delete['at']
+
+      if parent
+        if parent.kind_of?(Tk::BLT::Treeview::Node) ||
+            parent.kind_of?(Tk::BLT::Treeview::Tag)
+          path = [get_full(parent.id)[0], name]
+          at = nil # ignore 'at' option
+        else
+          path = [parent.to_s, name]
+        end
+      else
+        path = name
+      end
+
+      if at
+        @id = tk_call(@tpath, 'insert', '-at', tagid(at), pos, path, keys)
+      else
+        @id = tk_call(@tpath, 'insert', pos, path, keys)
+      end
+      @path = @id
+    end
+  end
+
+  def id
+    @id
+  end
+end
+
+######################################
+
+class Tk::BLT::Treeview::Tag < TkObject
+  include Tk::BLT::Treeview::TagOrID_Methods
+
+  TreeTagID_TBL = TkCore::INTERP.create_table
+
+  (TreeTag_ID = ['blt_treeview_tag'.freeze, TkUtil.untrust('00000')]).instance_eval{
+    @mutex = Mutex.new
+    def mutex; @mutex; end
+    freeze
+  }
+
+  TkCore::INTERP.init_ip_env{
+    TreeTagID_TBL.mutex.synchronize{ TreeTagID_TBL.clear }
+  }
+
+  def self.id2obj(tree, name)
+    tpath = tree.path
+    TreeTagID_TBL.mutex.synchronize{
+      if TreeTagID_TBL[tpath]
+        if TreeTagID_TBL[tpath][name]
+          TreeTagID_TBL[tpath][name]
+        else
+          #self.new(tree, name)
+          (obj = self.allocate).instance_eval{
+            @parent = @tree = tree
+            @tpath = @parent.path
+            @path = @id = name
+            TreeTagID_TBL[@tpath] = {} unless TreeTagID_TBL[@tpath]
+            TreeTagID_TBL[@tpath][@id] = self
+          }
+          obj
+        end
+      else
+        id
+      end
+    }
+  end
+
+  def self.new_by_name(tree, name, *ids)
+    TreeTagID_TBL.mutex.synchronize{
+      unless (obj = TreeTagID_TBL[tree.path][name])
+        (obj = self.allocate).instance_eval{
+          initialize(tree, name, ids)
+          TreeTagID_TBL[@tpath] = {} unless TreeTagID_TBL[@tpath]
+          TreeTagID_TBL[@tpath][@id] = self
+        }
+      end
+      obj
+    }
+  end
+
+  def self.new(tree, *ids)
+    TreeTagID_TBL.mutex.synchronize{
+      (obj = self.allocate).instance_eval{
+        if tree.kind_of?(Array)
+          initialize(tree[0], tree[1], ids)
+        else
+          initialize(tree, nil, ids)
+        end
+        TreeTagID_TBL[@tpath] = {} unless TreeTagID_TBL[@tpath]
+        TreeTagID_TBL[@tpath][@id] = self
+      }
+      obj
+    }
+  end
+
+  def initialize(tree, name, ids)
+    @parent = @tree = tree
+    @tpath = @parent.path
+
+    if name
+      @path = @id = name
+    else
+      TreeTag_ID.mutex.synchronize{
+        @path = @id = TreeTag_ID.join(TkCore::INTERP._ip_id_).freeze
+        TreeTag_ID[1].succ!
+      }
+    end
+
+    unless ids.empty?
+      tk_call(@tpath, 'tag', 'add', @id, *(ids.collect{|id| tagid(id)}))
+    end
+  end
+
+  def tagid(tag)
+    if tag.kind_of?(Tk::BLT::Treeview::Node) \
+      || tag.kind_of?(Tk::BLT::Treeview::Tag)
+      tag.id
+    else
+      tag
+    end
+  end
+  private :tagid
+
+  def id
+    @id
+  end
+
+  def add(*ids)
+    tk_call(@tpath, 'tag', 'add', @id, *(ids{|id| tagid(id)}))
+    self
+  end
+
+  def remove(*ids)
+    tk_call(@tpath, 'tag', 'delete', @id, *(ids{|id| tagid(id)}))
+    self
+  end
+
+  def forget
+    tk_call(@tpath, 'tag', 'forget', @id)
+    self
+  end
+
+  def nodes
+    simplelist(tk_call(@tpath, 'tag', 'nodes', @id)).collect{|id|
+      Tk::BLT::Treeview::Node.id2obj(@tree, id)
+    }
+  end
+end
+
+class Tk::BLT::Hiertable
+  TkCommandNames = ['::blt::hiertable'.freeze].freeze
+  WidgetClassName = 'Hiertable'.freeze
+  WidgetClassNames[WidgetClassName] ||= self
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/unix_dnd.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/unix_dnd.rb
new file mode 100644
index 0000000..8996f7c
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/unix_dnd.rb
@@ -0,0 +1,141 @@
+#
+#  tkextlib/blt/unix_dnd.rb
+#
+#    *** This is alpha version, because there is no document on BLT. ***
+#
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  module DnD
+    extend TkCore
+
+    TkCommandNames = ['::blt::dnd'.freeze].freeze
+
+    ##############################
+
+    extend TkItemConfigMethod
+
+    class << self
+      def __item_cget_cmd(id)
+        ['::blt::dnd', *id]
+      end
+      private :__item_cget_cmd
+
+      def __item_config_cmd(id)
+        ['::blt::dnd', *id]
+      end
+      private :__item_config_cmd
+
+      private :itemcget_tkstring, :itemcget, :itemcget_strict
+      private :itemconfigure, :itemconfiginfo, :current_itemconfiginfo
+
+      def cget_tkstring(win, option)
+        itemcget_tkstring(['cget', win], option)
+      end
+      def cget(win, option)
+        itemcget(['cget', win], option)
+      end
+      def cget_strict(win, option)
+        itemcget_strict(['cget', win], option)
+      end
+      def configure(win, slot, value=None)
+        itemconfigure(['configure', win], slot, value)
+      end
+      def configinfo(win, slot=nil)
+        itemconfiginfo(['configure', win], slot)
+      end
+      def current_configinfo(win, slot=nil)
+        current_itemconfiginfo(['configure', win], slot)
+      end
+
+      def token_cget_tkstring(win, option)
+        itemcget_tkstring(['token', 'cget', win], option)
+      end
+      def token_cget(win, option)
+        itemcget(['token', 'cget', win], option)
+      end
+      def token_cget_strict(win, option)
+        itemcget_strict(['token', 'cget', win], option)
+      end
+      def token_configure(win, slot, value=None)
+        itemconfigure(['token', 'configure', win], slot, value)
+      end
+      def token_configinfo(win, slot=nil)
+        itemconfiginfo(['token', 'configure', win], slot)
+      end
+      def current_token_configinfo(win, slot=nil)
+        current_itemconfiginfo(['token', 'configure', win], slot)
+      end
+
+      def token_windowconfigure(win, slot, value=None)
+        itemconfigure(['token', 'window', win], slot, value)
+      end
+      def token_windowconfiginfo(win, slot=nil)
+        itemconfiginfo(['token', 'window', win], slot)
+      end
+      def current_token_windowconfiginfo(win, slot=nil)
+        current_itemconfiginfo(['token', 'window', win], slot)
+      end
+    end
+
+    ##############################
+
+    def self.cancel(win)
+      tk_call('::blt::dnd', 'cancel', *wins)
+    end
+    def self.delete(*wins)
+      tk_call('::blt::dnd', 'delete', *wins)
+    end
+    def self.delete_source(*wins)
+      tk_call('::blt::dnd', 'delete', '-source', *wins)
+    end
+    def self.delete_target(*wins)
+      tk_call('::blt::dnd', 'delete', '-target', *wins)
+    end
+    def self.drag(win, x, y, token=None)
+      tk_call('::blt::dnd', 'drag', win, x, y, token)
+    end
+    def self.drop(win, x, y, token=None)
+      tk_call('::blt::dnd', 'drop', win, x, y, token)
+    end
+    def self.get_data(win, fmt=nil, cmd=nil)
+      if fmt
+        tk_call('::blt::dnd', 'getdata', win, fmt, cmd)
+      else
+        list(tk_call('::blt::dnd', 'getdata', win))
+      end
+    end
+    def self.names(pat=None)
+      list(tk_call('::blt::dnd', 'names', pat))
+    end
+    def self.source_names(pat=None)
+      list(tk_call('::blt::dnd', 'names', '-source', pat))
+    end
+    def self.target_names(pat=None)
+      list(tk_call('::blt::dnd', 'names', '-target', pat))
+    end
+    def self.pull(win, fmt)
+      tk_call('::blt::dnd', 'pull', win, fmt)
+    end
+    def self.register(win, keys={})
+      tk_call('::blt::dnd', 'register', win, keys)
+    end
+    def self.select(win, x, y, timestamp)
+      tk_call('::blt::dnd', 'select', win, x, y, timestamp)
+    end
+    def self.set_data(win, fmt=nil, cmd=nil)
+      if fmt
+        tk_call('::blt::dnd', 'setdata', win, fmt, cmd)
+      else
+        list(tk_call('::blt::dnd', 'setdata', win))
+      end
+    end
+    def self.token(*args)
+      tk_call('::blt::dnd', 'token', *args)
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/vector.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/vector.rb
new file mode 100644
index 0000000..742e901
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/vector.rb
@@ -0,0 +1,256 @@
+#
+#  tkextlib/blt/vector.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  class Vector < TkVariable
+    TkCommandNames = ['::blt::vector'.freeze].freeze
+
+    def self.create(*args)
+      tk_call('::blt::vector', 'create', *args)
+    end
+
+    def self.destroy(*args)
+      tk_call('::blt::vector', 'destroy', *args)
+    end
+
+    def self.expr(expression)
+      tk_call('::blt::vector', 'expr', expression)
+    end
+
+    def self.names(pat=None)
+      list = simplelist(tk_call('::blt::vector', 'names', pat))
+      TkVar_ID_TBL.mutex.synchronize{
+        list.collect{|name|
+          if TkVar_ID_TBL[name]
+            TkVar_ID_TBL[name]
+          elsif name[0..1] == '::' && TkVar_ID_TBL[name[2..-1]]
+            TkVar_ID_TBL[name[2..-1]]
+          else
+            name
+          end
+        }
+      }
+    end
+
+    ####################################
+
+    def initialize(size=nil, keys={})
+      if size.kind_of?(Hash)
+        keys = size
+        size = nil
+      end
+      if size.kind_of?(Array)
+        # [first, last]
+        size = size.join(':')
+      end
+      if size
+        @id = TkCore::INTERP._invoke('::blt::vector', 'create',
+                                     "#auto(#{size})", *hash_kv(keys))
+      else
+        @id = TkCore::INTERP._invoke('::blt::vector', 'create',
+                                     "#auto", *hash_kv(keys))
+      end
+
+      TkVar_ID_TBL.mutex.synchronize{
+        TkVar_ID_TBL[@id] = self
+      }
+
+      @def_default = false
+      @default_val = nil
+
+      @trace_var  = nil
+      @trace_elem = nil
+      @trace_opts = nil
+
+      # teach Tk-ip that @id is global var
+      TkCore::INTERP._invoke_without_enc('global', @id)
+    end
+
+    def destroy
+      tk_call('::blt::vector', 'destroy', @id)
+    end
+
+    def inspect
+      '#<Tk::BLT::Vector: ' + @id + '>'
+    end
+
+    def to_s
+      @id
+    end
+
+    def *(item)
+      list(tk_call(@id, '*', item))
+    end
+
+    def +(item)
+      list(tk_call(@id, '+', item))
+    end
+
+    def -(item)
+      list(tk_call(@id, '-', item))
+    end
+
+    def /(item)
+      list(tk_call(@id, '/', item))
+    end
+
+    def append(*vectors)
+      tk_call(@id, 'append', *vectors)
+    end
+
+    def binread(channel, len=None, keys={})
+      if len.kind_of?(Hash)
+        keys = len
+        len = None
+      end
+      keys = _symbolkey2str(keys)
+      keys['swap'] = None if keys.delete('swap')
+      tk_call(@id, 'binread', channel, len, keys)
+    end
+
+    def clear()
+      tk_call(@id, 'clear')
+      self
+    end
+
+    def delete(*indices)
+      tk_call(@id, 'delete', *indices)
+      self
+    end
+
+    def dup_vector(vec)
+      tk_call(@id, 'dup', vec)
+      self
+    end
+
+    def expr(expression)
+      tk_call(@id, 'expr', expression)
+      self
+    end
+
+    def index(idx, val=None)
+      number(tk_call(@id, 'index', idx, val))
+    end
+
+    def [](idx)
+      index(idx)
+    end
+
+    def []=(idx, val)
+      index(idx, val)
+    end
+
+    def length()
+      number(tk_call(@id, 'length'))
+    end
+
+    def length=(size)
+      number(tk_call(@id, 'length', size))
+    end
+
+    def merge(*vectors)
+      tk_call(@id, 'merge', *vectors)
+      self
+    end
+
+    def normalize(vec=None)
+      tk_call(@id, 'normalize', vec)
+      self
+    end
+
+    def notify(keyword)
+      tk_call(@id, 'notify', keyword)
+      self
+    end
+
+    def offset()
+      number(tk_call(@id, 'offset'))
+    end
+
+    def offset=(val)
+      number(tk_call(@id, 'offset', val))
+    end
+
+    def random()
+      tk_call(@id, 'random')
+    end
+
+    def populate(vector, density=None)
+      tk_call(@id, 'populate', vector, density)
+      self
+    end
+
+    def range(first, last=None)
+      list(tk_call(@id, 'range', first, last))
+    end
+
+    def search(val1, val2=None)
+      list(tk_call(@id, 'search', val1, val2))
+    end
+
+    def set(item)
+      tk_call(@id, 'set', item)
+      self
+    end
+
+    def seq(start, finish=None, step=None)
+      tk_call(@id, 'seq', start, finish, step)
+      self
+    end
+
+    def sort(*vectors)
+      tk_call(@id, 'sort', *vectors)
+      self
+    end
+
+    def sort_reverse(*vectors)
+      tk_call(@id, 'sort', '-reverse', *vectors)
+      self
+    end
+
+    def split(*vectors)
+      tk_call(@id, 'split', *vectors)
+      self
+    end
+
+    def variable(var)
+      tk_call(@id, 'variable', var)
+      self
+    end
+  end
+
+  class VectorAccess < Vector
+    def self.new(name)
+      TkVar_ID_TBL.mutex.synchronize{
+        if TkVar_ID_TBL[name]
+          TkVar_ID_TBL[name]
+        else
+          (obj = self.allocate).instance_eval{
+            initialize(name)
+            TkVar_ID_TBL[@id] = self
+          }
+          obj
+        end
+      }
+    end
+
+    def initialize(vec_name)
+      @id = vec_name
+
+      @def_default = false
+      @default_val = nil
+
+      @trace_var  = nil
+      @trace_elem = nil
+      @trace_opts = nil
+
+      # teach Tk-ip that @id is global var
+      TkCore::INTERP._invoke_without_enc('global', @id)
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/watch.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/watch.rb
new file mode 100644
index 0000000..292623f
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/watch.rb
@@ -0,0 +1,175 @@
+#
+#  tkextlib/blt/watch.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  class Watch < TkObject
+    extend TkCore
+
+    TkCommandNames = ['::blt::watch'.freeze].freeze
+
+    WATCH_ID_TBL = TkCore::INTERP.create_table
+
+    (BLT_WATCH_ID = ['blt_watch_id'.freeze, TkUtil.untrust('00000')]).instance_eval{
+      @mutex = Mutex.new
+      def mutex; @mutex; end
+      freeze
+    }
+
+    TkCore::INTERP.init_ip_env{
+      WATCH_ID_TBL.mutex.synchronize{ WATCH_ID_TBL.clear }
+    }
+
+    def self.names(state = None)
+      lst = tk_split_list(tk_call('::blt::watch', 'names', state))
+      WATCH_ID_TBL.mutex.synchronize{
+        lst.collect{|name|
+          WATCH_ID_TBL[name] || name
+        }
+      }
+    end
+
+    def __numval_optkeys
+      ['maxlevel']
+    end
+    private :__numval_optkeys
+
+    def __boolval_optkeys
+      ['active']
+    end
+    private :__boolval_optkeys
+
+    def __config_cmd
+      ['::blt::watch', 'configure', self.path]
+    end
+    private :__config_cmd
+
+    def initialize(name = nil, keys = {})
+      if name.kind_of?(Hash)
+        keys = name
+        name = nil
+      end
+
+      if name
+        @id = name.to_s
+      else
+        BLT_WATCH_ID.mutex.synchronize{
+          @id = BLT_WATCH_ID.join(TkCore::INTERP._ip_id_)
+          BLT_WATCH_ID[1].succ!
+        }
+      end
+
+      @path = @id
+
+      WATCH_ID_TBL.mutex.synchronize{
+        WATCH_ID_TBL[@id] = self
+      }
+      tk_call('::blt::watch', 'create', @id, *hash_kv(keys))
+    end
+
+    def activate
+      tk_call('::blt::watch', 'activate', @id)
+      self
+    end
+    def deactivate
+      tk_call('::blt::watch', 'deactivate', @id)
+      self
+    end
+    def delete
+      tk_call('::blt::watch', 'delete', @id)
+      self
+    end
+    def info
+      ret = []
+      lst = tk_split_simplelist(tk_call('::blt::watch', 'info', @id))
+      until lst.empty?
+        k, v, *lst = lst
+        k = k[1..-1]
+        case k
+        when /^(#{__strval_optkeys.join('|')})$/
+          # do nothing
+
+        when /^(#{__numval_optkeys.join('|')})$/
+          begin
+            v = number(v)
+          rescue
+            v = nil
+          end
+
+        when /^(#{__numstrval_optkeys.join('|')})$/
+          v = num_or_str(v)
+
+        when /^(#{__boolval_optkeys.join('|')})$/
+          begin
+            v = bool(v)
+          rescue
+            v = nil
+          end
+
+        when /^(#{__listval_optkeys.join('|')})$/
+          v = simplelist(v)
+
+        when /^(#{__numlistval_optkeys.join('|')})$/
+          v = list(v)
+
+        else
+          if v.index('{')
+            v = tk_split_list(v)
+          else
+            v = tk_tcl2ruby(v)
+          end
+        end
+
+        ret << [k, v]
+      end
+
+      ret
+    end
+    def configinfo(slot = nil)
+      if slot
+        slot = slot.to_s
+        v = cget(slot)
+        if TkComm::GET_CONFIGINFO_AS_ARRAY
+          [slot, v]
+        else
+          {slot=>v}
+        end
+      else
+        if TkComm::GET_CONFIGINFO_AS_ARRAY
+          info
+        else
+          Hash[*(info.flatten)]
+        end
+      end
+    end
+    def cget_strict(key)
+      key = key.to_s
+      begin
+        info.assoc(key)[1]
+      rescue
+        fail ArgumentError, "unknown option '#{key}'"
+      end
+    end
+    def cget(key)
+      unless TkConfigMethod.__IGNORE_UNKNOWN_CONFIGURE_OPTION__
+        cget_strict(key)
+      else
+        begin
+          cget_strict(key)
+        rescue => e
+          if current_configinfo.has_key?(key.to_s)
+            # error on known option
+            fail e
+          else
+            # unknown option
+            nil
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/win_printer.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/win_printer.rb
new file mode 100644
index 0000000..7ac6a0d
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/win_printer.rb
@@ -0,0 +1,61 @@
+#
+#  tkextlib/blt/win_printer.rb
+#
+#      *** Windows only ***
+#
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  class Printer < TkObject
+    extend TkCore
+
+    TkCommandNames = ['::blt::printer'.freeze].freeze
+
+    def self.enum(attribute)
+      simplelist(tk_call('::blt::printer', 'enum', attribute))
+    end
+
+    def self.names(pat=None)
+      simplelist(tk_call('::blt::printer', 'names', pat))
+    end
+
+    def self.open(printer)
+      self.new(printer)
+    end
+
+    #################################
+
+    def initialize(printer)
+      @printer_id = tk_call('::blt::printer', 'open', printer)
+    end
+
+    def close
+      tk_call('::blt::print', 'close', @printer_id)
+      self
+    end
+    def get_attrs(var)
+      tk_call('::blt::print', 'getattrs', @printer_id, var)
+      var
+    end
+    def set_attrs(var)
+      tk_call('::blt::print', 'setattrs', @printer_id, var)
+      self
+    end
+    def snap(win)
+      tk_call('::blt::print', 'snap', @printer_id, win)
+      self
+    end
+    def write(str)
+      tk_call('::blt::print', 'write', @printer_id, str)
+      self
+    end
+    def write_with_title(title, str)
+      tk_call('::blt::print', 'write', @printer_id, title, str)
+      self
+    end
+  end
+end
diff --git a/jni/ruby/ext/tk/lib/tkextlib/blt/winop.rb b/jni/ruby/ext/tk/lib/tkextlib/blt/winop.rb
new file mode 100644
index 0000000..03bdb60
--- /dev/null
+++ b/jni/ruby/ext/tk/lib/tkextlib/blt/winop.rb
@@ -0,0 +1,107 @@
+#
+#  tkextlib/blt/winop.rb
+#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+
+require 'tk'
+require 'tkextlib/blt.rb'
+
+module Tk::BLT
+  module Winop
+    extend TkCore
+
+    TkCommandNames = ['::blt::winop'.freeze].freeze
+  end
+  WinOp = Winop
+end
+
+class << Tk::BLT::Winop
+  def changes(win)
+    tk_call('::blt::winop', 'changes', win)
+  end
+
+  def colormap(win)
+    Hash[*list(tk_call('::blt::winop', 'colormap', win))]
+  end
+
+  def convolve(src, dest, filter)
+    tk_call('::blt::winop', 'convolve', src, dest, filter)
+  end
+
+  def image_convolve(src, dest, filter)
+    tk_call('::blt::winop', 'image', 'convolve', src, dest, filter)
+  end
+  def image_gradient(photo, left, right, type)
+    tk_call('::blt::winop', 'image', 'gradient', photo, left, right, type)
+  end
+  def image_read_jpeg(file, photo)
+    tk_call('::blt::winop', 'image', 'readjpeg', file, photo)
+  end
+  def image_resample(src, dest, horiz_filter=None, vert_filter=None)
+    tk_call('::blt::winop', 'image', 'resample',
+            src, dest, horiz_filter, vert_filter)
+  end
+  def image_rotate(src, dest, angle)
+    tk_call('::blt::winop', 'image', 'rotate', src, dest, angle)
+  end
+  def image_snap(win, photo, width=None, height=None)
+    tk_call('::blt::winop', 'image', 'snap', win, photo, width, height)
+  end
+  def image_subsample(src, dest, x, y, width, height,
+                      horiz_filter=None, vert_filter=None)
+    tk_call('::blt::winop', 'image', 'subsample',
+            src, dest, x, y, width, height, horiz_filter, vert_filter)
+  end
+
+  def quantize(src, dest, colors)
+    tk_call('::blt::winop', 'quantize', src, dest, colors)
+  end
+
+  def query()
+    tk_call('::blt::winop', 'query')
+  end
+
+  def read_jpeg(file, photo)
+    tk_call('::blt::winop', 'readjpeg', file, photo)
+  end
+
+  def resample(src, dest, horiz_filter=None, vert_filter=None)
+    tk_call('::blt::winop', 'resample',
+            src, dest, horiz_filter, vert_filter)
+  end
+
+  def subsample(src, dest, x, y, width, height,
+                horiz_filter=None, vert_filter=None)
+    tk_call('::blt::winop', 'subsample',
+            src, dest, x, y, width, height, horiz_filter, vert_filter)
+  end
+
+  def raise(*wins)
+    tk_call('::blt::winop', 'raise', *wins)
+  end
+
+  def lower(*wins)
+    tk_call('::blt::winop', 'lower', *wins)
+  end
+
+  def map(*wins)
+    tk_call('::blt::winop', 'map', *wins)
+  end
+
+  def unmap(*wins)
+    tk_call('::blt::winop', 'unmap', *wins)
+  end
+
+  def move(win, x, y)
+    tk_call('::blt::winop', 'move', win, x, y)
+  end
+
+  def snap(win, photo)
+    tk_call('::blt::winop', 'snap', win, photo)
+  end
+
+  def warpto(win = None)
+    tk_call('::blt::winop', 'warpto', win)
+  end
+  alias warp_to warpto
+end
-- 
cgit v1.2.3-70-g09d2