summaryrefslogtreecommitdiff
path: root/ruby_1_8_6/ext/tk/sample/tktextio.rb
diff options
context:
space:
mode:
Diffstat (limited to 'ruby_1_8_6/ext/tk/sample/tktextio.rb')
-rw-r--r--ruby_1_8_6/ext/tk/sample/tktextio.rb1050
1 files changed, 1050 insertions, 0 deletions
diff --git a/ruby_1_8_6/ext/tk/sample/tktextio.rb b/ruby_1_8_6/ext/tk/sample/tktextio.rb
new file mode 100644
index 0000000000..4573bcebdf
--- /dev/null
+++ b/ruby_1_8_6/ext/tk/sample/tktextio.rb
@@ -0,0 +1,1050 @@
+#!/usr/bin/env ruby
+#
+# TkTextIO class :: handling I/O stream on a TkText widget
+# by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+# NOTE: TkTextIO supports 'character' (not 'byte') access only.
+# So, for example, TkTextIO#getc returns a character, TkTextIO#pos
+# means the character position, TkTextIO#read(size) counts by
+# characters, and so on.
+# Of course, it is available to make TkTextIO class to suuport
+# 'byte' access. However, it may break multi-byte characters.
+# and then, displayed string on the text widget may be garbled.
+# I think that it is not good on the supposed situation of using
+# TkTextIO.
+#
+require 'tk'
+require 'tk/text'
+require 'tk/textmark'
+require 'thread'
+
+class TkTextIO < TkText
+ # keep safe level
+ @@create_queues = proc{ [Queue.new, Mutex.new, Queue.new, Mutex.new] }
+
+ OPT_DEFAULTS = {
+ 'mode' => nil,
+ 'overwrite' => false,
+ 'text' => nil,
+ 'show' => :pos,
+ 'wrap' => 'char',
+ 'sync' => true,
+ 'prompt' => nil,
+ 'prompt_cmd' => nil,
+ 'hist_size' => 1000,
+ }
+
+ def create_self(keys)
+ opts = _get_io_params((keys.kind_of?(Hash))? keys: {})
+
+ super(keys)
+
+ @count_var = TkVariable.new
+
+ @write_buffer = ''
+ @read_buffer = ''
+ @buf_size = 0
+ @buf_max = 1024
+
+ @write_buf_queue, @write_buf_mutex,
+ @read_buf_queue, @read_buf_mutex = @@create_queues.call
+
+ @idle_flush = TkTimer.new(:idle, 1, proc{ @flusher.run rescue nil })
+ @timer_flush = TkTimer.new(250, -1, proc{ @flusher.run rescue nil })
+
+ @flusher = Thread.new{ loop { Thread.stop; flush() } }
+
+ @receiver = Thread.new{
+ begin
+ loop {
+ str = @write_buf_queue.deq
+ @write_buf_mutex.synchronize { @write_buffer << str }
+ @idle_flush.start
+ }
+ ensure
+ @flusher.kill
+ end
+ }
+
+ @timer_flush.start
+
+ _setup_io(opts)
+ end
+ private :create_self
+
+ def destroy
+ @flusher.kill rescue nil
+
+ @idle_flush.stop rescue nil
+ @timer_flush.stop rescue nil
+
+ @receiver.kill rescue nil
+
+ super()
+ end
+
+ ####################################
+
+ def _get_io_params(keys)
+ opts = {}
+ self.class.const_get(:OPT_DEFAULTS).each{|k, v|
+ if keys.has_key?(k)
+ opts[k] = keys.delete(k)
+ else
+ opts[k] = v
+ end
+ }
+ opts
+ end
+
+ def _setup_io(opts)
+ unless defined? @txtpos
+ @txtpos = TkTextMark.new(self, '1.0')
+ else
+ @txtpos.set('1.0')
+ end
+ @txtpos.gravity = :left
+
+ @lineno = 0
+ @line_offset = 0
+
+ @hist_max = opts['hist_size'].to_i
+ @hist_index = 0
+ @history = Array.new(@hist_max)
+ @history[0] = ''
+
+ self['wrap'] = wrap
+
+ self.show_mode = opts['show']
+
+ self.value = opts['text'] if opts['text']
+
+ @overwrite = (opts['overwrite'])? true: false
+
+ @sync = opts['sync']
+
+ @prompt = opts['prompt']
+ @prompt_cmd = opts['prompt_cmd']
+
+ @open = {:r => true, :w => true} # default is 'r+'
+
+ @console_mode = false
+ @end_of_stream = false
+ @console_buffer = nil
+
+ case opts['mode']
+ when nil
+ # do nothing
+
+ when :console, 'console'
+ @console_mode = true
+ # @console_buffer = TkTextIO.new(:mode=>'r')
+ @console_buffer = self.class.new(:mode=>'r')
+ self.show_mode = :insert
+
+ when 'r', 'rb'
+ @open[:r] = true; @open[:w] = nil
+
+ when 'r+', 'rb+', 'r+b'
+ @open[:r] = true; @open[:w] = true
+
+ when 'w', 'wb'
+ @open[:r] = nil; @open[:w] = true
+ self.value=''
+
+ when 'w+', 'wb+', 'w+b'
+ @open[:r] = true; @open[:w] = true
+ self.value=''
+
+ when 'a', 'ab'
+ @open[:r] = nil; @open[:w] = true
+ @txtpos.set('end - 1 char')
+ @txtpos.gravity = :right
+
+ when 'a+', 'ab+', 'a+b'
+ @open[:r] = true; @open[:w] = true
+ @txtpos.set('end - 1 char')
+ @txtpos.gravity = :right
+
+ else
+ fail ArgumentError, "unknown mode `#{opts['mode']}'"
+ end
+
+ unless defined? @ins_head
+ @ins_head = TkTextMark.new(self, 'insert')
+ @ins_head.gravity = :left
+ end
+
+ unless defined? @ins_tail
+ @ins_tail = TkTextMark.new(self, 'insert')
+ @ins_tail.gravity = :right
+ end
+
+ unless defined? @tmp_mark
+ @tmp_mark = TkTextMark.new(self, 'insert')
+ @tmp_mark.gravity = :left
+ end
+
+ if @console_mode
+ _set_console_line
+ _setup_console_bindings
+ end
+ end
+ private :_get_io_params, :_setup_io
+
+ def _set_console_line
+ @tmp_mark.set(@ins_tail)
+
+ mark_set('insert', 'end')
+
+ prompt = ''
+ prompt << @prompt_cmd.call if @prompt_cmd
+ prompt << @prompt if @prompt
+ insert(@tmp_mark, prompt)
+
+ @ins_head.set(@ins_tail)
+ @ins_tail.set('insert')
+
+ @txtpos.set(@tmp_mark)
+
+ _see_pos
+ end
+
+ def _replace_console_line(str)
+ self.delete(@ins_head, @ins_tail)
+ self.insert(@ins_head, str)
+ end
+
+ def _get_console_line
+ @tmp_mark.set(@ins_tail)
+ s = self.get(@ins_head, @tmp_mark)
+ _set_console_line
+ s
+ end
+ private :_set_console_line, :_replace_console_line, :_get_console_line
+
+ def _cb_up
+ @history[@hist_index].replace(self.get(@ins_head, @ins_tail))
+ @hist_index += 1
+ @hist_index -= 1 if @hist_index >= @hist_max || !@history[@hist_index]
+ _replace_console_line(@history[@hist_index]) if @history[@hist_index]
+ Tk.callback_break
+ end
+ def _cb_down
+ @history[@hist_index].replace(self.get(@ins_head, @ins_tail))
+ @hist_index -= 1
+ @hist_index = 0 if @hist_index < 0
+ _replace_console_line(@history[@hist_index]) if @history[@hist_index]
+ Tk.callback_break
+ end
+ def _cb_left
+ if @console_mode && compare('insert', '<=', @ins_head)
+ mark_set('insert', @ins_head)
+ Tk.callback_break
+ end
+ end
+ def _cb_backspace
+ if @console_mode && compare('insert', '<=', @ins_head)
+ Tk.callback_break
+ end
+ end
+ def _cb_ctrl_a
+ if @console_mode
+ mark_set('insert', @ins_head)
+ Tk.callback_break
+ end
+ end
+ private :_cb_up, :_cb_down, :_cb_left, :_cb_backspace, :_cb_ctrl_a
+
+ def _setup_console_bindings
+ @bindtag = TkBindTag.new
+
+ tags = self.bindtags
+ tags[tags.index(self)+1, 0] = @bindtag
+ self.bindtags = tags
+
+ @bindtag.bind('Return'){
+ insert('end - 1 char', "\n")
+ if (str = _get_console_line)
+ @read_buf_queue.push(str)
+
+ @history[0].replace(str.chomp)
+ @history.pop
+ @history.unshift('')
+ @hist_index = 0
+ end
+
+ Tk.update
+ Tk.callback_break
+ }
+ @bindtag.bind('Alt-Return'){
+ Tk.callback_continue
+ }
+
+ @bindtag.bind('FocusIn'){
+ if @console_mode
+ mark_set('insert', @ins_tail)
+ Tk.callback_break
+ end
+ }
+
+ ins_mark = TkTextMark.new(self, 'insert')
+
+ @bindtag.bind('ButtonPress'){
+ if @console_mode
+ ins_mark.set('insert')
+ end
+ }
+
+ @bindtag.bind('ButtonRelease-1'){
+ if @console_mode && compare('insert', '<=', @ins_head)
+ mark_set('insert', ins_mark)
+ Tk.callback_break
+ end
+ }
+
+ @bindtag.bind('ButtonRelease-2', '%x %y'){|x, y|
+ if @console_mode
+ # paste a text at 'insert' only
+ x1, y1, x2, y2 = bbox(ins_mark)
+ unless x == x1 && y == y1
+ Tk.event_generate(self, 'ButtonRelease-2', :x=>x1, :y=>y1)
+ Tk.callback_break
+ end
+ end
+ }
+
+ @bindtag.bind('Up'){ _cb_up }
+ @bindtag.bind('Control-p'){ _cb_up }
+
+ @bindtag.bind('Down'){ _cb_down }
+ @bindtag.bind('Control-n'){ _cb_down }
+
+ @bindtag.bind('Left'){ _cb_left }
+ @bindtag.bind('Control-b'){ _cb_left }
+
+ @bindtag.bind('BackSpace'){ _cb_backspace }
+ @bindtag.bind('Control-h'){ _cb_backspace }
+
+ @bindtag.bind('Home'){ _cb_ctrl_a }
+ @bindtag.bind('Control-a'){ _cb_ctrl_a }
+ end
+ private :_setup_console_bindings
+
+ def _block_read(size = nil, ret = '', block_mode = true)
+ return '' if size == 0
+ return nil if ! @read_buf_queue && @read_buffer.empty?
+ ret = '' unless ret.kind_of?(String)
+ ret.replace('') unless ret.empty?
+
+ if block_mode == nil # partial
+ if @read_buffer.empty?
+ ret << @read_buffer.slice!(0..-1)
+ return ret
+ end
+ end
+
+ if size.kind_of?(Numeric)
+ loop{
+ @read_buf_mutex.synchronize {
+ buf_len = @read_buffer.length
+ if buf_len >= size
+ ret << @read_buffer.slice!(0, size)
+ return ret
+ else
+ ret << @read_buffer.slice!(0..-1)
+ size -= buf_len
+ return ret unless @read_buf_queue
+ end
+ }
+ @read_buffer << @read_buf_queue.pop
+ }
+ else # readline
+ rs = (size)? size: $/
+ rs = rs.to_s if rs.kind_of?(Regexp)
+ loop{
+ @read_buf_mutex.synchronize {
+ if (str = @read_buffer.slice!(/\A(.*)(#{rs})/m))
+ ret << str
+ return ret
+ else
+ ret << @read_buffer.slice!(0..-1)
+ return ret unless @read_buf_queue
+ end
+ }
+ @read_buffer << @read_buf_queue.pop
+ }
+ end
+ end
+
+ def _block_write
+ ###### currently, not support
+ end
+ private :_block_read, :_block_write
+
+ ####################################
+
+ def <<(obj)
+ _write(obj)
+ self
+ end
+
+ def binmode
+ self
+ end
+
+ def clone
+ fail NotImplementedError, 'cannot clone TkTextIO'
+ end
+ def dup
+ fail NotImplementedError, 'cannot duplicate TkTextIO'
+ end
+
+ def close
+ close_read
+ close_write
+ nil
+ end
+ def close_read
+ @open[:r] = false if @open[:r]
+ nil
+ end
+ def close_write
+ @open[:w] = false if @opne[:w]
+ nil
+ end
+
+ def closed?(dir=nil)
+ case dir
+ when :r, 'r'
+ !@open[:r]
+ when :w, 'w'
+ !@open[:w]
+ else
+ !@open[:r] && !@open[:w]
+ end
+ end
+
+ def _check_readable
+ fail IOError, "not opened for reading" if @open[:r].nil?
+ fail IOError, "closed stream" if !@open[:r]
+ end
+ def _check_writable
+ fail IOError, "not opened for writing" if @open[:w].nil?
+ fail IOError, "closed stream" if !@open[:w]
+ end
+ private :_check_readable, :_check_writable
+
+ def each_line(rs = $/)
+ _check_readable
+ while(s = self.gets(rs))
+ yield(s)
+ end
+ self
+ end
+ alias each each_line
+
+ def each_char
+ _check_readable
+ while(c = self.getc)
+ yield(c)
+ end
+ self
+ end
+ alias each_byte each_char
+
+ def eof?
+ compare(@txtpos, '==', 'end - 1 char')
+ end
+ alias eof eof?
+
+ def fcntl(*args)
+ fail NotImplementedError, "fcntl is not implemented on #{self.class}"
+ end
+
+ def fsync
+ 0
+ end
+
+ def fileno
+ nil
+ end
+
+ def flush
+ Thread.pass
+ if @open[:w] || ! @write_buffer.empty?
+ @write_buf_mutex.synchronize {
+ _sync_write_buf(@write_buffer)
+ @write_buffer[0..-1] = ''
+ }
+ end
+ self
+ end
+
+ def getc
+ return _block_read(1) if @console_mode
+
+ _check_readable
+ return nil if eof?
+ c = get(@txtpos)
+ @txtpos.set(@txtpos + '1 char')
+ _see_pos
+ c
+ end
+
+ def gets(rs = $/)
+ return _block_read(rs) if @console_mode
+
+ _check_readable
+ return nil if eof?
+ _readline(rs)
+ end
+
+ def ioctrl(*args)
+ fail NotImplementedError, 'iocntl is not implemented on TkTextIO'
+ end
+
+ def isatty
+ false
+ end
+ def tty?
+ false
+ end
+
+ def lineno
+ @lineno + @line_offset
+ end
+
+ def lineno=(num)
+ @line_offset = num - @lineno
+ num
+ end
+
+ def overwrite?
+ @overwrite
+ end
+
+ def overwrite=(ovwt)
+ @overwrite = (ovwt)? true: false
+ end
+
+ def pid
+ nil
+ end
+
+ def index_pos
+ index(@txtpos)
+ end
+ alias tell_index index_pos
+
+ def index_pos=(idx)
+ @txtpos.set(idx)
+ @txtpos.set('end - 1 char') if compare(@txtpos, '>=', :end)
+ _see_pos
+ idx
+ end
+
+ def pos
+ s = get('1.0', @txtpos)
+ number(tk_call('string', 'length', s))
+ end
+ alias tell pos
+
+ def pos=(idx)
+ seek(idx, IO::SEEK_SET)
+ idx
+ end
+
+ def pos_gravity
+ @txtpos.gravity
+ end
+
+ def pos_gravity=(side)
+ @txtpos.gravity = side
+ side
+ end
+
+ def print(arg=$_, *args)
+ _check_writable
+ args.unshift(arg)
+ args.map!{|val| (val == nil)? 'nil': val.to_s }
+ str = args.join($,)
+ str << $\ if $\
+ _write(str)
+ nil
+ end
+ def printf(*args)
+ _check_writable
+ _write(sprintf(*args))
+ nil
+ end
+
+ def putc(c)
+ _check_writable
+ c = c.chr if c.kind_of?(Fixnum)
+ _write(c)
+ c
+ end
+
+ def puts(*args)
+ _check_writable
+ if args.empty?
+ _write("\n")
+ return nil
+ end
+ args.each{|arg|
+ if arg == nil
+ _write("nil\n")
+ elsif arg.kind_of?(Array)
+ puts(*arg)
+ elsif arg.kind_of?(String)
+ _write(arg.chomp)
+ _write("\n")
+ else
+ begin
+ arg = arg.to_ary
+ puts(*arg)
+ rescue
+ puts(arg.to_s)
+ end
+ end
+ }
+ nil
+ end
+
+ def _read(len)
+ epos = @txtpos + "#{len} char"
+ s = get(@txtpos, epos)
+ @txtpos.set(epos)
+ @txtpos.set('end - 1 char') if compare(@txtpos, '>=', :end)
+ _see_pos
+ s
+ end
+ private :_read
+
+ def read(len=nil, buf=nil)
+ return _block_read(len, buf) if @console_mode
+
+ _check_readable
+ if len
+ return "" if len == 0
+ return nil if eof?
+ s = _read(len)
+ else
+ s = get(@txtpos, 'end - 1 char')
+ @txtpos.set('end - 1 char')
+ _see_pos
+ end
+ buf.replace(s) if buf.kind_of?(String)
+ s
+ end
+
+ def readchar
+ return _block_read(1) if @console_mode
+
+ _check_readable
+ fail EOFError if eof?
+ c = get(@txtpos)
+ @txtpos.set(@txtpos + '1 char')
+ _see_pos
+ c
+ end
+
+ def _readline(rs = $/)
+ if rs == nil
+ s = get(@txtpos, 'end - 1 char')
+ @txtpos.set('end - 1 char')
+ elsif rs == ''
+ @count_var.value # make it global
+ idx = tksearch_with_count([:regexp], @count_var,
+ "\n(\n)+", @txtpos, 'end - 1 char')
+ if idx
+ s = get(@txtpos, idx) << "\n"
+ @txtpos.set("#{idx} + #{@count_var.value} char")
+ @txtpos.set('end - 1 char') if compare(@txtpos, '>=', :end)
+ else
+ s = get(@txtpos, 'end - 1 char')
+ @txtpos.set('end - 1 char')
+ end
+ else
+ @count_var.value # make it global
+ idx = tksearch_with_count(@count_var, rs, @txtpos, 'end - 1 char')
+ if idx
+ s = get(@txtpos, "#{idx} + #{@count_var.value} char")
+ @txtpos.set("#{idx} + #{@count_var.value} char")
+ @txtpos.set('end - 1 char') if compare(@txtpos, '>=', :end)
+ else
+ s = get(@txtpos, 'end - 1 char')
+ @txtpos.set('end - 1 char')
+ end
+ end
+
+ _see_pos
+ @lineno += 1
+ $_ = s
+ end
+ private :_readline
+
+ def readline(rs = $/)
+ return _block_readline(rs) if @console_mode
+
+ _check_readable
+ fail EOFError if eof?
+ _readline(rs)
+ end
+
+ def readlines(rs = $/)
+ if @console_mode
+ lines = []
+ while (line = _block_readline(rs))
+ lines << line
+ end
+ return lines
+ end
+
+ _check_readable
+ lines = []
+ until(eof?)
+ lines << _readline(rs)
+ end
+ $_ = nil
+ lines
+ end
+
+ def readpartial(maxlen, buf=nil)
+ #return @console_buffer.readpartial(maxlen, buf) if @console_mode
+ return _block_read(maxlen, buf, nil) if @console_mode
+
+ _check_readable
+ fail EOFError if eof?
+ s = _read(maxlen)
+ buf.replace(s) if buf.kind_of?(String)
+ s
+ end
+
+ def reopen(*args)
+ fail NotImplementedError, 'reopen is not implemented on TkTextIO'
+ end
+
+ def rewind
+ @txtpos.set('1.0')
+ _see_pos
+ @lineno = 0
+ @line_offset = 0
+ self
+ end
+
+ def seek(offset, whence=IO::SEEK_SET)
+ case whence
+ when IO::SEEK_SET
+ offset = "1.0 + #{offset} char" if offset.kind_of?(Numeric)
+ @txtpos.set(offset)
+
+ when IO::SEEK_CUR
+ offset = "#{offset} char" if offset.kind_of?(Numeric)
+ @txtpos.set(@txtpos + offset)
+
+ when IO::SEEK_END
+ offset = "#{offset} char" if offset.kind_of?(Numeric)
+ @txtpos.set("end - 1 char + #{offset}")
+
+ else
+ fail Errno::EINVAL, 'invalid whence argument'
+ end
+
+ @txtpos.set('end - 1 char') if compare(@txtpos, '>=', :end)
+ _see_pos
+
+ 0
+ end
+ alias sysseek seek
+
+ def _see_pos
+ see(@show) if @show
+ end
+ private :_see_pos
+
+ def show_mode
+ (@show == @txtpos)? :pos : @show
+ end
+
+ def show_mode=(mode)
+ # define show mode when file position is changed.
+ # mode == :pos or "pos" or true :: see current file position.
+ # mode == :insert or "insert" :: see insert cursor position.
+ # mode == nil or false :: do nothing
+ # else see 'mode' position ('mode' should be text index or mark)
+ case mode
+ when :pos, 'pos', true
+ @show = @txtpos
+ when :insert, 'insert'
+ @show = :insert
+ when nil, false
+ @show = false
+ else
+ begin
+ index(mode)
+ rescue
+ fail ArgumentError, 'invalid show-position'
+ end
+ @show = mode
+ end
+
+ _see_pos
+
+ mode
+ end
+
+ def stat
+ fail NotImplementedError, 'stat is not implemented on TkTextIO'
+ end
+
+ def sync
+ @sync
+ end
+
+ def sync=(mode)
+ @sync = mode
+ end
+
+ def sysread(len, buf=nil)
+ return _block_read(len, buf) if @console_mode
+
+ _check_readable
+ fail EOFError if eof?
+ s = _read(len)
+ buf.replace(s) if buf.kind_of?(String)
+ s
+ end
+
+ def syswrite(obj)
+ _write(obj)
+ end
+
+ def to_io
+ self
+ end
+
+ def trancate(len)
+ delete("1.0 + #{len} char", :end)
+ 0
+ end
+
+ def ungetc(c)
+ if @console_mode
+ @read_buf_mutex.synchronize {
+ @read_buffer[0,0] = c.chr
+ }
+ return nil
+ end
+
+ _check_readable
+ c = c.chr if c.kind_of?(Fixnum)
+ if compare(@txtpos, '>', '1.0')
+ @txtpos.set(@txtpos - '1 char')
+ delete(@txtpos)
+ insert(@txtpos, tk_call('string', 'range', c, 0, 1))
+ @txtpos.set(@txtpos - '1 char') if @txtpos.gravity == 'right'
+ _see_pos
+ else
+ fail IOError, 'cannot ungetc at head of stream'
+ end
+ nil
+ end
+
+=begin
+ def _write(obj)
+ #s = _get_eval_string(obj)
+ s = (obj.kind_of?(String))? obj: obj.to_s
+ n = number(tk_call('string', 'length', s))
+ delete(@txtpos, @txtpos + "#{n} char") if @overwrite
+ self.insert(@txtpos, s)
+ @txtpos.set(@txtpos + "#{n} char")
+ @txtpos.set('end - 1 char') if compare(@txtpos, '>=', :end)
+ _see_pos
+ Tk.update if @sync
+ n
+ end
+ private :_write
+=end
+#=begin
+ def _sync_write_buf(s)
+ if (n = number(tk_call('string', 'length', s))) > 0
+ delete(@txtpos, @txtpos + "#{n} char") if @overwrite
+ self.insert(@txtpos, s)
+ #Tk.update
+
+ @txtpos.set(@txtpos + "#{n} char")
+ @txtpos.set('end - 1 char') if compare(@txtpos, '>=', :end)
+
+ @ins_head.set(@txtpos) if compare(@txtpos, '>', @ins_head)
+
+ _see_pos
+ end
+ self
+ end
+ private :_sync_write_buf
+
+ def _write(obj)
+ s = (obj.kind_of?(String))? obj: obj.to_s
+ n = number(tk_call('string', 'length', s))
+ @write_buf_queue.enq(s)
+ if @sync
+ Thread.pass
+ Tk.update
+ end
+ n
+ end
+ private :_write
+#=end
+
+ def write(obj)
+ _check_writable
+ _write(obj)
+ end
+end
+
+####################
+# TEST
+####################
+if __FILE__ == $0
+ ev_loop = Thread.new{Tk.mainloop}
+
+ f = TkFrame.new.pack
+ #tio = TkTextIO.new(f, :show=>:nil,
+ #tio = TkTextIO.new(f, :show=>:pos,
+ tio = TkTextIO.new(f, :show=>:insert,
+ :text=>">>> This is an initial text line. <<<\n\n"){
+# yscrollbar(TkScrollbar.new(f).pack(:side=>:right, :fill=>:y))
+ pack(:side=>:left, :fill=>:both, :expand=>true)
+ }
+
+ Tk.update
+
+ $stdin = tio
+ $stdout = tio
+ $stderr = tio
+
+ STDOUT.print("\n========= TkTextIO#gets for inital text ========\n\n")
+
+ while(s = gets)
+ STDOUT.print(s)
+ end
+
+ STDOUT.print("\n============ put strings to TkTextIO ===========\n\n")
+
+ puts "On this sample, a text widget works as if it is a I/O stream."
+ puts "Please see the code."
+ puts
+ printf("printf message: %d %X\n", 123456, 255)
+ puts
+ printf("(output by 'p' method) This TkTextIO object is ...\n")
+ p tio
+ print(" [ Current wrap mode of this object is 'char'. ]\n")
+ puts
+ warn("This is a warning message generated by 'warn' method.")
+ puts
+ puts "current show_mode is #{tio.show_mode}."
+ if tio.show_mode == :pos
+ puts "So, you can see the current file position on this text widget."
+ else
+ puts "So, you can see the position '#{tio.show_mode}' on this text widget."
+ end
+ print("Please scroll up this text widget to see the head of lines.\n")
+ print("---------------------------------------------------------\n")
+
+ STDOUT.print("\n=============== TkTextIO#readlines =============\n\n")
+
+ tio.seek(0)
+ lines = readlines
+ STDOUT.puts(lines.inspect)
+
+ STDOUT.print("\n================== TkTextIO#each ===============\n\n")
+
+ tio.rewind
+ tio.each{|line| STDOUT.printf("%2d: %s\n", tio.lineno, line.chomp)}
+
+ STDOUT.print("\n================================================\n\n")
+
+ STDOUT.print("\n========= reverse order (seek by lines) ========\n\n")
+
+ tio.seek(-1, IO::SEEK_END)
+ begin
+ begin
+ tio.seek(:linestart, IO::SEEK_CUR)
+ rescue
+ # maybe use old version of tk/textmark.rb
+ tio.seek('0 char linestart', IO::SEEK_CUR)
+ end
+ STDOUT.print(gets)
+ tio.seek('-1 char linestart -1 char', IO::SEEK_CUR)
+ end while(tio.pos > 0)
+
+ STDOUT.print("\n================================================\n\n")
+
+ tio.seek(0, IO::SEEK_END)
+
+ STDOUT.print("tio.sync == #{tio.sync}\n")
+# tio.sync = false
+# STDOUT.print("tio.sync == #{tio.sync}\n")
+
+ (0..10).each{|i|
+ STDOUT.print("#{i}\n")
+ s = ''
+ (0..1000).each{ s << '*' }
+ print(s)
+ }
+ print("\n")
+ print("\n=========================================================\n\n")
+
+ s = ''
+ timer = TkTimer.new(:idle, -1, proc{
+ #STDOUT.print("idle call\n")
+ unless s.empty?
+ print(s)
+ s = ''
+ end
+ }).start
+ (0..10).each{|i|
+ STDOUT.print("#{i}\n")
+ (0..1000).each{ s << '*' }
+ }
+# timer.stop
+ until s.empty?
+ sleep 0.1
+ end
+ timer.stop
+
+=begin
+ tio.sync = false
+ print("\n")
+ #(0..10000).each{ putc('*') }
+ (0..10).each{|i|
+ STDOUT.print("#{i}\n")
+ (0..1000).each{ putc('*') }
+ }
+
+ (0..10).each{|i|
+ STDOUT.print("#{i}\n")
+ s = ''
+ (0..1000).each{ s << '*' }
+ print(s)
+ }
+=end
+
+ num = 0
+# io = TkTextIO.new(:mode=>:console, :prompt=>'').pack
+#=begin
+ io = TkTextIO.new(:mode=>:console,
+ :prompt_cmd=>proc{
+ s = "[#{num}]"
+ num += 1
+ s
+ },
+ :prompt=>'-> ').pack
+#=end
+ Thread.new{loop{sleep 2; io.puts 'hoge'}}
+ Thread.new{loop{p io.gets}}
+
+ ev_loop.join
+end