ext/tk/lib/tktext.rb


DEFINITIONS

This source file includes following functions.


   1  #
   2  #               tktext.rb - Tk text classes
   3  #                       $Date: 2002/06/28 14:40:25 $
   4  #                       by Yukihiro Matsumoto <matz@caelum.co.jp>
   5  
   6  require 'tk.rb'
   7  require 'tkfont'
   8  
   9  module TkTreatTextTagFont
  10    include TkTreatItemFont
  11  
  12    ItemCMD = ['tag', 'configure']
  13    def __conf_cmd(idx)
  14      ItemCMD[idx]
  15    end
  16  
  17    def __item_pathname(tagOrId)
  18      if tagOrId.kind_of?(TkTextTag)
  19        self.path + ';' + tagOrId.id
  20      else
  21        self.path + ';' + tagOrId
  22      end
  23    end
  24  end
  25  
  26  class TkText<TkTextWin
  27    ItemConfCMD = ['tag', 'configure']
  28    include TkTreatTextTagFont
  29    include Scrollable
  30  
  31    WidgetClassName = 'Text'.freeze
  32    WidgetClassNames[WidgetClassName] = self
  33  
  34    def self.to_eval
  35      WidgetClassName
  36    end
  37  
  38    def self.new(*args, &block)
  39      obj = super(*args){}
  40      obj.init_instance_variable
  41      obj.instance_eval(&block) if defined? yield
  42      obj
  43    end
  44  
  45    def init_instance_variable
  46      @tags = {}
  47    end
  48  
  49    def create_self(keys)
  50      if keys and keys != None
  51        tk_call 'text', @path, *hash_kv(keys)
  52      else
  53        tk_call 'text', @path
  54      end
  55      init_instance_variable
  56    end
  57  
  58    def index(index)
  59      tk_send 'index', index
  60    end
  61  
  62    def value
  63      tk_send 'get', "1.0", "end - 1 char"
  64    end
  65  
  66    def value= (val)
  67      tk_send 'delete', "1.0", 'end'
  68      tk_send 'insert', "1.0", val
  69    end
  70  
  71    def _addcmd(cmd)
  72      @cmdtbl.push cmd
  73    end
  74  
  75    def _addtag(name, obj)
  76      @tags[name] = obj
  77    end
  78  
  79    def tagid2obj(tagid)
  80      if not @tags[tagid]
  81        tagid
  82      else
  83        @tags[tagid]
  84      end
  85    end
  86  
  87    def tag_names(index=None)
  88      tk_split_list(tk_send('tag', 'names', index)).collect{|elt|
  89        tagid2obj(elt)
  90      }
  91    end
  92  
  93    def mark_names
  94      tk_split_list(tk_send('mark', 'names')).collect{|elt|
  95        tagid2obj(elt)
  96      }
  97    end
  98  
  99    def mark_next(index)
 100      tagid2obj(tk_send('mark', 'next', index))
 101    end
 102  
 103    def mark_previous(index)
 104      tagid2obj(tk_send('mark', 'previous', index))
 105    end
 106  
 107    def window_names
 108      tk_send('window', 'names').collect{|elt|
 109        tagid2obj(elt)
 110      }
 111    end
 112  
 113    def image_names
 114      tk_send('image', 'names').collect{|elt|
 115        tagid2obj(elt)
 116      }
 117    end
 118  
 119    def set_insert(index)
 120      tk_send 'mark', 'set', 'insert', index
 121    end
 122  
 123    def set_current(index)
 124      tk_send 'mark', 'set', 'current', index
 125    end
 126  
 127    def insert(index, chars, *tags)
 128      super index, chars, tags.collect{|x|_get_eval_string(x)}.join(' ')
 129    end
 130  
 131    def destroy
 132      @tags = {} unless @tags
 133      @tags.each_value do |t|
 134        t.destroy
 135      end
 136      super
 137    end
 138  
 139    def backspace
 140      self.delete 'insert'
 141    end
 142  
 143    def compare(idx1, op, idx2)
 144      bool(tk_send('compare', idx1, op, idx2))
 145    end
 146  
 147    def debug
 148      bool(tk_send('debug'))
 149    end
 150    def debug=(boolean)
 151      tk_send 'debug', boolean
 152    end
 153  
 154    def bbox(index)
 155      inf = tk_send('bbox', index)
 156      (inf == "")?  [0,0,0,0]: inf
 157    end
 158    def dlineinfo(index)
 159      inf = tk_send('dlineinfo', index)
 160      (inf == "")?  [0,0,0,0,0]: inf
 161    end
 162  
 163    def yview_pickplace(*what)
 164      tk_send 'yview', '-pickplace', *what
 165    end
 166  
 167    def xview_pickplace(*what)
 168      tk_send 'xview', '-pickplace', *what
 169    end
 170  
 171    def tag_add(tag, index1, index2=None)
 172      tk_send 'tag', 'add', tag, index1, index2
 173    end
 174  
 175    def tag_bind(tag, seq, cmd=Proc.new, args=nil)
 176      _bind(['tag', 'bind', tag], seq, cmd, args)
 177    end
 178  
 179    def tag_bind_append(tag, seq, cmd=Proc.new, args=nil)
 180      _bind_append(['tag', 'bind', tag], seq, cmd, args)
 181    end
 182  
 183    def tag_bindinfo(tag, context=nil)
 184      _bindinfo(['tag', 'bind', tag], context)
 185    end
 186  
 187    def tag_cget(tag, key)
 188      case key.to_s
 189      when 'text', 'label', 'show', 'data', 'file'
 190        tk_call @path, 'tag', 'cget', tag, "-#{key}"
 191      else
 192        tk_tcl2ruby tk_call(@path, 'tag', 'cget', tag, "-#{key}")
 193      end
 194    end
 195  
 196    def tag_configure(tag, key, val=None)
 197      if key.kind_of? Hash
 198        key = _symbolkey2str(key)
 199        if ( key['font'] || key['kanjifont'] \
 200            || key['latinfont'] || key['asciifont'] )
 201          tagfont_configure(tag, key)
 202        else
 203          tk_send 'tag', 'configure', tag, *hash_kv(key)
 204        end
 205  
 206      else
 207        if  key == 'font' || key == :font || 
 208            key == 'kanjifont' || key == :kanjifont ||
 209            key == 'latinfont' || key == :latinfont || 
 210            key == 'asciifont' || key == :asciifont
 211          tagfont_configure(tag, {key=>val})
 212        else
 213          tk_send 'tag', 'configure', tag, "-#{key}", val
 214        end
 215      end
 216    end
 217  
 218    def tag_configinfo(tag, key=nil)
 219      if key
 220        case key.to_s
 221        when 'text', 'label', 'show', 'data', 'file'
 222          conf = tk_split_simplelist(tk_send('tag','configure',tag,"-#{key}"))
 223        else
 224          conf = tk_split_list(tk_send('tag','configure',tag,"-#{key}"))
 225        end
 226        conf[0] = conf[0][1..-1]
 227        conf
 228      else
 229        tk_split_simplelist(tk_send('tag', 'configure', tag)).collect{|conflist|
 230          conf = tk_split_simplelist(conflist)
 231          conf[0] = conf[0][1..-1]
 232          case conf[0]
 233          when 'text', 'label', 'show', 'data', 'file'
 234          else
 235            if conf[3]
 236              if conf[3].index('{')
 237                conf[3] = tk_split_list(conf[3]) 
 238              else
 239                conf[3] = tk_tcl2ruby(conf[3]) 
 240              end
 241            end
 242            if conf[4]
 243              if conf[4].index('{')
 244                conf[4] = tk_split_list(conf[4]) 
 245              else
 246                conf[4] = tk_tcl2ruby(conf[4]) 
 247              end
 248            end
 249          end
 250          conf
 251        }
 252      end
 253    end
 254  
 255    def tag_raise(tag, above=None)
 256      tk_send 'tag', 'raise', tag, above
 257    end
 258  
 259    def tag_lower(tag, below=None)
 260      tk_send 'tag', 'lower', tag, below
 261    end
 262  
 263    def tag_remove(tag, *index)
 264      tk_send 'tag', 'remove', tag, *index
 265    end
 266  
 267    def tag_ranges(tag)
 268      l = tk_split_simplelist(tk_send('tag', 'ranges', tag))
 269      r = []
 270      while key=l.shift
 271        r.push [key, l.shift]
 272      end
 273      r
 274    end
 275  
 276    def tag_nextrange(tag, first, last=None)
 277      tk_split_simplelist(tk_send('tag', 'nextrange', tag, first, last))
 278    end
 279  
 280    def tag_prevrange(tag, first, last=None)
 281      tk_split_simplelist(tk_send('tag', 'prevrange', tag, first, last))
 282    end
 283  
 284    def _ktext_length(txt)
 285      if $KCODE !~ /n/i
 286        return txt.gsub(/[^\Wa-zA-Z_\d]/, ' ').length
 287      end
 288  
 289      # $KCODE == 'NONE'
 290      if JAPANIZED_TK
 291        tk_call('kstring', 'length', txt).to_i
 292      else
 293        begin
 294          tk_call('encoding', 'convertto', 'ascii', txt).length
 295        rescue StandardError, NameError
 296          # sorry, I have no plan
 297          txt.length
 298        end
 299      end
 300    end
 301    private :_ktext_length
 302  
 303    def search_with_length(pat,start,stop=None)
 304      pat = pat.chr if pat.kind_of? Integer
 305      if stop != None
 306        return ["", 0] if compare(start,'>=',stop)
 307        txt = get(start,stop)
 308        if (pos = txt.index(pat))
 309          match = $&
 310          #pos = txt[0..(pos-1)].split('').length if pos > 0
 311          pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
 312          if pat.kind_of? String
 313            #return [index(start + " + #{pos} chars"), pat.split('').length]
 314            return [index(start + " + #{pos} chars"), 
 315                    _ktext_length(pat), pat.dup]
 316          else
 317            #return [index(start + " + #{pos} chars"), $&.split('').length]
 318            return [index(start + " + #{pos} chars"), 
 319                    _ktext_length(match), match]
 320          end
 321        else
 322          return ["", 0]
 323        end
 324      else
 325        txt = get(start,'end - 1 char')
 326        if (pos = txt.index(pat))
 327          match = $&
 328          #pos = txt[0..(pos-1)].split('').length if pos > 0
 329          pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
 330          if pat.kind_of? String
 331            #return [index(start + " + #{pos} chars"), pat.split('').length]
 332            return [index(start + " + #{pos} chars"), 
 333                    _ktext_length(pat), pat.dup]
 334          else
 335            #return [index(start + " + #{pos} chars"), $&.split('').length]
 336            return [index(start + " + #{pos} chars"), 
 337                    _ktext_length(match), match]
 338          end
 339        else
 340          txt = get('1.0','end - 1 char')
 341          if (pos = txt.index(pat))
 342            match = $&
 343            #pos = txt[0..(pos-1)].split('').length if pos > 0
 344            pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
 345            if pat.kind_of? String
 346              #return [index("1.0 + #{pos} chars"), pat.split('').length]
 347              return [index("1.0 + #{pos} chars"), 
 348                      _ktext_length(pat), pat.dup]
 349            else
 350              #return [index("1.0 + #{pos} chars"), $&.split('').length]
 351              return [index("1.0 + #{pos} chars"), _ktext_length(match), match]
 352            end
 353          else
 354            return ["", 0]
 355          end
 356        end
 357      end
 358    end
 359  
 360    def search(pat,start,stop=None)
 361      search_with_length(pat,start,stop)[0]
 362    end
 363  
 364    def rsearch_with_length(pat,start,stop=None)
 365      pat = pat.chr if pat.kind_of? Integer
 366      if stop != None
 367        return ["", 0] if compare(start,'<=',stop)
 368        txt = get(stop,start)
 369        if (pos = txt.rindex(pat))
 370          match = $&
 371          #pos = txt[0..(pos-1)].split('').length if pos > 0
 372          pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
 373          if pat.kind_of? String
 374            #return [index(stop + " + #{pos} chars"), pat.split('').length]
 375            return [index(stop + " + #{pos} chars"), _ktext_length(pat), pat.dup]
 376          else
 377            #return [index(stop + " + #{pos} chars"), $&.split('').length]
 378            return [index(stop + " + #{pos} chars"), _ktext_length(match), match]
 379          end
 380        else
 381          return ["", 0]
 382        end
 383      else
 384        txt = get('1.0',start)
 385        if (pos = txt.rindex(pat))
 386          match = $&
 387          #pos = txt[0..(pos-1)].split('').length if pos > 0
 388          pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
 389          if pat.kind_of? String
 390            #return [index("1.0 + #{pos} chars"), pat.split('').length]
 391            return [index("1.0 + #{pos} chars"), _ktext_length(pat), pat.dup]
 392          else
 393            #return [index("1.0 + #{pos} chars"), $&.split('').length]
 394            return [index("1.0 + #{pos} chars"), _ktext_length(match), match]
 395          end
 396        else
 397          txt = get('1.0','end - 1 char')
 398          if (pos = txt.rindex(pat))
 399            match = $&
 400            #pos = txt[0..(pos-1)].split('').length if pos > 0
 401            pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
 402            if pat.kind_of? String
 403              #return [index("1.0 + #{pos} chars"), pat.split('').length]
 404              return [index("1.0 + #{pos} chars"), _ktext_length(pat), pat.dup]
 405            else
 406              #return [index("1.0 + #{pos} chars"), $&.split('').length]
 407              return [index("1.0 + #{pos} chars"), _ktext_length(match), match]
 408            end
 409          else
 410            return ["", 0]
 411          end
 412        end
 413      end
 414    end
 415  
 416    def rsearch(pat,start,stop=None)
 417      rsearch_with_length(pat,start,stop)[0]
 418    end
 419  
 420    def dump(type_info, *index, &block)
 421      args = type_info.collect{|inf| '-' + inf}
 422      args << '-command' << Proc.new(&block) if iterator?
 423      str = tk_send('dump', *(args + index))
 424      result = []
 425      sel = nil
 426      i = 0
 427      while i < str.size
 428        # retrieve key
 429        idx = str.index(/ /, i)
 430        result.push str[i..(idx-1)]
 431        i = idx + 1
 432        
 433        # retrieve value
 434        case result[-1]
 435        when 'text'
 436          if str[i] == ?{
 437            # text formed as {...}
 438            val, i = _retrieve_braced_text(str, i)
 439            result.push val
 440          else
 441            # text which may contain backslahes
 442            val, i = _retrieve_backslashed_text(str, i)
 443            result.push val
 444          end
 445        else
 446          idx = str.index(/ /, i)
 447          val = str[i..(idx-1)]
 448          case result[-1]
 449          when 'mark'
 450            case val
 451            when 'insert'
 452              result.push TkTextMarkInsert.new(self)
 453            when 'current'
 454              result.push TkTextMarkCurrent.new(self)
 455            when 'anchor'
 456              result.push TkTextMarkAnchor.new(self)
 457            else
 458              result.push tk_tcl2ruby(val)
 459            end
 460          when 'tagon'
 461            if val == 'sel'
 462              if sel
 463                result.push sel
 464              else
 465                result.push TkTextTagSel.new(self)
 466              end
 467            else
 468              result.push tk_tcl2ruby(val)
 469            end
 470          when 'tagoff'
 471              result.push tk_tcl2ruby(sel)
 472          when 'window'
 473            result.push tk_tcl2ruby(val)
 474          end
 475          i = idx + 1
 476        end
 477  
 478        # retrieve index
 479        idx = str.index(/ /, i)
 480        if idx
 481          result.push str[i..(idx-1)]
 482          i = idx + 1
 483        else
 484          result.push str[i..-1]
 485          break
 486        end
 487      end
 488      
 489      kvis = []
 490      until result.empty?
 491        kvis.push [result.shift, result.shift, result.shift]
 492      end
 493      kvis  # result is [[key1, value1, index1], [key2, value2, index2], ...]
 494    end
 495  
 496    def _retrieve_braced_text(str, i)
 497      cnt = 0
 498      idx = i
 499      while idx < str.size
 500        case str[idx]
 501        when ?{
 502          cnt += 1
 503        when ?}
 504          cnt -= 1
 505          if cnt == 0
 506            break
 507          end
 508        end
 509        idx += 1
 510      end
 511      return str[i+1..idx-1], idx + 2
 512    end
 513    private :_retrieve_braced_text
 514  
 515    def _retrieve_backslashed_text(str, i)
 516      j = i
 517      idx = nil
 518      loop {
 519        idx = str.index(/ /, j)
 520        if str[idx-1] == ?\\
 521          j += 1
 522        else
 523          break
 524        end
 525      }
 526      val = str[i..(idx-1)]
 527      val.gsub!(/\\( |\{|\})/, '\1')
 528      return val, idx + 1
 529    end
 530    private :_retrieve_backslashed_text
 531  
 532    def dump_all(*index, &block)
 533      dump(['all'], *index, &block)
 534    end
 535    def dump_mark(*index, &block)
 536      dump(['mark'], *index, &block)
 537    end
 538    def dump_tag(*index, &block)
 539      dump(['tag'], *index, &block)
 540    end
 541    def dump_text(*index, &block)
 542      dump(['text'], *index, &block)
 543    end
 544    def dump_window(*index, &block)
 545      dump(['window'], *index, &block)
 546    end
 547    def dump_image(*index, &block)
 548      dump(['image'], *index, &block)
 549    end
 550  end
 551  
 552  class TkTextTag<TkObject
 553    include TkTreatTagFont
 554  
 555    TTagID_TBL = {}
 556    Tk_TextTag_ID = ['tag0000']
 557  
 558    def TkTextTag.id2obj(text, id)
 559      tpath = text.path
 560      return id unless TTagID_TBL[tpath]
 561      TTagID_TBL[tpath][id]? TTagID_TBL[tpath][id]: id
 562    end
 563  
 564    def initialize(parent, *args)
 565      if not parent.kind_of?(TkText)
 566        fail format("%s need to be TkText", parent.inspect)
 567      end
 568      @parent = @t = parent
 569      @tpath = parent.path
 570      @path = @id = Tk_TextTag_ID[0]
 571      TTagID_TBL[@id] = self
 572      TTagID_TBL[@tpath] = {} unless TTagID_TBL[@tpath]
 573      TTagID_TBL[@tpath][@id] = self
 574      Tk_TextTag_ID[0] = Tk_TextTag_ID[0].succ
 575      #tk_call @t.path, "tag", "configure", @id, *hash_kv(keys)
 576      if args != [] then
 577        keys = args.pop
 578        if keys.kind_of? Hash then
 579          add(*args) if args != []
 580          configure(keys)
 581        else
 582          args.push keys
 583          add(*args)
 584        end
 585      end
 586      @t._addtag id, self
 587    end
 588  
 589    def id
 590      return @id
 591    end
 592  
 593    def first
 594      @id + '.first'
 595    end
 596  
 597    def last
 598      @id + '.last'
 599    end
 600  
 601    def add(*index)
 602      tk_call @t.path, 'tag', 'add', @id, *index
 603    end
 604  
 605    def remove(*index)
 606      tk_call @t.path, 'tag', 'remove', @id, *index
 607    end
 608  
 609    def ranges
 610      l = tk_split_simplelist(tk_call(@t.path, 'tag', 'ranges', @id))
 611      r = []
 612      while key=l.shift
 613        r.push [key, l.shift]
 614      end
 615      r
 616    end
 617  
 618    def nextrange(first, last=None)
 619      tk_split_simplelist(tk_call(@t.path, 'tag', 'nextrange', @id, first, last))
 620    end
 621  
 622    def prevrange(first, last=None)
 623      tk_split_simplelist(tk_call(@t.path, 'tag', 'prevrange', @id, first, last))
 624    end
 625  
 626    def [](key)
 627      cget key
 628    end
 629  
 630    def []=(key,val)
 631      configure key, val
 632    end
 633  
 634    def cget(key)
 635      case key.to_s
 636      when 'text', 'label', 'show', 'data', 'file'
 637        tk_call @t.path, 'tag', 'cget', @id, "-#{key}"
 638      else
 639        tk_tcl2ruby tk_call(@t.path, 'tag', 'cget', @id, "-#{key}")
 640      end
 641    end
 642  
 643    def configure(key, val=None)
 644      @t.tag_configure @id, key, val
 645    end
 646  #  def configure(key, val=None)
 647  #    if key.kind_of? Hash
 648  #      tk_call @t.path, 'tag', 'configure', @id, *hash_kv(key)
 649  #    else
 650  #      tk_call @t.path, 'tag', 'configure', @id, "-#{key}", val
 651  #    end
 652  #  end
 653  #  def configure(key, value)
 654  #    if value == FALSE
 655  #      value = "0"
 656  #    elsif value.kind_of? Proc
 657  #      value = install_cmd(value)
 658  #    end
 659  #    tk_call @t.path, 'tag', 'configure', @id, "-#{key}", value
 660  #  end
 661  
 662    def configinfo(key=nil)
 663      @t.tag_configinfo @id, key
 664    end
 665  
 666    def bind(seq, cmd=Proc.new, args=nil)
 667      _bind([@t.path, 'tag', 'bind', @id], seq, cmd, args)
 668    end
 669  
 670    def bind_append(seq, cmd=Proc.new, args=nil)
 671      _bind_append([@t.path, 'tag', 'bind', @id], seq, cmd, args)
 672    end
 673  
 674    def bindinfo(context=nil)
 675      _bindinfo([@t.path, 'tag', 'bind', @id], context)
 676    end
 677  
 678    def raise(above=None)
 679      tk_call @t.path, 'tag', 'raise', @id, above
 680    end
 681  
 682    def lower(below=None)
 683      tk_call @t.path, 'tag', 'lower', @id, below
 684    end
 685  
 686    def destroy
 687      tk_call @t.path, 'tag', 'delete', @id
 688      TTagID_TBL[@tpath][@id] = nil if CTagID_TBL[@tpath]
 689    end
 690  end
 691  
 692  class TkTextNamedTag<TkTextTag
 693    def self.new(parent, name, *args)
 694      if TTagID_TBL[parent.path] && TTagID_TBL[parent.path][name]
 695        return TTagID_TBL[parent.path][name]
 696      else
 697        super(parent, name, *args)
 698      end
 699    end
 700  
 701    def initialize(parent, name, *args)
 702      if not parent.kind_of?(TkText)
 703        fail format("%s need to be TkText", parent.inspect)
 704      end
 705      @t = parent
 706      @tpath = parent.path
 707      @path = @id = name
 708      TTagID_TBL[@tpath] = {} unless TTagID_TBL[@tpath]
 709      TTagID_TBL[@tpath][@id] = self
 710      if mode
 711        tk_call @t.path, "addtag", @id, *args
 712      end
 713    end
 714  end
 715  
 716  class TkTextTagSel<TkTextTag
 717    def initialize(parent, keys=nil)
 718      if not parent.kind_of?(TkText)
 719        fail format("%s need to be TkText", parent.inspect)
 720      end
 721      @t = parent
 722      @path = @id = 'sel'
 723      #tk_call @t.path, "tag", "configure", @id, *hash_kv(keys)
 724      configure(keys) if keys
 725      @t._addtag id, self
 726    end
 727  end
 728  
 729  class TkTextMark<TkObject
 730    Tk_TextMark_ID = ['mark0000']
 731    def initialize(parent, index)
 732      if not parent.kind_of?(TkText)
 733        fail format("%s need to be TkText", parent.inspect)
 734      end
 735      @t = parent
 736      @path = @id = Tk_TextMark_ID[0]
 737      Tk_TextMark_ID[0] = Tk_TextMark_ID[0].succ
 738      tk_call @t.path, 'mark', 'set', @id, index
 739      @t._addtag id, self
 740    end
 741    def id
 742      return @id
 743    end
 744  
 745    def set(where)
 746      tk_call @t.path, 'mark', 'set', @id, where
 747    end
 748  
 749    def unset
 750      tk_call @t.path, 'mark', 'unset', @id
 751    end
 752    alias destroy unset
 753  
 754    def gravity
 755      tk_call @t.path, 'mark', 'gravity', @id
 756    end
 757  
 758    def gravity=(direction)
 759      tk_call @t.path, 'mark', 'gravity', @id, direction
 760    end
 761  
 762    def next(index = nil)
 763      if index
 764        @t.tagid2obj(tk_call(@t.path, 'mark', 'next', index))
 765      else
 766        @t.tagid2obj(tk_call(@t.path, 'mark', 'next', @id))
 767      end
 768    end
 769  
 770    def previous(index = nil)
 771      if index
 772        @t.tagid2obj(tk_call(@t.path, 'mark', 'previous', index))
 773      else
 774        @t.tagid2obj(tk_call(@t.path, 'mark', 'previous', @id))
 775      end
 776    end
 777  end
 778  
 779  class TkTextMarkInsert<TkTextMark
 780    def initialize(parent, index=nil)
 781      if not parent.kind_of?(TkText)
 782        fail format("%s need to be TkText", parent.inspect)
 783      end
 784      @t = parent
 785      @path = @id = 'insert'
 786      tk_call @t.path, 'mark', 'set', @id, index if index
 787      @t._addtag id, self
 788    end
 789  end
 790  
 791  class TkTextMarkCurrent<TkTextMark
 792    def initialize(parent,index=nil)
 793      if not parent.kind_of?(TkText)
 794        fail format("%s need to be TkText", parent.inspect)
 795      end
 796      @t = parent
 797      @path = @id = 'current'
 798      tk_call @t.path, 'mark', 'set', @id, index if index
 799      @t._addtag id, self
 800    end
 801  end
 802  
 803  class TkTextMarkAnchor<TkTextMark
 804    def initialize(parent,index=nil)
 805      if not parent.kind_of?(TkText)
 806        fail format("%s need to be TkText", parent.inspect)
 807      end
 808      @t = parent
 809      @path = @id = 'anchor'
 810      tk_call @t.path, 'mark', 'set', @id, index if index
 811      @t._addtag id, self
 812    end
 813  end
 814  
 815  class TkTextWindow<TkObject
 816    def initialize(parent, index, keys)
 817      if not parent.kind_of?(TkText)
 818        fail format("%s need to be TkText", parent.inspect)
 819      end
 820      @t = parent
 821      if index == 'end'
 822        @path = TkTextMark.new(@t, tk_call(@t.path, 'index', 'end - 1 chars'))
 823      elsif index.kind_of? TkTextMark
 824        if tk_call(@t.path,'index',index.path) == tk_call(@t.path,'index','end')
 825          @path = TkTextMark.new(@t, tk_call(@t.path, 'index', 'end - 1 chars'))
 826        else
 827          @path = TkTextMark.new(@t, tk_call(@t.path, 'index', index.path))
 828        end
 829      else
 830        @path = TkTextMark.new(@t, tk_call(@t.path, 'index', index))
 831      end
 832      @path.gravity = 'left'
 833      @index = @path.path
 834      keys = _symbolkey2str(keys)
 835      @id = keys['window']
 836      if keys['create']
 837        @p_create = keys['create']
 838        if @p_create.kind_of? Proc
 839          keys['create'] = install_cmd(proc{@id = @p_create.call; @id.path})
 840        end
 841      end
 842      tk_call @t.path, 'window', 'create', @index, *hash_kv(keys)
 843    end
 844  
 845    def [](slot)
 846      cget(slot)
 847    end
 848    def []=(slot, value)
 849      configure(slot, value)
 850    end
 851  
 852    def cget(slot)
 853      case slot.to_s
 854      when 'text', 'label', 'show', 'data', 'file'
 855        tk_call @t.path, 'window', 'cget', @index, "-#{slot}"
 856      else
 857        tk_tcl2ruby tk_call(@t.path, 'window', 'cget', @index, "-#{slot}")
 858      end
 859    end
 860  
 861    def configure(slot, value=None)
 862      if slot.kind_of? Hash
 863        slot = _symbolkey2str(slot)
 864        @id = slot['window'] if slot['window']
 865        if slot['create']
 866          self.create=value
 867          slot['create']=nil
 868        end
 869        if slot.size > 0
 870          tk_call @t.path, 'window', 'configure', @index, *hash_kv(slot)
 871        end
 872      else
 873        @id = value if slot == 'window' || slot == :window
 874        if slot == 'create' || slot == :create
 875          self.create=value
 876        else
 877          tk_call @t.path, 'window', 'configure', @index, "-#{slot}", value
 878        end
 879      end
 880    end
 881  
 882    def window
 883      @id
 884    end
 885  
 886    def window=(value)
 887      tk_call @t.path, 'window', 'configure', @index, '-window', value
 888      @id = value
 889    end
 890  
 891    def create
 892      @p_create
 893    end
 894  
 895    def create=(value)
 896      @p_create = value
 897      if @p_create.kind_of? Proc
 898        value = install_cmd(proc{@id = @p_create.call})
 899      end
 900      tk_call @t.path, 'window', 'configure', @index, '-create', value
 901    end
 902  
 903    def configinfo(slot = nil)
 904      if slot
 905        case slot.to_s
 906        when 'text', 'label', 'show', 'data', 'file'
 907          conf = tk_split_simplelist(tk_call(@t.path, 'window', 'configure', 
 908                                             @index, "-#{slot}"))
 909        else
 910          conf = tk_split_list(tk_call(@t.path, 'window', 'configure', 
 911                                       @index, "-#{slot}"))
 912        end
 913        conf[0] = conf[0][1..-1]
 914        conf
 915      else
 916        tk_split_simplelist(tk_call(@t.path, 'window', 'configure', 
 917                                    @index)).collect{|conflist|
 918          conf = tk_split_simplelist(conflist)
 919          conf[0] = conf[0][1..-1]
 920          case conf[0]
 921          when 'text', 'label', 'show', 'data', 'file'
 922          else
 923            if conf[3]
 924              if conf[3].index('{')
 925                conf[3] = tk_split_list(conf[3]) 
 926              else
 927                conf[3] = tk_tcl2ruby(conf[3]) 
 928              end
 929            end
 930            if conf[4]
 931              if conf[4].index('{')
 932                conf[4] = tk_split_list(conf[4]) 
 933              else
 934                conf[4] = tk_tcl2ruby(conf[4]) 
 935              end
 936            end
 937          end
 938          conf
 939        }
 940      end
 941    end
 942  
 943  end
 944  
 945  class TkTextImage<TkObject
 946    def initialize(parent, index, keys)
 947      if not parent.kind_of?(TkText)
 948        fail format("%s need to be TkText", parent.inspect)
 949      end
 950      @t = parent
 951      if index == 'end'
 952        @path = TkTextMark.new(@t, tk_call(@t.path, 'index', 'end - 1 chars'))
 953      elsif index.kind_of? TkTextMark
 954        if tk_call(@t.path,'index',index.path) == tk_call(@t.path,'index','end')
 955          @path = TkTextMark.new(@t, tk_call(@t.path, 'index', 'end - 1 chars'))
 956        else
 957          @path = TkTextMark.new(@t, tk_call(@t.path, 'index', index.path))
 958        end
 959      else
 960        @path = TkTextMark.new(@t, tk_call(@t.path, 'index', index))
 961      end
 962      @path.gravity = 'left'
 963      @index = @path.path
 964      @id = tk_call(@t.path, 'image', 'create', @index, *hash_kv(keys))
 965    end
 966  
 967    def [](slot)
 968      cget(slot)
 969    end
 970    def []=(slot, value)
 971      configure(slot, value)
 972    end
 973  
 974    def cget(slot)
 975      case slot.to_s
 976      when 'text', 'label', 'show', 'data', 'file'
 977        tk_call @t.path, 'image', 'cget', @index, "-#{slot}"
 978      else
 979        tk_tcl2ruby tk_call(@t.path, 'image', 'cget', @index, "-#{slot}")
 980      end
 981    end
 982  
 983    def configure(slot, value=None)
 984      if slot.kind_of? Hash
 985        tk_call @t.path, 'image', 'configure', @index, *hash_kv(slot)
 986      else
 987        tk_call @t.path, 'image', 'configure', @index, "-#{slot}", value
 988      end
 989    end
 990  #  def configure(slot, value)
 991  #    tk_call @t.path, 'image', 'configure', @index, "-#{slot}", value
 992  #  end
 993  
 994    def image
 995      tk_call @t.path, 'image', 'configure', @index, '-image'
 996    end
 997  
 998    def image=(value)
 999      tk_call @t.path, 'image', 'configure', @index, '-image', value
1000    end
1001  
1002    def configinfo(slot = nil)
1003      if slot
1004        case slot.to_s
1005        when 'text', 'label', 'show', 'data', 'file'
1006          conf = tk_split_simplelist(tk_call(@t.path, 'image', 'configure', 
1007                                             @index, "-#{slot}"))
1008        else
1009          conf = tk_split_list(tk_call(@t.path, 'image', 'configure', 
1010                                       @index, "-#{slot}"))
1011        end
1012        conf[0] = conf[0][1..-1]
1013        conf
1014      else
1015        tk_split_simplelist(tk_call(@t.path, 'image', 'configure', 
1016                                    @index)).collect{|conflist|
1017          conf = tk_split_simplelist(conflist)
1018          conf[0] = conf[0][1..-1]
1019          case conf[0]
1020          when 'text', 'label', 'show', 'data', 'file'
1021          else
1022            if conf[3]
1023              if conf[3].index('{')
1024                conf[3] = tk_split_list(conf[3]) 
1025              else
1026                conf[3] = tk_tcl2ruby(conf[3]) 
1027              end
1028            end
1029            if conf[4]
1030              if conf[4].index('{')
1031                conf[4] = tk_split_list(conf[4]) 
1032              else
1033                conf[4] = tk_tcl2ruby(conf[4]) 
1034              end
1035            end
1036          end
1037          conf
1038        }
1039      end
1040    end
1041  end