ruby/ext/tk/lib/tcltk.rb
nagai aff4ce9c77 * ext/tk/*: full update Ruby/Tk to support Ruby(1.9|1.8) and Tc/Tk8.5.
* ext/tk/lib/tkextlib/tile.rb: [incompatible] remove TileWidgets' 
  instate/state/identify method to avoid the conflict with standard
  widget options. Those methods are renamed to ttk_instate/ttk_state/
  ttk_identify (tile_instate/tile_state/tile_identify are available 
  too). Although I don't recommend, if you realy need old methods, 
  please define "Tk::USE_OBSOLETE_TILE_STATE_METHOD = true" before 
  "require 'tkextlib/tile'".

* ext/tk/lib/tkextlib/tile.rb: "Tk::Tile::__Import_Tile_Widgets__!"
  is obsolete. It outputs warning. To control default widget set, 
  use "Tk.default_widget_set = :Ttk".

* ext/tk/lib/tk.rb: __IGNORE_UNKNOWN_CONFIGURE_OPTION__ method and 
  __set_IGNORE_UNKNOWN_CONFIGURE_OPTION__!(mode) method are defind 
  as module methods of TkConfigMethod. It may help users to wrap old 
  Ruby/Tk scripts (use standard widgets) to force to use Ttk widgets.
  Ttk widgets don't have some options of standard widgets which are 
  control the view of widgets. When set ignore-mode true, configure 
  method tries to ignoure such unknown options with no exception. 
  Of course, it may raise other troubles on the GUI design. 
  So, those are a little danger methods. 

* ext/tk/lib/tk/itemconfig.rb: __IGNORE_UNKNOWN_CONFIGURE_OPTION__ 
  method and __set_IGNORE_UNKNOWN_CONFIGURE_OPTION__!(mode) method 
  are defind as module methods of TkItemConfigMethod as the same 
  purpose as TkConfigMethod's ones.

* ext/tk/sample/ttk_wrapper.rb: A new example. This is a tool for 
  wrapping old Ruby/Tk scripts (which use standard widgets) to use 
  Ttk (Tile) widgets as default.

* ext/tk/sample/tkextlib/tile/demo.rb: use ttk_instate/ttk_state 
  method instead of instate/state method.

* ext/tk/lib/tk/root, ext/tk/lib/tk/namespace.rb,
  ext/tk/lib/tk/text.rb, ext/tk/lib/tkextlib/*: some 'instance_eval's  
  are replaced to "instance_exec(self)".

* ext/tk/lib/tk/event.rb: bug fix on KEY_TBL and PROC_TBL (?x is not 
  a character code on Ruby1.9).

* ext/tk/lib/tk/variable.rb: support new style of operation argument 
  on Tcl/Tk's 'trace' command for variables. 

* ext/tk/sample/demos-jp/widget, ext/tk/sample/demos-en/widget: bug fix

* ext/tk/sammple/demos-jp/textpeer.rb, 
  ext/tk/sammple/demos-en/textpeer.rb: new widget demo.

* ext/tk/tcltklib.c: decrase SEGV troubles (probably)

* ext/tk/lib/tk.rb: remove Thread.critical access if Ruby1.9

* ext/tk/lib/tk/multi-tk.rb: support Ruby1.9 (probably)

* ext/tk/lib/tkextlib/tile.rb: add method to define Tcl/Tk command 
  to make Tcl/Tk theme sources (based on different version of Tile 
  extension) available. 
  (Tk::Tile::__define_LoadImages_proc_for_comaptibility__)

* ext/tk/lib/tk.rb, ext/tk/lib/tk/wm.rb: support dockable frames
  (Tcl/Tk8.5 feature). 'wm' command can treat many kinds of widgets 
  as toplevel widgets.

* ext/tk/lib/tkextlib/tile/style.rb: ditto.
  (Tk::Tile::Style.__define_wrapper_proc_for_compatibility__)

* ext/tk/lib/tk/font.rb: add actual_hash and metrics_hash to get 
  properties as a hash. metrics_hash method returns a boolean value 
  for 'fixed' option. But metrics method returns numeric value 
  (0 or 1) for 'fixed' option, because of backward compatibility. 

* ext/tk/lib/tk/timer.rb: somtimes fail to set callback procedure.

* ext/tk/lib/tk.rb: add Tk.sleep and Tk.wakeup method. Tk.sleep 
  doesn't block the eventloop. It will be better to use the method 
  in event callbacks.

* ext/tk/sample/tksleep_sample.rb: sample script about Tk.sleep.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@15849 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-03-29 05:25:45 +00:00

367 lines
10 KiB
Ruby
Raw Blame History

# tof
#### tcltk library, more direct manipulation of tcl/tk
#### Sep. 5, 1997 Y. Shigehiro
require "tcltklib"
################
# module TclTk: collection of tcl/tk utilities (supplies namespace.)
module TclTk
# initialize Hash to hold unique symbols and such
@namecnt = {}
# initialize Hash to hold callbacks
@callback = {}
end
# TclTk.mainloop(): call TclTkLib.mainloop()
def TclTk.mainloop()
print("mainloop: start\n") if $DEBUG
TclTkLib.mainloop()
print("mainloop: end\n") if $DEBUG
end
# TclTk.deletecallbackkey(ca): remove callback from TclTk module
# this does not remove callbacks from tcl/tk interpreter
# without calling this method, TclTkInterpreter will not be GCed
# ca: callback(TclTkCallback)
def TclTk.deletecallbackkey(ca)
print("deletecallbackkey: ", ca.to_s(), "\n") if $DEBUG
@callback.delete(ca.to_s)
end
# TclTk.dcb(ca, wid, W): call TclTk.deletecallbackkey() for each callbacks
# in an array.
# this is for callback for top-level <Destroy>
# ca: array of callbacks(TclTkCallback)
# wid: top-level widget(TclTkWidget)
# w: information about window given by %W(String)
def TclTk.dcb(ca, wid, w)
if wid.to_s() == w
ca.each{|i|
TclTk.deletecallbackkey(i)
}
end
end
# TclTk._addcallback(ca): register callback
# ca: callback(TclTkCallback)
def TclTk._addcallback(ca)
print("_addcallback: ", ca.to_s(), "\n") if $DEBUG
@callback[ca.to_s()] = ca
end
# TclTk._callcallback(key, arg): invoke registered callback
# key: key to select callback (to_s value of the TclTkCallback)
# arg: parameter from tcl/tk interpreter
def TclTk._callcallback(key, arg)
print("_callcallback: ", @callback[key].inspect, "\n") if $DEBUG
@callback[key]._call(arg)
# throw out callback value
# should return String to satisfy rb_eval_string()
return ""
end
# TclTk._newname(prefix): generate unique name(String)
# prefix: prefix of the unique name
def TclTk._newname(prefix)
# generated name counter is stored in @namecnt
if !@namecnt.key?(prefix)
# first appearing prefix, initialize
@namecnt[prefix] = 1
else
# already appeared prefix, generate next name
@namecnt[prefix] += 1
end
return "#{prefix}#{@namecnt[prefix]}"
end
################
# class TclTkInterpreter: tcl/tk interpreter
class TclTkInterpreter
# initialize():
def initialize()
# generate interpreter object
@ip = TclTkIp.new()
# add ruby_fmt command to tcl interpreter
# ruby_fmt command format arguments by `format' and call `ruby' command
# (notice ruby command receives only one argument)
if $DEBUG
@ip._eval("proc ruby_fmt {fmt args} { puts \"ruby_fmt: $fmt $args\" ; set cmd [list ruby [format $fmt $args]] ; uplevel $cmd }")
else
@ip._eval("proc ruby_fmt {fmt args} { set cmd [list ruby [format $fmt $args]] ; uplevel $cmd }")
end
# @ip._get_eval_string(*args): generate string to evaluate in tcl interpreter
# *args: script which is going to be evaluated under tcl/tk
def @ip._get_eval_string(*args)
argstr = ""
args.each{|arg|
argstr += " " if argstr != ""
# call to_eval if it is defined
if (arg.respond_to?(:to_eval))
argstr += arg.to_eval()
else
# call to_s unless defined
argstr += arg.to_s()
end
}
return argstr
end
# @ip._eval_args(*args): evaluate string under tcl/tk interpreter
# returns result string.
# *args: script which is going to be evaluated under tcl/tk
def @ip._eval_args(*args)
# calculate the string to eval in the interpreter
argstr = _get_eval_string(*args)
# evaluate under the interpreter
print("_eval: \"", argstr, "\"") if $DEBUG
res = _eval(argstr)
if $DEBUG
print(" -> \"", res, "\"\n")
elsif _return_value() != 0
print(res, "\n")
end
fail(%Q/can't eval "#{argstr}"/) if _return_value() != 0 #'
return res
end
# generate tcl/tk command object and register in the hash
@commands = {}
# for all commands registered in tcl/tk interpreter:
@ip._eval("info command").split(/ /).each{|comname|
if comname =~ /^[.]/
# if command is a widget (path), generate TclTkWidget,
# and register it in the hash
@commands[comname] = TclTkWidget.new(@ip, comname)
else
# otherwise, generate TclTkCommand
@commands[comname] = TclTkCommand.new(@ip, comname)
end
}
end
# commands(): returns hash of the tcl/tk commands
def commands()
return @commands
end
# rootwidget(): returns root widget(TclTkWidget)
def rootwidget()
return @commands["."]
end
# _tcltkip(): returns @ip(TclTkIp)
def _tcltkip()
return @ip
end
# method_missing(id, *args): execute undefined method as tcl/tk command
# id: method symbol
# *args: method arguments
def method_missing(id, *args)
# if command named by id registered, then execute it
if @commands.key?(id.id2name)
return @commands[id.id2name].e(*args)
else
# otherwise, exception
super
end
end
end
# class TclTkObject: base class of the tcl/tk objects
class TclTkObject
# initialize(ip, exp):
# ip: interpreter(TclTkIp)
# exp: tcl/tk representation
def initialize(ip, exp)
fail("type is not TclTkIp") if !ip.kind_of?(TclTkIp)
@ip = ip
@exp = exp
end
# to_s(): returns tcl/tk representation
def to_s()
return @exp
end
end
# class TclTkCommand: tcl/tk commands
# you should not call TclTkCommand.new()
# commands are created by TclTkInterpreter:initialize()
class TclTkCommand < TclTkObject
# e(*args): execute command. returns String (e is for exec or eval)
# *args: command arguments
def e(*args)
return @ip._eval_args(to_s(), *args)
end
end
# class TclTkLibCommand: tcl/tk commands in the library
class TclTkLibCommand < TclTkCommand
# initialize(ip, name):
# ip: interpreter(TclTkInterpreter)
# name: command name (String)
def initialize(ip, name)
super(ip._tcltkip, name)
end
end
# class TclTkVariable: tcl/tk variable
class TclTkVariable < TclTkObject
# initialize(interp, dat):
# interp: interpreter(TclTkInterpreter)
# dat: the value to set(String)
# if nil, not initialize variable
def initialize(interp, dat)
# auto-generate tcl/tk representation (variable name)
exp = TclTk._newname("v_")
# initialize TclTkObject
super(interp._tcltkip(), exp)
# safe this for `set' command
@set = interp.commands()["set"]
# set value
set(dat) if dat
end
# although you can set/refer variable by using set in tcl/tk,
# we provide the method for accessing variables
# set(data): set tcl/tk variable using `set'
# data: new value
def set(data)
@set.e(to_s(), data.to_s())
end
# get(): read tcl/tk variable(String) using `set'
def get()
return @set.e(to_s())
end
end
# class TclTkWidget: tcl/tk widget
class TclTkWidget < TclTkCommand
# initialize(*args):
# *args: parameters
def initialize(*args)
if args[0].kind_of?(TclTkIp)
# in case the 1st argument is TclTkIp:
# Wrap tcl/tk widget by TclTkWidget
# (used in TclTkInterpreter#initialize())
# need two arguments
fail("invalid # of parameter") if args.size != 2
# ip: interpreter(TclTkIp)
# exp: tcl/tk representation
ip, exp = args
# initialize TclTkObject
super(ip, exp)
elsif args[0].kind_of?(TclTkInterpreter)
# in case 1st parameter is TclTkInterpreter:
# generate new widget from parent widget
# interp: interpreter(TclTkInterpreter)
# parent: parent widget
# command: widget generating tk command(label <20><>)
# *args: argument to the command
interp, parent, command, *args = args
# generate widget name
exp = parent.to_s()
exp += "." if exp !~ /[.]$/
exp += TclTk._newname("w_")
# initialize TclTkObject
super(interp._tcltkip(), exp)
# generate widget
res = @ip._eval_args(command, exp, *args)
# fail("can't create Widget") if res != exp
# for tk_optionMenu, it is legal res != exp
else
fail("first parameter is not TclTkInterpreter")
end
end
end
# class TclTkCallback: tcl/tk callbacks
class TclTkCallback < TclTkObject
# initialize(interp, pr, arg):
# interp: interpreter(TclTkInterpreter)
# pr: callback procedure(Proc)
# arg: string to pass as block parameters of pr
# bind command of tcl/tk uses % replacement for parameters
# pr can receive replaced data using block parameter
# its format is specified by arg string
# You should not specify arg for the command like
# scrollbar with -command option, which receives parameters
# without specifying any replacement
def initialize(interp, pr, arg = nil)
# auto-generate tcl/tk representation (variable name)
exp = TclTk._newname("c_")
# initialize TclTkObject
super(interp._tcltkip(), exp)
# save parameters
@pr = pr
@arg = arg
# register in the module
TclTk._addcallback(self)
end
# to_eval(): retuens string representation for @ip._eval_args
def to_eval()
if @arg
# bind replaces %s before calling ruby_fmt, so %%s is used
s = %Q/{ruby_fmt {TclTk._callcallback("#{to_s()}", "%%s")} #{@arg}}/
else
s = %Q/{ruby_fmt {TclTk._callcallback("#{to_s()}", "%s")}}/
end
return s
end
# _call(arg): invoke callback
# arg: callback parameter
def _call(arg)
@pr.call(arg)
end
end
# class TclTkImage: tcl/tk images
class TclTkImage < TclTkCommand
# initialize(interp, t, *args):
# generating image is done by TclTkImage.new()
# destrying is done by image delete (inconsistent, sigh)
# interp: interpreter(TclTkInterpreter)
# t: image type (photo, bitmap, etc.)
# *args: command argument
def initialize(interp, t, *args)
# auto-generate tcl/tk representation
exp = TclTk._newname("i_")
# initialize TclTkObject
super(interp._tcltkip(), exp)
# generate image
res = @ip._eval_args("image create", t, exp, *args)
fail("can't create Image") if res != exp
end
end
# eof