2 # tktext.rb - Tk text classes
3 # $Date: 2002/02/01 06:35:37 $
4 # by Yukihiro Matsumoto <matz@caelum.co.jp>
9 module TkTreatTextTagFont
10 def tagfont_configinfo(tag)
11 if tag.kind_of? TkTextTag
12 pathname = self.path + ';' + tag.id
14 pathname = self.path + ';' + tag
16 ret = TkFont.used_on(pathname)
18 ret = TkFont.init_widget_font(pathname,
19 self.path, 'tag', 'configure', tag)
23 alias tagfontobj tagfont_configinfo
25 def tagfont_configure(tag, slot)
26 if tag.kind_of? TkTextTag
27 pathname = self.path + ';' + tag.id
29 pathname = self.path + ';' + tag
31 if (fnt = slot.delete('font'))
32 if fnt.kind_of? TkFont
33 return fnt.call_font_configure(pathname,
34 self.path,'tag','configure',tag,slot)
36 latintagfont_configure(tag, fnt) if fnt
39 if (ltn = slot.delete('latinfont'))
40 latintagfont_configure(tag, ltn) if ltn
42 if (ltn = slot.delete('asciifont'))
43 latintagfont_configure(tag, ltn) if ltn
45 if (knj = slot.delete('kanjifont'))
46 kanjitagfont_configure(tag, knj) if knj
49 tk_call(self.path, 'tag', 'configure', tag, *hash_kv(slot)) if slot != {}
53 def latintagfont_configure(tag, ltn, keys=nil)
54 fobj = tagfontobj(tag)
55 if ltn.kind_of? TkFont
57 ltn.latin_configinfo.each{|key,val| conf[key] = val if val != []}
59 fobj.latin_replace(ltn)
60 fobj.latin_configure(keys) if keys
62 fobj.latin_configure(conf.update(keys))
64 fobj.latin_configure(conf)
67 fobj.latin_replace(ltn)
70 alias asciitagfont_configure latintagfont_configure
72 def kanjitagfont_configure(tag, knj, keys=nil)
73 fobj = tagfontobj(tag)
74 if knj.kind_of? TkFont
76 knj.kanji_configinfo.each{|key,val| conf[key] = val if val != []}
78 fobj.kanji_replace(knj)
79 fobj.kanji_configure(keys) if keys
81 fobj.kanji_configure(conf.update(keys))
83 fobj.kanji_configure(conf)
86 fobj.kanji_replace(knj)
90 def tagfont_copy(tag, window, wintag=nil)
92 window.tagfontobj(wintag).configinfo.each{|key,value|
93 tagfontobj(tag).configure(key,value)
95 tagfontobj(tag).replace(window.tagfontobj(wintag).latin_font,
96 window.tagfontobj(wintag).kanji_font)
98 window.tagfont(wintag).configinfo.each{|key,value|
99 tagfontobj(tag).configure(key,value)
101 tagfontobj(tag).replace(window.fontobj.latin_font,
102 window.fontobj.kanji_font)
106 def latintagfont_copy(tag, window, wintag=nil)
108 tagfontobj(tag).latin_replace(window.tagfontobj(wintag).latin_font)
110 tagfontobj(tag).latin_replace(window.fontobj.latin_font)
113 alias asciitagfont_copy latintagfont_copy
115 def kanjitagfont_copy(tag, window, wintag=nil)
117 tagfontobj(tag).kanji_replace(window.tagfontobj(wintag).kanji_font)
119 tagfontobj(tag).kanji_replace(window.fontobj.kanji_font)
124 class TkText<TkTextWin
125 include TkTreatTextTagFont
128 WidgetClassName = 'Text'.freeze
129 WidgetClassNames[WidgetClassName] = self
135 def self.new(*args, &block)
137 obj.init_instance_variable
138 obj.instance_eval &block if defined? yield
142 def init_instance_variable
147 tk_call 'text', @path
148 init_instance_variable
152 tk_send 'index', index
156 tk_send 'get', "1.0", "end - 1 char"
160 tk_send 'delete', "1.0", 'end'
161 tk_send 'insert', "1.0", val
168 def _addtag(name, obj)
180 def tag_names(index=None)
181 tk_split_list(tk_send('tag', 'names', index)).collect{|elt|
187 tk_split_list(tk_send('mark', 'names')).collect{|elt|
193 tagid2obj(tk_send('mark', 'next', index))
196 def mark_previous(index)
197 tagid2obj(tk_send('mark', 'previous', index))
201 tk_send('window', 'names').collect{|elt|
207 tk_send('image', 'names').collect{|elt|
212 def set_insert(index)
213 tk_send 'mark', 'set', 'insert', index
216 def set_current(index)
217 tk_send 'mark', 'set', 'current', index
220 def insert(index, chars, *tags)
221 super index, chars, tags.collect{|x|_get_eval_string(x)}.join(' ')
225 @tags = {} unless @tags
226 @tags.each_value do |t|
236 def compare(idx1, op, idx2)
237 bool(tk_send('compare', idx1, op, idx2))
241 bool(tk_send('debug'))
244 tk_send 'debug', boolean
248 inf = tk_send('bbox', index)
249 (inf == "")? [0,0,0,0]: inf
252 inf = tk_send('dlineinfo', index)
253 (inf == "")? [0,0,0,0,0]: inf
256 def yview_pickplace(*what)
257 tk_send 'yview', '-pickplace', *what
260 def xview_pickplace(*what)
261 tk_send 'xview', '-pickplace', *what
264 def tag_add(tag, index1, index2=None)
265 tk_send 'tag', 'add', tag, index1, index2
268 def tag_bind(tag, seq, cmd=Proc.new, args=nil)
269 _bind(['tag', 'bind', tag], seq, cmd, args)
272 def tag_bind_append(tag, seq, cmd=Proc.new, args=nil)
273 _bind_append(['tag', 'bind', tag], seq, cmd, args)
276 def tag_bindinfo(tag, context=nil)
277 _bindinfo(['tag', 'bind', tag], context)
280 def tag_cget(tag, key)
282 when 'text', 'label', 'show', 'data', 'flie'
283 tk_call @path, 'tag', 'cget', tag, "-#{key}"
285 tk_tcl2ruby tk_call @path, 'tag', 'cget', tag, "-#{key}"
289 def tag_configure(tag, key, val=None)
291 if ( key['font'] || key['kanjifont'] \
292 || key['latinfont'] || key['asciifont'] )
293 tagfont_configure(tag, key.dup)
295 tk_send 'tag', 'configure', tag, *hash_kv(key)
299 if key == 'font' || key == 'kanjifont' ||
300 key == 'latinfont' || key == 'asciifont'
301 tagfont_configure({key=>val})
303 tk_send 'tag', 'configure', tag, "-#{key}", val
308 def tag_configinfo(tag, key=nil)
311 when 'text', 'label', 'show', 'data', 'flie'
312 conf = tk_split_simplelist(tk_send('tag','configure',tag,"-#{key}"))
314 conf = tk_split_list(tk_send('tag','configure',tag,"-#{key}"))
316 conf[0] = conf[0][1..-1]
319 tk_split_simplelist(tk_send('tag', 'configure', tag)).collect{|conflist|
320 conf = tk_split_simplelist(conflist)
321 conf[0] = conf[0][1..-1]
323 when 'text', 'label', 'show', 'data', 'flie'
326 if conf[3].index('{')
327 conf[3] = tk_split_list(conf[3])
329 conf[3] = tk_tcl2ruby(conf[3])
333 if conf[4].index('{')
334 conf[4] = tk_split_list(conf[4])
336 conf[4] = tk_tcl2ruby(conf[4])
345 def tag_raise(tag, above=None)
346 tk_send 'tag', 'raise', tag, above
349 def tag_lower(tag, below=None)
350 tk_send 'tag', 'lower', tag, below
353 def tag_remove(tag, *index)
354 tk_send 'tag', 'remove', tag, *index
358 l = tk_split_simplelist(tk_send('tag', 'ranges', tag))
361 r.push [key, l.shift]
366 def tag_nextrange(tag, first, last=None)
367 tk_split_simplelist(tk_send('tag', 'nextrange', tag, first, last))
370 def tag_prevrange(tag, first, last=None)
371 tk_split_simplelist(tk_send('tag', 'prevrange', tag, first, last))
374 def _ktext_length(txt)
376 return txt.gsub(/[^\Wa-zA-Z_\d]/, ' ').length
381 tk_call('kstring', 'length', txt).to_i
384 tk_call('encoding', 'convertto', 'ascii', txt).length
385 rescue StandardError, NameError
386 # sorry, I have no plan
391 private :_ktext_length
393 def search_with_length(pat,start,stop=None)
394 pat = pat.chr if pat.kind_of? Integer
396 return ["", 0] if compare(start,'>=',stop)
397 txt = get(start,stop)
398 if (pos = txt.index(pat))
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(start + " + #{pos} chars"), pat.split('').length]
404 return [index(start + " + #{pos} chars"),
405 _ktext_length(pat), pat.dup]
407 #return [index(start + " + #{pos} chars"), $&.split('').length]
408 return [index(start + " + #{pos} chars"),
409 _ktext_length(match), match]
415 txt = get(start,'end - 1 char')
416 if (pos = txt.index(pat))
418 #pos = txt[0..(pos-1)].split('').length if pos > 0
419 pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
420 if pat.kind_of? String
421 #return [index(start + " + #{pos} chars"), pat.split('').length]
422 return [index(start + " + #{pos} chars"),
423 _ktext_length(pat), pat.dup]
425 #return [index(start + " + #{pos} chars"), $&.split('').length]
426 return [index(start + " + #{pos} chars"),
427 _ktext_length(match), match]
430 txt = get('1.0','end - 1 char')
431 if (pos = txt.index(pat))
433 #pos = txt[0..(pos-1)].split('').length if pos > 0
434 pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
435 if pat.kind_of? String
436 #return [index("1.0 + #{pos} chars"), pat.split('').length]
437 return [index("1.0 + #{pos} chars"),
438 _ktext_length(pat), pat.dup]
440 #return [index("1.0 + #{pos} chars"), $&.split('').length]
441 return [index("1.0 + #{pos} chars"), _ktext_length(match), match]
450 def search(pat,start,stop=None)
451 search_with_length(pat,start,stop)[0]
454 def rsearch_with_length(pat,start,stop=None)
455 pat = pat.chr if pat.kind_of? Integer
457 return ["", 0] if compare(start,'<=',stop)
458 txt = get(stop,start)
459 if (pos = txt.rindex(pat))
461 #pos = txt[0..(pos-1)].split('').length if pos > 0
462 pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
463 if pat.kind_of? String
464 #return [index(stop + " + #{pos} chars"), pat.split('').length]
465 return [index(stop + " + #{pos} chars"), _ktext_length(pat), pat.dup]
467 #return [index(stop + " + #{pos} chars"), $&.split('').length]
468 return [index(stop + " + #{pos} chars"), _ktext_length(match), match]
474 txt = get('1.0',start)
475 if (pos = txt.rindex(pat))
477 #pos = txt[0..(pos-1)].split('').length if pos > 0
478 pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
479 if pat.kind_of? String
480 #return [index("1.0 + #{pos} chars"), pat.split('').length]
481 return [index("1.0 + #{pos} chars"), _ktext_length(pat), pat.dup]
483 #return [index("1.0 + #{pos} chars"), $&.split('').length]
484 return [index("1.0 + #{pos} chars"), _ktext_length(match), match]
487 txt = get('1.0','end - 1 char')
488 if (pos = txt.rindex(pat))
490 #pos = txt[0..(pos-1)].split('').length if pos > 0
491 pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
492 if pat.kind_of? String
493 #return [index("1.0 + #{pos} chars"), pat.split('').length]
494 return [index("1.0 + #{pos} chars"), _ktext_length(pat), pat.dup]
496 #return [index("1.0 + #{pos} chars"), $&.split('').length]
497 return [index("1.0 + #{pos} chars"), _ktext_length(match), match]
506 def rsearch(pat,start,stop=None)
507 rsearch_with_length(pat,start,stop)[0]
510 def dump(type_info, *index, &block)
511 args = type_info.collect{|inf| '-' + inf}
512 args << '-command' << Proc.new(&block) if iterator?
513 str = tk_send('dump', *(args + index))
519 idx = str.index(/ /, i)
520 result.push str[i..(idx-1)]
527 # text formed as {...}
528 val, i = _retrieve_braced_text(str, i)
531 # text which may contain backslahes
532 val, i = _retrieve_backslashed_text(str, i)
536 idx = str.index(/ /, i)
537 val = str[i..(idx-1)]
542 result.push TkTextMarkInsert.new(self)
544 result.push TkTextMarkCurrent.new(self)
546 result.push TkTextMarkAnchor.new(self)
548 result.push tk_tcl2ruby(val)
555 result.push TkTextTagSel.new(self)
558 result.push tk_tcl2ruby(val)
561 result.push tk_tcl2ruby(sel)
563 result.push tk_tcl2ruby(val)
569 idx = str.index(/ /, i)
571 result.push str[i..(idx-1)]
574 result.push str[i..-1]
581 kvis.push [result.shift, result.shift, result.shift]
583 kvis # result is [[key1, value1, index1], [key2, value2, index2], ...]
586 def _retrieve_braced_text(str, i)
601 return str[i+1..idx-1], idx + 2
603 private :_retrieve_braced_text
605 def _retrieve_backslashed_text(str, i)
609 idx = str.index(/ /, j)
616 val = str[i..(idx-1)]
617 val.gsub!(/\\( |\{|\})/, '\1')
620 private :_retrieve_backslashed_text
622 def dump_all(*index, &block)
623 dump(['all'], *index, &block)
625 def dump_mark(*index, &block)
626 dump(['mark'], *index, &block)
628 def dump_tag(*index, &block)
629 dump(['tag'], *index, &block)
631 def dump_text(*index, &block)
632 dump(['text'], *index, &block)
634 def dump_window(*index, &block)
635 dump(['window'], *index, &block)
637 def dump_image(*index, &block)
638 dump(['image'], *index, &block)
642 class TkTextTag<TkObject
643 include TkTreatTagFont
645 Tk_TextTag_ID = ['tag0000']
647 def initialize(parent, *args)
648 if not parent.kind_of?(TkText)
649 fail format("%s need to be TkText", parent.inspect)
651 @parent = @t = parent
652 @path = @id = Tk_TextTag_ID[0]
653 Tk_TextTag_ID[0] = Tk_TextTag_ID[0].succ
654 #tk_call @t.path, "tag", "configure", @id, *hash_kv(keys)
657 if keys.kind_of? Hash then
658 add(*args) if args != []
681 tk_call @t.path, 'tag', 'add', @id, *index
685 tk_call @t.path, 'tag', 'remove', @id, *index
689 l = tk_split_simplelist(tk_call(@t.path, 'tag', 'ranges', @id))
692 r.push [key, l.shift]
697 def nextrange(first, last=None)
698 tk_split_simplelist(tk_call(@t.path, 'tag', 'nextrange', @id, first, last))
701 def prevrange(first, last=None)
702 tk_split_simplelist(tk_call(@t.path, 'tag', 'prevrange', @id, first, last))
715 when 'text', 'label', 'show', 'data', 'flie'
716 tk_call @t.path, 'tag', 'cget', @id, "-#{key}"
718 tk_tcl2ruby tk_call @t.path, 'tag', 'cget', @id, "-#{key}"
722 def configure(key, val=None)
723 @t.tag_configure @id, key, val
725 # def configure(key, val=None)
726 # if key.kind_of? Hash
727 # tk_call @t.path, 'tag', 'configure', @id, *hash_kv(key)
729 # tk_call @t.path, 'tag', 'configure', @id, "-#{key}", val
732 # def configure(key, value)
735 # elsif value.kind_of? Proc
736 # value = install_cmd(value)
738 # tk_call @t.path, 'tag', 'configure', @id, "-#{key}", value
741 def configinfo(key=nil)
742 @t.tag_configinfo @id, key
745 def bind(seq, cmd=Proc.new, args=nil)
746 _bind([@t.path, 'tag', 'bind', @id], seq, cmd, args)
749 def bind_append(seq, cmd=Proc.new, args=nil)
750 _bind_append([@t.path, 'tag', 'bind', @id], seq, cmd, args)
753 def bindinfo(context=nil)
754 _bindinfo([@t.path, 'tag', 'bind', @id], context)
757 def raise(above=None)
758 tk_call @t.path, 'tag', 'raise', @id, above
761 def lower(below=None)
762 tk_call @t.path, 'tag', 'lower', @id, below
766 tk_call @t.path, 'tag', 'delete', @id
770 class TkTextTagSel<TkTextTag
771 def initialize(parent, keys=nil)
772 if not parent.kind_of?(TkText)
773 fail format("%s need to be TkText", parent.inspect)
777 #tk_call @t.path, "tag", "configure", @id, *hash_kv(keys)
778 configure(keys) if keys
783 class TkTextMark<TkObject
784 Tk_TextMark_ID = ['mark0000']
785 def initialize(parent, index)
786 if not parent.kind_of?(TkText)
787 fail format("%s need to be TkText", parent.inspect)
790 @path = @id = Tk_TextMark_ID[0]
791 Tk_TextMark_ID[0] = Tk_TextMark_ID[0].succ
792 tk_call @t.path, 'mark', 'set', @id, index
800 tk_call @t.path, 'mark', 'set', @id, where
804 tk_call @t.path, 'mark', 'unset', @id
809 tk_call @t.path, 'mark', 'gravity', @id
812 def gravity=(direction)
813 tk_call @t.path, 'mark', 'gravity', @id, direction
817 @t.tagid2obj(tk_call(@t.path, 'mark', 'next', index))
821 @t.tagid2obj(tk_call(@t.path, 'mark', 'previous', index))
825 class TkTextMarkInsert<TkTextMark
826 def initialize(parent, index=nil)
827 if not parent.kind_of?(TkText)
828 fail format("%s need to be TkText", parent.inspect)
831 @path = @id = 'insert'
832 tk_call @t.path, 'mark', 'set', @id, index if index
837 class TkTextMarkCurrent<TkTextMark
838 def initialize(parent,index=nil)
839 if not parent.kind_of?(TkText)
840 fail format("%s need to be TkText", parent.inspect)
843 @path = @id = 'current'
844 tk_call @t.path, 'mark', 'set', @id, index if index
849 class TkTextMarkAnchor<TkTextMark
850 def initialize(parent,index=nil)
851 if not parent.kind_of?(TkText)
852 fail format("%s need to be TkText", parent.inspect)
855 @path = @id = 'anchor'
856 tk_call @t.path, 'mark', 'set', @id, index if index
861 class TkTextWindow<TkObject
862 def initialize(parent, index, keys)
863 if not parent.kind_of?(TkText)
864 fail format("%s need to be TkText", parent.inspect)
868 @path = TkTextMark.new(@t, tk_call(@t.path, 'index', 'end - 1 chars'))
869 elsif index.kind_of? TkTextMark
870 if tk_call(@t.path,'index',index.path) == tk_call(@t.path,'index','end')
871 @path = TkTextMark.new(@t, tk_call(@t.path, 'index', 'end - 1 chars'))
873 @path = TkTextMark.new(@t, tk_call(@t.path, 'index', index.path))
876 @path = TkTextMark.new(@t, tk_call(@t.path, 'index', index))
878 @path.gravity = 'left'
882 @p_create = keys['create']
883 if @p_create.kind_of? Proc
884 keys['create'] = install_cmd(proc{@id = @p_create.call; @id.path})
887 tk_call @t.path, 'window', 'create', @index, *hash_kv(keys)
894 configure(slot, value)
899 when 'text', 'label', 'show', 'data', 'flie'
900 tk_call @t.path, 'window', 'cget', @index, "-#{slot}"
902 tk_tcl2ruby tk_call @t.path, 'window', 'cget', @index, "-#{slot}"
906 def configure(slot, value=None)
907 if slot.kind_of? Hash
908 @id = slot['window'] if slot['window']
914 tk_call @t.path, 'window', 'configure', @index, *hash_kv(slot)
917 @id = value if slot == 'window'
921 tk_call @t.path, 'window', 'configure', @index, "-#{slot}", value
931 tk_call @t.path, 'window', 'configure', @index, '-window', value
941 if @p_create.kind_of? Proc
942 value = install_cmd(proc{@id = @p_create.call})
944 tk_call @t.path, 'window', 'configure', @index, '-create', value
947 def configinfo(slot = nil)
950 when 'text', 'label', 'show', 'data', 'flie'
951 conf = tk_split_simplelist(tk_call @t.path, 'window', 'configure',
954 conf = tk_split_list(tk_call @t.path, 'window', 'configure',
957 conf[0] = conf[0][1..-1]
960 tk_split_simplelist(tk_call @t.path, 'window', 'configure',
961 @index).collect{|conflist|
962 conf = tk_split_simplelist(conflist)
963 conf[0] = conf[0][1..-1]
965 when 'text', 'label', 'show', 'data', 'flie'
968 if conf[3].index('{')
969 conf[3] = tk_split_list(conf[3])
971 conf[3] = tk_tcl2ruby(conf[3])
975 if conf[4].index('{')
976 conf[4] = tk_split_list(conf[4])
978 conf[4] = tk_tcl2ruby(conf[4])
989 class TkTextImage<TkObject
990 def initialize(parent, index, keys)
991 if not parent.kind_of?(TkText)
992 fail format("%s need to be TkText", parent.inspect)
996 @path = TkTextMark.new(@t, tk_call(@t.path, 'index', 'end - 1 chars'))
997 elsif index.kind_of? TkTextMark
998 if tk_call(@t.path,'index',index.path) == tk_call(@t.path,'index','end')
999 @path = TkTextMark.new(@t, tk_call(@t.path, 'index', 'end - 1 chars'))
1001 @path = TkTextMark.new(@t, tk_call(@t.path, 'index', index.path))
1004 @path = TkTextMark.new(@t, tk_call(@t.path, 'index', index))
1006 @path.gravity = 'left'
1008 @id = tk_call(@t.path, 'image', 'create', @index, *hash_kv(keys))
1014 def []=(slot, value)
1015 configure(slot, value)
1020 when 'text', 'label', 'show', 'data', 'flie'
1021 tk_call @t.path, 'image', 'cget', @index, "-#{slot}"
1023 tk_tcl2ruby tk_call @t.path, 'image', 'cget', @index, "-#{slot}"
1027 def configure(slot, value=None)
1028 if slot.kind_of? Hash
1029 tk_call @t.path, 'image', 'configure', @index, *hash_kv(slot)
1031 tk_call @t.path, 'image', 'configure', @index, "-#{slot}", value
1034 # def configure(slot, value)
1035 # tk_call @t.path, 'image', 'configure', @index, "-#{slot}", value
1039 tk_call @t.path, 'image', 'configure', @index, '-image'
1043 tk_call @t.path, 'image', 'configure', @index, '-image', value
1046 def configinfo(slot = nil)
1049 when 'text', 'label', 'show', 'data', 'flie'
1050 conf = tk_split_simplelist(tk_call @t.path, 'image', 'configure',
1053 conf = tk_split_list(tk_call @t.path, 'image', 'configure',
1056 conf[0] = conf[0][1..-1]
1059 tk_split_simplelist(tk_call @t.path, 'image', 'configure',
1060 @index).collect{|conflist|
1061 conf = tk_split_simplelist(conflist)
1062 conf[0] = conf[0][1..-1]
1064 when 'text', 'label', 'show', 'data', 'flie'
1067 if conf[3].index('{')
1068 conf[3] = tk_split_list(conf[3])
1070 conf[3] = tk_tcl2ruby(conf[3])
1074 if conf[4].index('{')
1075 conf[4] = tk_split_list(conf[4])
1077 conf[4] = tk_tcl2ruby(conf[4])