summaryrefslogtreecommitdiff
path: root/ext/tk/sample
diff options
context:
space:
mode:
authornagai <nagai@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2006-11-06 06:56:37 +0000
committernagai <nagai@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2006-11-06 06:56:37 +0000
commit1d82954444cba4017a26ee5feff93e885dba2245 (patch)
treed3b0cb578874bf8dcf8fd74ad5c39e444d5fd3df /ext/tk/sample
parent2734015cfe474868ee051ed4cfe6cdee0d0c3dc5 (diff)
* ext/tk/lib/tk/itemconfig.rb: bug fix on 'itemconfiginfo' method, and
modify to make it easy to override 'itemconfiginfo' method. * ext/tk/lib/tkextlib/tile/treeview.rb : support Tile 0.7.8. * ext/tk/lib/tkextlib/version.rb : [new] add Tk::Tkextlib_RELEASE_DATE to get the information from scripts. * ext/tk/lib/tk.rb: load 'tkextlib/version.rb', and update RELEASE_DATE. * ext/tk/lib/tkextlib/SUPPORT_STATUS: update. * ext/tk/sample/editable_listbox.rb: [new] the listbox with editable items. It's one of the example about usage of Place geometry manager. * ext/tk/sample/tktextio.rb: improve the functions of TkTextIO class. Those are required by 'irbtkw.rbw'. * ext/tk/sample/irbtkw.rbw: [new] IRB on Ruby/Tk. It doesn't need any real console. IRB works on a text widget without I/O blocking. That is, thread switching on IRB will work properly, even if on Windows. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@11283 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ext/tk/sample')
-rw-r--r--ext/tk/sample/editable_listbox.rb69
-rw-r--r--ext/tk/sample/irbtkw.rbw119
-rw-r--r--ext/tk/sample/tktextio.rb547
3 files changed, 685 insertions, 50 deletions
diff --git a/ext/tk/sample/editable_listbox.rb b/ext/tk/sample/editable_listbox.rb
new file mode 100644
index 0000000000..99345da380
--- /dev/null
+++ b/ext/tk/sample/editable_listbox.rb
@@ -0,0 +1,69 @@
+#
+# Editable_TkListbox class
+#
+# When "DoubleClick-1" on a listbox item, the entry box is opend on the
+# item. And when hit "Return" key on the entry box after modifying the
+# text, the entry box is closed and the item is changed. Or when hit
+# "Escape" key, the entry box is closed without modification.
+#
+# by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+require 'tk'
+
+class Editable_TkListbox < TkListbox
+ def _ebox_placer(coord_y)
+ idx = self.nearest(coord_y)
+ x, y, w, h = self.bbox(idx)
+ @ebox.place(:x => 0, :relwidth => 1.0,
+ :y => y - self.selectborderwidth,
+ :height => h + 2 * self.selectborderwidth)
+ @ebox.pos = idx
+ @ebox.value = self.listvariable.list[idx]
+ @ebox.focus
+ end
+ private :_ebox_placer
+
+
+ def create_self(keys)
+ super(keys)
+
+ unless self.listvariable
+ self.listvariable = TkVariable.new(self.get(0, :end))
+ end
+
+ @ebox = TkEntry.new(self){
+ @pos = -1
+ def self.pos; @pos; end
+ def self.pos=(idx); @pos = idx; end
+ }
+
+ @ebox.bind('Return'){
+ list = self.listvariable.list
+ list[@ebox.pos] = @ebox.value
+ self.listvariable.value = list
+ @ebox.place_forget
+ @ebox.pos = -1
+ }
+
+ @ebox.bind('Escape'){
+ @ebox.place_forget
+ @ebox.pos = -1
+ }
+
+ self.bind('Double-1', '%y'){|y| _ebox_placer(y) }
+ end
+end
+
+if $0 == __FILE__
+ scr = TkScrollbar.new.pack(:side=>:right, :fill=>:y)
+
+ lbox1 = Editable_TkListbox.new.pack(:side=>:left)
+ lbox2 = Editable_TkListbox.new.pack(:side=>:left)
+
+ scr.assign(lbox1, lbox2)
+
+ lbox1.insert(:end, *%w(a b c d e f g h i j k l m n))
+ lbox2.insert(:end, 0,1,2,3,4,5,6,7,8,9,0,1,2,3)
+
+ Tk.mainloop
+end
diff --git a/ext/tk/sample/irbtkw.rbw b/ext/tk/sample/irbtkw.rbw
new file mode 100644
index 0000000000..92fa5692f2
--- /dev/null
+++ b/ext/tk/sample/irbtkw.rbw
@@ -0,0 +1,119 @@
+#!/usr/bin/env ruby
+#
+# irbtkw.rb : IRB console with Ruby/Tk
+#
+# by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+release = '2006/11/06'
+
+require 'tk'
+begin
+ require 'tktextio'
+rescue LoadError
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'tktextio.rb')
+end
+
+require 'irb'
+
+# console setup
+top = TkToplevel.new(:title=>'IRB console')
+top.protocol(:WM_DELETE_WINDOW){ Tk.exit }
+
+console = TkTextIO.new(top, :mode=>:console,
+ :width=>80).pack(:side=>:left,
+ :expand=>true, :fill=>:both)
+console.yscrollbar(TkScrollbar.new(top, :width=>10).pack(:before=>console,
+ :side=>:right,
+ :expand=>false,
+ :fill=>:y))
+ev_loop = Thread.new{Tk.mainloop}
+
+# window position control
+root = Tk.root
+
+r_x = root.winfo_rootx
+r_y = root.winfo_rooty
+r_w = root.winfo_width
+
+t_x = top.winfo_rootx
+t_y = top.winfo_rooty
+t_w = top.winfo_width
+
+delta = 10
+
+ratio = 0.8
+s_w = (ratio * root.winfo_screenwidth).to_i
+
+if r_x < t_x
+ r_x, t_x = t_x, r_x
+end
+if t_x + t_w + r_w + delta < s_w
+ r_x = t_x + t_w + delta
+elsif t_w + r_w + delta < s_w
+ r_x = s_w - r_w
+ t_x = r_x - t_w
+else
+ r_x = s_w - r_w
+ t_x = 0
+end
+
+root.geometry("+#{r_x}+#{r_y}")
+top.geometry("+#{t_x}+#{t_y}")
+
+root.raise
+console.focus
+
+# I/O setup
+$stdin = console
+$stdout = console
+$stderr = console
+
+# dummy for rubyw.exe on Windows
+def STDIN.tty?
+ true
+end
+
+# IRB setup
+IRB.init_config(nil)
+IRB.conf[:USE_READLINE] = false
+IRB.init_error
+irb = IRB::Irb.new
+IRB.conf[:MAIN_CONTEXT] = irb.context
+
+class IRB::StdioInputMethod
+ def gets
+ prompt = "\n" << @prompt
+ $stdin.instance_eval{
+ flush
+ @prompt = prompt
+ _set_console_line
+ @prompt = nil
+ _see_pos
+ }
+
+ @line[@line_no += 1] = $stdin.gets
+ end
+end
+
+# IRB start
+$stdout.print("*** IRB console on Ruby/Tk (#{release}) ")
+irb_thread = Thread.new{
+ catch(:IRB_EXIT){
+ loop {
+ begin
+ irb.eval_input
+ rescue Exception
+ end
+ }
+ }
+}
+
+console.bind('Control-c'){
+ console.insert('end', "^C\n")
+ irb_thread.raise RubyLex::TerminateLineInput
+}
+
+irb_thread.join
+
+# exit
+Tk.exit
diff --git a/ext/tk/sample/tktextio.rb b/ext/tk/sample/tktextio.rb
index cb59c2d9d6..0b78f45b10 100644
--- a/ext/tk/sample/tktextio.rb
+++ b/ext/tk/sample/tktextio.rb
@@ -1,7 +1,7 @@
#!/usr/bin/env ruby
#
-# sample class of handling I/O stream on a TkText widget
-# by Hidetoshi NAGAI
+# 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
@@ -14,69 +14,376 @@
# 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)
- mode = nil
- ovwt = false
- text = nil
- wrap = 'char'
- show = :pos
-
- if keys.kind_of?(Hash)
- mode = keys.delete('mode')
- ovwt = keys.delete('overwrite')
- text = keys.delete('text')
- show = keys.delete('show') if keys.has_key?('show')
- wrap = keys.delete('wrap') || 'char'
- end
+ opts = _get_io_params((keys.kind_of?(Hash))? keys: {})
super(keys)
- self['wrap'] = wrap
- insert('1.0', text)
+ @count_var = TkVariable.new
- @txtpos = TkTextMark.new(self, '1.0')
- @txtpos.gravity = :left
+ @write_buffer = ''
+ @read_buffer = ''
+ @buf_size = 0
+ @buf_max = 1024
- self.show_mode = show
+ @write_buf_queue, @write_buf_mutex,
+ @read_buf_queue, @read_buf_mutex = @@create_queues.call
- @sync = true
- @overwrite = (ovwt)? true: false
+ @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
- @count_var = TkVariable.new
+
+ @hist_max = opts['hist_size']
+ @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+'
- case mode
- when '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+'
+ when 'r+', 'rb+', 'r+b'
@open[:r] = true; @open[:w] = true
- when 'w'
+ when 'w', 'wb'
@open[:r] = nil; @open[:w] = true
self.value=''
- when 'w+'
+ when 'w+', 'wb+', 'w+b'
@open[:r] = true; @open[:w] = true
self.value=''
- when 'a'
+ when 'a', 'ab'
@open[:r] = nil; @open[:w] = true
- @txtpos = TkTextMark.new(self, 'end - 1 char')
+ @txtpos.set('end - 1 char')
@txtpos.gravity = :right
- when 'a+'
+ when 'a+', 'ab+', 'a+b'
@open[:r] = true; @open[:w] = true
- @txtpos = TkTextMark.new(self, 'end - 1 char')
+ @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
@@ -107,14 +414,15 @@ class TkTextIO < TkText
nil
end
- def closed?
- close_read? && close_write?
- end
- def closed_read?
- !@open[:r]
- end
- def closed_write?
- !@open[:w]
+ 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
@@ -129,7 +437,7 @@ class TkTextIO < TkText
def each_line(rs = $/)
_check_readable
- while(s = gets)
+ while(s = self.gets(rs))
yield(s)
end
self
@@ -138,7 +446,7 @@ class TkTextIO < TkText
def each_char
_check_readable
- while(c = getc)
+ while(c = self.getc)
yield(c)
end
self
@@ -151,7 +459,7 @@ class TkTextIO < TkText
alias eof eof?
def fcntl(*args)
- fail NotImplementedError, 'fcntl is not implemented on TkTextIO'
+ fail NotImplementedError, "fcntl is not implemented on #{self.class}"
end
def fsync
@@ -163,11 +471,19 @@ class TkTextIO < TkText
end
def flush
- Tk.update if @open[:w] && @sync
+ 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)
@@ -177,8 +493,10 @@ class TkTextIO < TkText
end
def gets(rs = $/)
+ return _block_read(rs) if @console_mode
+
_check_readable
- return nil if eof?
+ return nil if eof?
_readline(rs)
end
@@ -233,7 +551,6 @@ class TkTextIO < TkText
alias tell pos
def pos=(idx)
- # @txtpos.set((idx.kind_of?(Numeric))? "1.0 + #{idx} char": idx)
seek(idx, IO::SEEK_SET)
idx
end
@@ -306,6 +623,8 @@ class TkTextIO < TkText
private :_read
def read(len=nil, buf=nil)
+ return _block_read(len, buf) if @console_mode
+
_check_readable
if len
return "" if len == 0
@@ -321,6 +640,8 @@ class TkTextIO < TkText
end
def readchar
+ return _block_read(1) if @console_mode
+
_check_readable
fail EOFError if eof?
c = get(@txtpos)
@@ -334,6 +655,7 @@ class TkTextIO < TkText
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
@@ -345,6 +667,7 @@ class TkTextIO < TkText
@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")
@@ -363,12 +686,22 @@ class TkTextIO < TkText
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?)
@@ -379,7 +712,11 @@ class TkTextIO < TkText
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
@@ -471,6 +808,8 @@ class TkTextIO < TkText
end
def sysread(len, buf=nil)
+ return _block_read(len, buf) if @console_mode
+
_check_readable
fail EOFError if eof?
s = _read(len)
@@ -492,6 +831,13 @@ class TkTextIO < TkText
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')
@@ -506,8 +852,10 @@ class TkTextIO < TkText
nil
end
+=begin
def _write(obj)
- s = _get_eval_string(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)
@@ -518,6 +866,37 @@ class TkTextIO < TkText
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
@@ -529,13 +908,19 @@ end
# TEST
####################
if __FILE__ == $0
+ ev_loop = Thread.new{Tk.mainloop}
+
f = TkFrame.new.pack
- tio = TkTextIO.new(f, :show=>:pos,
+ #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))
+# yscrollbar(TkScrollbar.new(f).pack(:side=>:right, :fill=>:y))
pack(:side=>:left, :fill=>:both, :expand=>true)
}
+ Tk.update
+
$stdin = tio
$stdout = tio
$stderr = tio
@@ -599,5 +984,67 @@ if __FILE__ == $0
tio.seek(0, IO::SEEK_END)
- Tk.mainloop
+ 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