# # tk.rb - Tk interface modue using tcltklib # $Date$ # by Yukihiro Matsumoto # use Shigehiro's tcltklib require "tcltklib" require "tkutil" module TkComm None = Object.new def None.to_s 'None' end Tk_CMDTBL = {} Tk_WINDOWS = {} def error_at frames = caller(1) frames.delete_if do |c| c =~ %r!/tk(|core|thcore|canvas|text|entry|scrollbox)\.rb:\d+! end frames end private :error_at def tk_tcl2ruby(val) if val.include? ?\s return val.split.collect{|v| tk_tcl2ruby(v)} end case val when /^-?\d+$/ val.to_i when /^\./ Tk_WINDOWS[val] when /^rb_out (c\d+)/ Tk_CMDTBL[$1] when / / val.split.collect{|elt| tk_tcl2ruby(elt) } when /^-?\d+\.\d*$/ val.to_f else val end end def tk_split_list(str) return [] if str == "" idx = str.index('{') return tk_tcl2ruby(str) unless idx list = tk_tcl2ruby(str[0,idx]) str = str[idx+1..-1] i = -1 brace = 1 str.each_byte {|c| i += 1 brace += 1 if c == ?{ brace -= 1 if c == ?} break if brace == 0 } if str[0, i] == ' ' list.push ' ' else list.push tk_split_list(str[0, i]) end list += tk_split_list(str[i+1..-1]) list end private :tk_tcl2ruby, :tk_split_list def hash_kv(keys) conf = [] if keys and keys != None for k, v in keys conf.push("-#{k}") v = install_cmd(v) if v.kind_of? Proc conf.push(v) end end conf end private :hash_kv def bool(val) case val when "1", 1, 'yes', 'true' TRUE else FALSE end end def number(val) case val when /^-?\d+$/ val.to_i when /^-?\d+\.\d*$/ val.to_f else val end end def string(val) if val == "{}" '' elsif val[0] == ?{ val[1..-2] else val end end def list(val) tk_split_list(val).to_a end def window(val) Tk_WINDOWS[val] end def procedure(val) if val =~ /^rb_out (c\d+)/ Tk_CMDTBL[$1] else nil end end private :bool, :number, :string, :list, :window, :procedure Tk_IDs = [0] # [0]-cmdid, [1]-winid def _curr_cmd_id id = format("c%.4d", Tk_IDs[0]) end def _next_cmd_id id = _curr_cmd_id Tk_IDs[0] += 1 end def install_cmd(cmd) return '' if cmd == '' id = _next_cmd_id Tk_CMDTBL[id] = cmd @cmdtbl = [] if not @cmdtbl @cmdtbl.push id return format("rb_out %s", id); end def uninstall_cmd(id) Tk_CMDTBL[id] = nil end private :install_cmd, :uninstall_cmd def install_win(ppath) id = format("w%.4d", Tk_IDs[0]) Tk_IDs[0] += 1 if !ppath or ppath == "." @path = format(".%s", id); else @path = format("%s.%s", ppath, id) end Tk_WINDOWS[@path] = self end def uninstall_win() Tk_WINDOWS[@path] = nil end class Event def initialize(seq,b,f,h,k,s,t,w,x,y,aa,ee,kk,nn,ww,tt,xx,yy) @serial = seq @num = b @focus = (f == 1) @height = h @keycode = k @state = s @time = t @width = w @x = x @y = y @char = aa @send_event = (ee == 1) @keysym = kk @keysym_num = nn @type = tt @widget = ww @x_root = xx @y_root = yy end attr :serial attr :num attr :focus attr :height attr :keycode attr :state attr :time attr :width attr :x attr :y attr :char attr :send_event attr :keysym attr :keysym_num attr :type attr :widget attr :x_root attr :y_root end def install_bind(cmd, args=nil) if args id = install_cmd(proc{|arg| TkUtil.eval_cmd cmd, *arg }) id + " " + args else id = install_cmd(proc{|arg| TkUtil.eval_cmd cmd, Event.new(*arg) }) id + ' %# %b %f %h %k %s %t %w %x %y %A %E %K %N %W %T %X %Y' end end def _bind(path, context, cmd, args=nil) begin id = install_bind(cmd, args) tk_call 'bind', path, "<#{context}>", id rescue uninstall_cmd(id) fail end end private :install_bind, :_bind def bind_all(context, cmd=Proc.new, args=nil) _bind 'all', context, cmd, args end def pack(*args) TkPack.configure *args end def after(ms, cmd=Proc.new) myid = _curr_cmd_id tk_call 'after', ms, install_cmd(proc{ TkUtil.eval_cmd cmd uninstall_cmd myid }) end def update(idle=nil) if idle tk_call 'update', 'idletasks' else tk_call 'update' end end end module TkCore include TkComm extend TkComm INTERP = TclTkIp.new INTERP._invoke("proc", "rb_out", "args", "ruby [format \"TkCore.callback %%Q!%s!\" $args]") def TkCore.callback(arg) arg = Array(tk_split_list(arg)) TkUtil.eval_cmd Tk_CMDTBL[arg.shift], *arg end def mainloop TclTkLib.mainloop end def _get_eval_string(str) return nil if str == None if str.kind_of?(Hash) str = hash_kv(str).join(" ") elsif str == nil str = "" elsif str == false str = "0" elsif str == true str = "1" elsif (str.respond_to?(:to_eval)) str = str.to_eval() else str = str.to_s() end return str end def tk_call(*args) print args.join(" "), "\n" if $DEBUG args.filter {|x|_get_eval_string(x)} args.compact! args.flatten! begin res = INTERP._invoke(*args) rescue NameError err = $! begin args.unshift "unknown" res = INTERP._invoke(*args) rescue raise unless /^invalid command/ =~ $! raise err end end if INTERP._return_value() != 0 fail RuntimeError, res, error_at end print "==> ", res, "\n" if $DEBUG return res end end module Tk include TkCore extend Tk def root TkRoot.new end def bell tk_call 'bell' end module Scrollable def xscrollcommand(cmd=Proc.new) configure_cmd 'xscrollcommand', cmd end def yscrollcommand(cmd=Proc.new) configure_cmd 'yscrollcommand', cmd end end module Wm def aspect(*args) w = window(tk_call('wm', 'grid', path, *args)) w.split.collect{|s|s.to_i} if args.length == 0 end def client(name=None) tk_call 'wm', 'client', path, name end def colormapwindows(*args) list(tk_call('wm', 'colormapwindows', path, *args)) end def wm_command(value=None) string(tk_call('wm', 'command', path, value)) end def deiconify tk_call 'wm', 'deiconify', path end def focusmodel(*args) tk_call 'wm', 'focusmodel', path, *args end def frame tk_call 'wm', 'frame', path end def geometry(*args) list(tk_call('wm', 'geometry', path, *args)) end def grid(*args) w = tk_call('wm', 'grid', path, *args) list(w) if args.size == 0 end def group(*args) tk_call 'wm', 'path', path, *args end def iconbitmap(*args) tk_call 'wm', 'bitmap', path, *args end def iconify tk_call 'wm', 'iconify', path end def iconmask(*args) tk_call 'wm', 'iconmask', path, *args end def iconname(*args) tk_call 'wm', 'iconname', path, *args end def iconposition(*args) w = tk_call('wm', 'iconposition', path, *args) list(w) if args.size == 0 end def iconwindow(*args) w = tk_call('wm', 'iconwindow', path, *args) window(w) if args.size == 0 end def maxsize(*args) w = tk_call('wm', 'maxsize', path, *args) list(w) if not args.size == 0 end def minsize(*args) w = tk_call('wm', 'minsize', path, *args) list(w) if args.size == 0 end def overrideredirect(bool=None) if bool == None bool(tk_call('wm', 'overrideredirect', path)) else tk_call 'wm', 'overrideredirect', path, bool end end def positionfrom(*args) tk_call 'wm', 'positionfrom', path, *args end def protocol(name, func=None) func = install_cmd(func) if not func == None tk_call 'wm', 'command', path, name, func end def resizable(*args) w = tk_call('wm', 'resizable', path, *args) if args.length == 0 list(w).collect{|e| bool(e)} end end def sizefrom(*args) list(tk_call('wm', 'sizefrom', path, *args)) end def state tk_call 'wm', 'state', path end def title(*args) tk_call 'wm', 'title', path, *args end def transient(*args) tk_call 'wm', 'transient', path, *args end def withdraw tk_call 'wm', 'withdraw', path end end end class TkVariable include Tk Tk_VARIABLE_ID = ["v00000"] def initialize(val="") @id = Tk_VARIABLE_ID[0] Tk_VARIABLE_ID[0] = Tk_VARIABLE_ID[0].succ s = '"' + _get_eval_string(val).gsub(/[][$"]/, '\\\\\&') + '"' #' INTERP._eval(format('global %s; set %s %s', @id, @id, s)) end def id @id end def value INTERP._eval(format('global %s; set %s', @id, @id)) end def value=(val) INTERP._eval(format('global %s; set %s %s', @id, @id, _get_eval_string(val))) end def to_i Integer(number(value)) end def to_f Float(number(value)) end def to_s String(string(value)) end def inspect format "", @id end def ==(other) case other when TkVariable self.equal(self) when String self.to_s == other when Integer self.to_i == other when Float self.to_f == other when Array self.to_a == other else false end end def to_a list(value) end def to_eval @id end end module TkSelection include Tk extend Tk def clear(win=Tk.root) tk_call 'selection', 'clear', win.path end def get(type=None) tk_call 'selection', 'get', type end def TkSelection.handle(win, func, type=None, format=None) id = install_cmd(func) tk_call 'selection', 'handle', win.path, id, type, format end def handle(func, type=None, format=None) TkSelection.handle self, func, type, format end def TkSelection.own(win, func=None) id = install_cmd(func) tk_call 'selection', 'own', win.path, id end def own(func=None) TkSelection.own self, func end module_function :clear, :get end module TkWinfo include Tk extend Tk def TkWinfo.atom(name) tk_call 'winfo', name end def winfo_atom(name) TkWinfo.atom name end def TkWinfo.atomname(id) tk_call 'winfo', id end def winfo_atomname(id) TkWinfo.atomname id end def TkWinfo.cells(window) number(tk_call('winfo', window.path)) end def winfo_cells TkWinfo.cells self end def TkWinfo.children(window) c = tk_call('winfo', 'children', window.path) list(c) end def winfo_children TkWinfo.children self end def TkWinfo.classname(window) tk_call 'winfo', 'class', window.path end def winfo_classname TkWinfo.classname self end def TkWinfo.containing(rootX, rootY) path = tk_call('winfo', 'class', window.path) window(path) end def winfo_containing(x, y) TkWinfo.containing x, y end def TkWinfo.depth(window) number(tk_call('winfo', 'depth', window.path)) end def winfo_depth(window) TkWinfo.depth self end def TkWinfo.exist?(window) bool(tk_call('winfo', 'exists', window.path)) end def winfo_exist?(window) TkWinfo.exist? self end def TkWinfo.fpixels(window, number) number(tk_call('winfo', 'fpixels', window.path, number)) end def winfo_fpixels(window, number) TkWinfo.fpixels self end def TkWinfo.geometry(window) list(tk_call('winfo', 'geometry', window.path)) end def winfo_geometry(window) TkWinfo.geometry self end def TkWinfo.height(window) number(tk_call('winfo', 'height', window.path)) end def winfo_height(window) TkWinfo.height self end def TkWinfo.id(window) number(tk_call('winfo', 'id', window.path)) end def winfo_id(window) TkWinfo.id self end def TkWinfo.mapped?(window) bool(tk_call('winfo', 'ismapped', window.path)) end def winfo_mapped?(window) TkWinfo.mapped? self end def TkWinfo.parent(window) window(tk_call('winfo', 'parent', window.path)) end def winfo_parent(window) TkWinfo.parent self end def TkWinfo.widget(id) window(tk_call('winfo', 'pathname', id)) end def winfo_widget(id) TkWinfo.widget id end def TkWinfo.pixels(window, number) number(tk_call('winfo', 'pixels', window.path, number)) end def winfo_pixels(window, number) TkWinfo.pixels self, number end def TkWinfo.reqheight(window) number(tk_call('winfo', 'reqheight', window.path)) end def winfo_reqheight(window) TkWinfo.reqheight self end def TkWinfo.reqwidth(window) number(tk_call('winfo', 'reqwidth', window.path)) end def winfo_reqwidth(window) TkWinfo.reqwidth self end def TkWinfo.rgb(window, color) list(tk_call('winfo', 'rgb', window.path, color)) end def winfo_rgb(window, color) TkWinfo.rgb self, color end def TkWinfo.rootx(window) number(tk_call('winfo', 'rootx', window.path)) end def winfo_rootx(window) TkWinfo.rootx self end def TkWinfo.rooty(window) number(tk_call('winfo', 'rooty', window.path)) end def winfo_rooty(window) TkWinfo.rooty self end def TkWinfo.screen(window) tk_call 'winfo', 'screen', window.path end def winfo_screen(window) TkWinfo.screen self end def TkWinfo.screencells(window) number(tk_call('winfo', 'screencells', window.path)) end def winfo_screencells(window) TkWinfo.screencells self end def TkWinfo.screendepth(window) number(tk_call('winfo', 'screendepth', window.path)) end def winfo_screendepth(window) TkWinfo.screendepth self end def TkWinfo.screenheight (window) number(tk_call('winfo', 'screenheight', window.path)) end def winfo_screenheight(window) TkWinfo.screenheight self end def TkWinfo.screenmmheight(window) number(tk_call('winfo', 'screenmmheight', window.path)) end def winfo_screenmmheight(window) TkWinfo.screenmmheight self end def TkWinfo.screenmmwidth(window) number(tk_call('winfo', 'screenmmwidth', window.path)) end def winfo_screenmmwidth(window) TkWinfo.screenmmwidth self end def TkWinfo.screenvisual(window) tk_call 'winfo', 'screenvisual', window.path end def winfo_screenvisual(window) TkWinfo.screenvisual self end def TkWinfo.screenwidth(window) number(tk_call('winfo', 'screenwidth', window.path)) end def winfo_screenwidth(window) TkWinfo.screenwidth self end def TkWinfo.toplevel(window) window(tk_call('winfo', 'toplevel', window.path)) end def winfo_toplevel(window) TkWinfo.toplevel self end def TkWinfo.visual(window) tk_call 'winfo', 'visual', window.path end def winfo_visual(window) TkWinfo.visual self end def TkWinfo.vrootheigh(window) number(tk_call('winfo', 'vrootheight', window.path)) end def winfo_vrootheight(window) TkWinfo.vrootheight self end def TkWinfo.vrootwidth(window) number(tk_call('winfo', 'vrootwidth', window.path)) end def winfo_vrootwidth(window) TkWinfo.vrootwidth self end def TkWinfo.vrootx(window) number(tk_call('winfo', 'vrootx', window.path)) end def winfo_vrootx(window) TkWinfo.vrootx self end def TkWinfo.vrooty(window) number(tk_call('winfo', 'vrooty', window.path)) end def winfo_vrooty(window) TkWinfo.vrooty self end def TkWinfo.width(window) number(tk_call('winfo', 'width', window.path)) end def winfo_width(window) TkWinfo.width self end def TkWinfo.x(window) number(tk_call('winfo', 'x', window.path)) end def winfo_x(window) TkWinfo.x self end def TkWinfo.y(window) number(tk_call('winfo', 'y', window.path)) end def winfo_y(window) TkWinfo.y self end end module TkPack include Tk extend Tk def configure(win, *args) if args[-1].kind_of?(Hash) keys = args.pop end wins = [win.epath] for i in args wins.push i.epath end tk_call "pack", 'configure', *(wins+hash_kv(keys)) end def forget(*args) tk_call 'pack', 'forget' *args end def propagate(master, bool=None) bool(tk_call('pack', 'propagate', master.epath, bool)) end module_function :configure, :forget, :propagate end module TkOption include Tk extend Tk def add pat, value, pri=None tk_call 'option', 'add', pat, value, pri end def clear tk_call 'option', 'clear' end def get win, classname, name tk_call 'option', 'get', classname, name end def readfile file, pri=None tk_call 'option', 'readfile', file, pri end module_function :add, :clear, :get, :readfile end class TkObject