mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* 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/trunk@11283 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
ced53248ff
commit
2ec88c167b
10 changed files with 1692 additions and 206 deletions
25
ChangeLog
25
ChangeLog
|
@ -1,3 +1,28 @@
|
|||
Mon Nov 6 15:41:55 2006 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
|
||||
|
||||
* ext/tk/lib/tk/itemconfig.rb: 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.
|
||||
|
||||
Mon Nov 6 00:42:05 2006 Yukihiro Matsumoto <matz@ruby-lang.org>
|
||||
|
||||
* parse.y (arg_dup_check): vid may be nameless internal id.
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
2006-11-06 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
|
||||
|
||||
* lib/tkextlib/version.rb: keep release date of tkextlib on
|
||||
"Tk::Tkextlib_RELEASE_DATE".
|
||||
|
||||
* lib/tkextlib/tile/treeview.rb : support Tile 0.7.8.
|
||||
Now, you can handle tree items as objects.
|
||||
|
||||
2006-10-04 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
|
||||
|
||||
* lib/tkextlib/tile.rb, lib/tkextlib/tile/* : support Tile 0.7.6.
|
||||
|
|
|
@ -4597,7 +4597,7 @@ end
|
|||
#Tk.freeze
|
||||
|
||||
module Tk
|
||||
RELEASE_DATE = '2006-09-01'.freeze
|
||||
RELEASE_DATE = '2006-11-06'.freeze
|
||||
|
||||
autoload :AUTO_PATH, 'tk/variable'
|
||||
autoload :TCL_PACKAGE_PATH, 'tk/variable'
|
||||
|
@ -4609,6 +4609,7 @@ end
|
|||
|
||||
# call setup script for Tk extension libraries (base configuration)
|
||||
begin
|
||||
require 'tkextlib/version.rb'
|
||||
require 'tkextlib/setup.rb'
|
||||
rescue LoadError
|
||||
# ignore
|
||||
|
|
|
@ -289,7 +289,7 @@ module TkItemConfigMethod
|
|||
self
|
||||
end
|
||||
|
||||
def itemconfiginfo(tagOrId, slot = nil)
|
||||
def __itemconfiginfo_core(tagOrId, slot = nil)
|
||||
if TkComm::GET_CONFIGINFO_AS_ARRAY
|
||||
if (slot && slot.to_s =~ /^(|latin|ascii|kanji)(#{__item_font_optkeys(tagid(tagOrId)).join('|')})$/)
|
||||
fontkey = $2
|
||||
|
@ -594,7 +594,7 @@ module TkItemConfigMethod
|
|||
if v.empty?
|
||||
conf[__item_configinfo_struct(tagid(tagOrId))[:current_value]] = nil
|
||||
else
|
||||
conf[__item_configinfo_struct(tagid(tagOrId))[:current_value]] = TkVarAccess.new
|
||||
conf[__item_configinfo_struct(tagid(tagOrId))[:current_value]] = TkVarAccess.new(v)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1020,13 +1020,18 @@ module TkItemConfigMethod
|
|||
end
|
||||
end
|
||||
end
|
||||
private :__itemconfiginfo_core
|
||||
|
||||
def itemconfiginfo(tagOrId, slot = nil)
|
||||
__itemconfiginfo_core(tagOrId, slot)
|
||||
end
|
||||
|
||||
def current_itemconfiginfo(tagOrId, slot = nil)
|
||||
if TkComm::GET_CONFIGINFO_AS_ARRAY
|
||||
if slot
|
||||
org_slot = slot
|
||||
begin
|
||||
conf = itemconfiginfo(tagOrId, slot)
|
||||
conf = __itemconfiginfo_core(tagOrId, slot)
|
||||
if ( ! __item_configinfo_struct(tagid(tagOrId))[:alias] \
|
||||
|| conf.size > __item_configinfo_struct(tagid(tagOrId))[:alias] + 1 )
|
||||
return {conf[0] => conf[-1]}
|
||||
|
@ -1037,7 +1042,7 @@ module TkItemConfigMethod
|
|||
"there is a configure alias loop about '#{org_slot}'"
|
||||
else
|
||||
ret = {}
|
||||
itemconfiginfo(tagOrId).each{|conf|
|
||||
__itemconfiginfo_core(tagOrId).each{|conf|
|
||||
if ( ! __item_configinfo_struct(tagid(tagOrId))[:alias] \
|
||||
|| conf.size > __item_configinfo_struct(tagid(tagOrId))[:alias] + 1 )
|
||||
ret[conf[0]] = conf[-1]
|
||||
|
@ -1047,7 +1052,7 @@ module TkItemConfigMethod
|
|||
end
|
||||
else # ! TkComm::GET_CONFIGINFO_AS_ARRAY
|
||||
ret = {}
|
||||
itemconfiginfo(slot).each{|key, conf|
|
||||
itemconfiginfo(tagOrId, slot).each{|key, conf|
|
||||
ret[key] = conf[-1] if conf.kind_of?(Array)
|
||||
}
|
||||
ret
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
[ current support status of Tcl/Tk extensions ]
|
||||
|
||||
*******<<< RELEASE_DATE of the libraries : 2006/10/04 >>>*******
|
||||
*** RELEASE_DATE of the libraries => see 'tkextlib/version.rb' ***
|
||||
|
||||
The following list shows *CURRENT* status when this file was modifyed
|
||||
at last. If you want to add other Tcl/Tk extensions to the planed list
|
||||
|
@ -83,7 +83,7 @@ BLT 2.4z http://sourceforge.net/projects/blt
|
|||
TkTreeCtrl CVS/Hd(2005-12-02)
|
||||
http://sourceforge.net/projects/tktreectrl ==> treectrl
|
||||
|
||||
Tile CVS/Hd(2006-10-01)
|
||||
Tile 0.7.8
|
||||
http://sourceforge.net/projects/tktable ==> tile
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
6
ext/tk/lib/tkextlib/version.rb
Normal file
6
ext/tk/lib/tkextlib/version.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# release date of tkextlib
|
||||
#
|
||||
module Tk
|
||||
Tkextlib_RELEASE_DATE = '2006-11-06'.freeze
|
||||
end
|
69
ext/tk/sample/editable_listbox.rb
Normal file
69
ext/tk/sample/editable_listbox.rb
Normal file
|
@ -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
|
119
ext/tk/sample/irbtkw.rbw
Normal file
119
ext/tk/sample/irbtkw.rbw
Normal file
|
@ -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
|
|
@ -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,68 +14,375 @@
|
|||
# TkTextIO.
|
||||
#
|
||||
require 'tk'
|
||||
require 'tk/text'
|
||||
require 'tk/textmark'
|
||||
require 'thread'
|
||||
|
||||
class TkTextIO < TkText
|
||||
def create_self(keys)
|
||||
mode = nil
|
||||
ovwt = false
|
||||
text = nil
|
||||
wrap = 'char'
|
||||
show = :pos
|
||||
# keep safe level
|
||||
@@create_queues = proc{ [Queue.new, Mutex.new, Queue.new, Mutex.new] }
|
||||
|
||||
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
|
||||
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)
|
||||
|
||||
self['wrap'] = wrap
|
||||
insert('1.0', text)
|
||||
@count_var = TkVariable.new
|
||||
|
||||
@txtpos = TkTextMark.new(self, '1.0')
|
||||
@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
|
||||
|
||||
self.show_mode = show
|
||||
|
||||
@sync = true
|
||||
@overwrite = (ovwt)? true: false
|
||||
|
||||
@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)
|
||||
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue