diff options
Diffstat (limited to 'ruby_1_8_6/ext/tk/sample/tktextio.rb')
-rw-r--r-- | ruby_1_8_6/ext/tk/sample/tktextio.rb | 1050 |
1 files changed, 0 insertions, 1050 deletions
diff --git a/ruby_1_8_6/ext/tk/sample/tktextio.rb b/ruby_1_8_6/ext/tk/sample/tktextio.rb deleted file mode 100644 index 4573bcebdf..0000000000 --- a/ruby_1_8_6/ext/tk/sample/tktextio.rb +++ /dev/null @@ -1,1050 +0,0 @@ -#!/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 |