# # tk.rb - Tk interface module using tcltklib # $Date$ # by Yukihiro Matsumoto # use Shigehiro's tcltklib require "tcltklib" require "tkutil" module TkComm WidgetClassNames = {}.taint None = Object.new def None.to_s 'None' end None.freeze #Tk_CMDTBL = {} #Tk_WINDOWS = {} Tk_IDs = ["00000".taint, "00000".taint].freeze # [0]-cmdid, [1]-winid # for backward compatibility Tk_CMDTBL = Object.new def Tk_CMDTBL.method_missing(id, *args) TkCore::INTERP.tk_cmd_tbl.send(id, *args) end Tk_CMDTBL.freeze Tk_WINDOWS = Object.new def Tk_WINDOWS.method_missing(id, *args) TkCore::INTERP.tk_windows.send(id, *args) end Tk_WINDOWS.freeze self.instance_eval{ @cmdtbl = [].taint } def error_at frames = caller() frames.delete_if do |c| c =~ %r!/tk(|core|thcore|canvas|text|entry|scrollbox)\.rb:\d+! end frames end private :error_at def _genobj_for_tkwidget(path) return TkRoot.new if path == '.' begin #tk_class = TkCore::INTERP._invoke('winfo', 'class', path) tk_class = Tk.ip_invoke('winfo', 'class', path) rescue return path end if ruby_class = WidgetClassNames[tk_class] ruby_class_name = ruby_class.name # gen_class_name = ruby_class_name + 'GeneratedOnTk' gen_class_name = ruby_class_name classname_def = '' elsif Object.const_defined?('Tk' + tk_class) ruby_class_name = 'Tk' + tk_class # gen_class_name = ruby_class_name + 'GeneratedOnTk' gen_class_name = ruby_class_name classname_def = '' else ruby_class_name = 'TkWindow' # gen_class_name = ruby_class_name + tk_class + 'GeneratedOnTk' gen_class_name = 'TkWidget_' + tk_class classname_def = "WidgetClassName = '#{tk_class}'.freeze" end unless Object.const_defined? gen_class_name Object.class_eval "class #{gen_class_name}<#{ruby_class_name} #{classname_def} end" end Object.class_eval "#{gen_class_name}.new('widgetname'=>'#{path}', 'without_creating'=>true)" end private :_genobj_for_tkwidget module_function :_genobj_for_tkwidget def tk_tcl2ruby(val) if val =~ /^rb_out (c\d+)/ #return Tk_CMDTBL[$1] return TkCore::INTERP.tk_cmd_tbl[$1] end if val.include? ?\s return val.split.collect{|v| tk_tcl2ruby(v)} end case val when /^@font/ TkFont.get_obj(val) when /^-?\d+$/ val.to_i when /^\./ #Tk_WINDOWS[val] ? Tk_WINDOWS[val] : _genobj_for_tkwidget(val) TkCore::INTERP.tk_windows[val]? TkCore::INTERP.tk_windows[val] : _genobj_for_tkwidget(val) when /^i\d+$/ TkImage::Tk_IMGTBL[val]? TkImage::Tk_IMGTBL[val] : val when / / val.split.collect{|elt| tk_tcl2ruby(elt) } when /^-?\d+\.?\d*(e[-+]?\d+)?$/ val.to_f else val end end def tk_split_list(str) return [] if str == "" idx = str.index('{') while idx and idx > 0 and str[idx-1] == ?\\ idx = str.index('{', idx+1) end unless idx list = tk_tcl2ruby(str) unless Array === list list = [list] end return list end list = tk_tcl2ruby(str[0,idx]) list = [] if list == "" 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.size == i + 1 return tk_split_list(str[0, i]) end 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 def tk_split_simplelist(str) return [] if str == "" idx = str.index('{') while idx and idx > 0 and str[idx-1] == ?\\ idx = str.index('{', idx+1) end return str.split unless idx list = str[0,idx].split 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 i == 0 list.push '' elsif str[0, i] == ' ' list.push ' ' else list.push str[0..i-1] end list += tk_split_simplelist(str[i+1..-1]) list end private :tk_tcl2ruby, :tk_split_list, :tk_split_simplelist def _symbolkey2str(keys) h = {} keys.each{|key,value| h[key.to_s] = value} h end private :_symbolkey2str def hash_kv(keys) conf = [] if keys and keys != None for k, v in keys conf.push("-#{k}") conf.push(v) end end conf end private :hash_kv module_function :hash_kv def array2tk_list(ary) ary.collect{|e| if e.kind_of? Array "{#{array2tk_list(e)}}" elsif e.kind_of? Hash "{#{e.to_a.collect{|ee| array2tk_list(ee)}.join(' ')}}" else s = _get_eval_string(e) (s.index(/\s/) || s.size == 0)? "{#{s}}": s end }.join(" ") end private :array2tk_list module_function :array2tk_list 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*(e[-+]?\d+)?$/ val.to_f else fail(ArgumentError, Kernel.format('invalid value for Number:"%s"', val.to_s)) end end def string(val) if val == "{}" '' elsif val[0] == ?{ val[1..-2] else val end end def list(val) tk_split_list(val) end def simplelist(val) tk_split_simplelist(val) end def window(val) if val =~ /^\./ #Tk_WINDOWS[val]? Tk_WINDOWS[val] : _genobj_for_tkwidget(val) TkCore::INTERP.tk_windows[val]? TkCore::INTERP.tk_windows[val] : _genobj_for_tkwidget(val) else nil end end def procedure(val) if val =~ /^rb_out (c\d+)/ #Tk_CMDTBL[$1] #TkCore::INTERP.tk_cmd_tbl[$1] TkCore::INTERP.tk_cmd_tbl[$1].cmd else #nil val end end private :bool, :number, :string, :list, :simplelist, :window, :procedure module_function :bool, :number, :string, :list, :simplelist module_function :window, :procedure def _get_eval_string(str) return nil if str == None if str.kind_of?(String) # do nothing elsif str.kind_of?(Symbol) str = str.id2name elsif str.kind_of?(Hash) str = hash_kv(str).join(" ") elsif str.kind_of?(Array) str = array2tk_list(str) elsif str.kind_of?(Proc) str = install_cmd(str) 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() || '' unless str.kind_of? String fail RuntimeError, "fail to convert the object to a string" end str end return str end private :_get_eval_string module_function :_get_eval_string def ruby2tcl(v) if v.kind_of?(Hash) v = hash_kv(v) v.flatten! v.collect{|e|ruby2tcl(e)} else _get_eval_string(v) end end private :ruby2tcl def _curr_cmd_id #id = format("c%.4d", Tk_IDs[0]) id = "c" + TkComm::Tk_IDs[0] end def _next_cmd_id id = _curr_cmd_id #Tk_IDs[0] += 1 TkComm::Tk_IDs[0].succ! id end private :_curr_cmd_id, :_next_cmd_id module_function :_curr_cmd_id, :_next_cmd_id def install_cmd(cmd) return '' if cmd == '' id = _next_cmd_id #Tk_CMDTBL[id] = cmd TkCore::INTERP.tk_cmd_tbl[id] = TkCore::INTERP.get_cb_entry(cmd) @cmdtbl = [] unless defined? @cmdtbl @cmdtbl.taint unless @cmdtbl.tainted? @cmdtbl.push id return Kernel.format("rb_out %s", id); end def uninstall_cmd(id) id = $1 if /rb_out (c\d+)/ =~ id #Tk_CMDTBL.delete(id) TkCore::INTERP.tk_cmd_tbl.delete(id) end private :install_cmd, :uninstall_cmd module_function :install_cmd def install_win(ppath,name=nil) if !name or name == '' #name = format("w%.4d", Tk_IDs[1]) #Tk_IDs[1] += 1 name = "w" + Tk_IDs[1] Tk_IDs[1].succ! end if name[0] == ?. @path = name.dup elsif !ppath or ppath == "." @path = Kernel.format(".%s", name); else @path = Kernel.format("%s.%s", ppath, name) end #Tk_WINDOWS[@path] = self TkCore::INTERP.tk_windows[@path] = self end def uninstall_win() #Tk_WINDOWS.delete(@path) TkCore::INTERP.tk_windows.delete(@path) end private :install_win, :uninstall_win class Event module TypeNum KeyPress = 2 KeyRelease = 3 ButtonPress = 4 ButtonRelease = 5 MotionNotify = 6 EnterNotify = 7 LeaveNotify = 8 FocusIn = 9 FocusOut = 10 KeymapNotify = 11 Expose = 12 GraphicsExpose = 13 NoExpose = 14 VisibilityNotify = 15 CreateNotify = 16 DestroyNotify = 17 UnmapNotify = 18 MapNotify = 19 MapRequest = 20 ReparentNotify = 21 ConfigureNotify = 22 ConfigureRequest = 23 GravityNotify = 24 ResizeRequest = 25 CirculateNotify = 26 CirculateRequest = 27 PropertyNotify = 28 SelectionClear = 29 SelectionRequest = 30 SelectionNotify = 31 ColormapNotify = 32 ClientMessage = 33 MappingNotify = 34 end EV_KEY = '#abcdfhikmopstwxyABDEKNRSTWXY' EV_TYPE = 'nsnnsbnsnsbsxnnnnsnnbsnssnwnn' def self.scan_args(arg_str, arg_val) arg_cnv = [] arg_str.strip.split(/\s+/).each_with_index{|kwd,idx| if kwd =~ /^%(.)$/ if num = EV_KEY.index($1) case EV_TYPE[num] when ?n begin val = TkComm::number(arg_val[idx]) rescue ArgumentError # ignore --> no convert val = TkComm::string(arg_val[idx]) end arg_cnv << val when ?s arg_cnv << TkComm::string(arg_val[idx]) when ?b arg_cnv << TkComm::bool(arg_val[idx]) when ?w arg_cnv << TkComm::window(arg_val[idx]) when ?x begin arg_cnv << TkComm::number(arg_val[idx]) rescue ArgumentError arg_cnv << arg_val[idx] end else arg_cnv << arg_val[idx] end else arg_cnv << arg_val[idx] end else arg_cnv << arg_val[idx] end } arg_cnv end def initialize(seq,a,b,c,d,f,h,i,k,m,o,p,s,t,w,x,y, aa,bb,dd,ee,kk,nn,rr,ss,tt,ww,xx,yy) @serial = seq @above = a @num = b @count = c @detail = d @focus = f @height = h @win_hex = i @keycode = k @mode = m @override = o @place = p @state = s @time = t @width = w @x = x @y = y @char = aa @borderwidth = bb @wheel_delta = dd @send_event = ee @keysym = kk @keysym_num = nn @rootwin_id = rr @subwindow = ss @type = tt @widget = ww @x_root = xx @y_root = yy end attr :serial attr :above attr :num attr :count attr :detail attr :focus attr :height attr :win_hex attr :keycode attr :mode attr :override attr :place attr :state attr :time attr :width attr :x attr :y attr :char attr :borderwidth attr :wheel_delta attr :send_event attr :keysym attr :keysym_num attr :rootwin_id attr :subwindow 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, *Event.scan_args(args, arg)) }) id + " " + args else args = ' %# %a %b %c %d %f %h %i %k %m %o %p %s %t %w %x %y' + ' %A %B %D %E %K %N %R %S %T %W %X %Y' id = install_cmd(proc{|*arg| TkUtil.eval_cmd(cmd, Event.new(*Event.scan_args(args, arg))) }) id + args end end def tk_event_sequence(context) if context.kind_of? TkVirtualEvent context = context.path end if context.kind_of? Array context = context.collect{|ev| if ev.kind_of? TkVirtualEvent ev.path else ev end }.join("><") end if /,/ =~ context context = context.split(/\s*,\s*/).join("><") else context end end def _bind_core(mode, what, context, cmd, args=nil) id = install_bind(cmd, args) if cmd begin tk_call(*(what + ["<#{tk_event_sequence(context)}>", mode + id])) rescue uninstall_cmd(id) if cmd fail end end def _bind(what, context, cmd, args=nil) _bind_core('', what, context, cmd, args) end def _bind_append(what, context, cmd, args=nil) _bind_core('+', what, context, cmd, args) end def _bind_remove(what, context) tk_call(*(what + ["<#{tk_event_sequence(context)}>", ''])) end def _bindinfo(what, context=nil) if context tk_call(*what+["<#{tk_event_sequence(context)}>"]).collect {|cmdline| if cmdline =~ /^rb_out (c\d+)\s+(.*)$/ #[Tk_CMDTBL[$1], $2] [TkCore::INTERP.tk_cmd_tbl[$1], $2] else cmdline end } else tk_split_simplelist(tk_call(*what)).collect!{|seq| l = seq.scan(/<*[^<>]+>*/).collect!{|subseq| case (subseq) when /^<<[^<>]+>>$/ TkVirtualEvent.getobj(subseq[1..-2]) when /^<[^<>]+>$/ subseq[1..-2] else subseq.split('') end }.flatten (l.size == 1) ? l[0] : l } end end private :install_bind, :tk_event_sequence, :_bind_core, :_bind, :_bind_append, :_bind_remove, :_bindinfo def bind(tagOrClass, context, cmd=Proc.new, args=nil) _bind(["bind", tagOrClass], context, cmd, args) tagOrClass end def bind_append(tagOrClass, context, cmd=Proc.new, args=nil) _bind_append(["bind", tagOrClass], context, cmd, args) tagOrClass end def bind_remove(tagOrClass, context) _bind_remove(['bind', tagOrClass], context) tagOrClass end def bindinfo(tagOrClass, context=nil) _bindinfo(['bind', tagOrClass], context) end def bind_all(context, cmd=Proc.new, args=nil) _bind(['bind', 'all'], context, cmd, args) TkBindTag::ALL end def bind_append_all(context, cmd=Proc.new, args=nil) _bind_append(['bind', 'all'], context, cmd, args) TkBindTag::ALL end def bind_remove_all(context) _bind_remove(['bind', 'all'], context) TkBindTag::ALL end def bindinfo_all(context=nil) _bindinfo(['bind', 'all'], context) end def pack(*args) TkPack.configure(*args) end def grid(*args) TkGrid.configure(*args) end def update(idle=nil) if idle tk_call 'update', 'idletasks' else tk_call 'update' end end end module TkCore include TkComm extend TkComm unless self.const_defined? :INTERP if self.const_defined? :IP_NAME name = IP_NAME.to_s else name = nil end if self.const_defined? :IP_OPTS if IP_OPTS.kind_of?(Hash) opts = hash_kv(IP_OPTS).join(' ') else opts = IP_OPTS.to_s end else opts = '' end INTERP = TclTkIp.new(name, opts) def INTERP.__getip self end INTERP.instance_eval{ @tk_cmd_tbl = {}.taint @tk_windows = {}.taint @tk_table_list = [].taint @init_ip_env = [].taint # table of Procs @add_tk_procs = [].taint # table of [name, args, body] @cb_entry_class = Class.new{|c| def initialize(ip, cmd) @ip = ip @cmd = cmd end attr_reader :ip, :cmd def call(*args) @ip.cb_eval(@cmd, *args) end } } def INTERP.tk_cmd_tbl @tk_cmd_tbl end def INTERP.tk_windows @tk_windows end def INTERP.tk_object_table(id) @tk_table_list[id] end def INTERP.create_table id = @tk_table_list.size (tbl = {}).tainted? || tbl.taint @tk_table_list << tbl obj = Object.new obj.instance_eval <<-EOD def self.method_missing(m, *args) TkCore::INTERP.tk_object_table(#{id}).send(m, *args) end EOD return obj end def INTERP.get_cb_entry(cmd) @cb_entry_class.new(__getip, cmd).freeze end def INTERP.cb_eval(cmd, *args) TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *args)) end def INTERP.init_ip_env(script = Proc.new) @init_ip_env << script script.call(self) end def INTERP.add_tk_procs(name, args = nil, body = nil) @add_tk_procs << [name, args, body] self._invoke('proc', name, args, body) if args && body end def INTERP.init_ip_internal ip = self @init_ip_env.each{|script| script.call(ip)} @add_tk_procs.each{|name,args,body| ip._invoke('proc',name,args,body)} end end INTERP.add_tk_procs('rb_out', 'args', <<-'EOL') regsub -all {!} $args {\\!} args regsub -all "{" $args "\\{" args if {[set st [catch {ruby [format "TkCore.callback %%Q!%s!" $args]} ret]] != 0} { return -code $st $ret } { return $ret } EOL EventFlag = TclTkLib::EventFlag def callback_break fail TkCallbackBreak, "Tk callback returns 'break' status" end def callback_continue fail TkCallbackContinue, "Tk callback returns 'continue' status" end def TkCore.callback(arg) # arg = tk_split_list(arg) arg = tk_split_simplelist(arg) #_get_eval_string(TkUtil.eval_cmd(Tk_CMDTBL[arg.shift], *arg)) #_get_eval_string(TkUtil.eval_cmd(TkCore::INTERP.tk_cmd_tbl[arg.shift], # *arg)) cb_obj = TkCore::INTERP.tk_cmd_tbl[arg.shift] unless $DEBUG cb_obj.call(*arg) else begin raise 'check backtrace' rescue # ignore backtrace before 'callback' pos = -($!.backtrace.size) end begin cb_obj.call(*arg) rescue trace = $!.backtrace raise $!, "\n#{trace[0]}: #{$!.message} (#{$!.class})\n" + "\tfrom #{trace[1..pos].join("\n\tfrom ")}" end end end def load_cmd_on_ip(tk_cmd) bool(tk_call('auto_load', tk_cmd)) end def after(ms, cmd=Proc.new) myid = _curr_cmd_id cmdid = install_cmd(cmd) tk_call("after",ms,cmdid) # return # if false #defined? Thread # Thread.start do # ms = Float(ms)/1000 # ms = 10 if ms == 0 # sleep ms/1000 # cmd.call # end # else # cmdid = install_cmd(cmd) # tk_call("after",ms,cmdid) # end end def after_idle(cmd=Proc.new) myid = _curr_cmd_id cmdid = install_cmd(cmd) tk_call('after','idle',cmdid) end def clock_clicks(ms=nil) if ms tk_call('clock','clicks','-milliseconds').to_i else tk_call('clock','clicks').to_i end end def clock_format(clk, form=nil) if form tk_call('clock','format',clk,'-format',form).to_i else tk_call('clock','format',clk).to_i end end def clock_formatGMT(clk, form=nil) if form tk_call('clock','format',clk,'-format',form,'-gmt','1').to_i else tk_call('clock','format',clk,'-gmt','1').to_i end end def clock_scan(str, base=nil) if base tk_call('clock','scan',str,'-base',base).to_i else tk_call('clock','scan',str).to_i end end def clock_scanGMT(str, base=nil) if base tk_call('clock','scan',str,'-base',base,'-gmt','1').to_i else tk_call('clock','scan',str,'-gmt','1').to_i end end def clock_seconds tk_call('clock','seconds').to_i end def windowingsystem tk_call('tk', 'windowingsystem') end def scaling(scale=nil) if scale tk_call('tk', 'scaling', scale) else Float(number(tk_call('tk', 'scaling'))) end end def scaling_displayof(win, scale=nil) if scale tk_call('tk', 'scaling', '-displayof', win, scale) else Float(number(tk_call('tk', '-displayof', win, 'scaling'))) end end def appname(name=None) tk_call('tk', 'appname', name) end def appsend(interp, async, *args) if async tk_call('send', '-async', '--', interp, *args) else tk_call('send', '--', interp, *args) end end def rb_appsend(interp, async, *args) args = args.collect!{|c| _get_eval_string(c).gsub(/[\[\]$"]/, '\\\\\&')} args.push(').to_s"') appsend(interp, async, 'ruby "(', *args) end def appsend_displayof(interp, win, async, *args) win = '.' if win == nil if async tk_call('send', '-async', '-displayof', win, '--', interp, *args) else tk_call('send', '-displayor', win, '--', interp, *args) end end def rb_appsend_displayof(interp, win, async, *args) args = args.collect!{|c| _get_eval_string(c).gsub(/[\[\]$"]/, '\\\\\&')} args.push(').to_s"') appsend_displayof(interp, win, async, 'ruby "(', *args) end def info(*args) tk_call('info', *args) end def mainloop(check_root = true) TclTkLib.mainloop(check_root) end def mainloop_watchdog(check_root = true) # watchdog restarts mainloop when mainloop is dead TclTkLib.mainloop_watchdog(check_root) end def do_one_event(flag = TclTkLib::EventFlag::ALL) TclTkLib.do_one_event(flag) end def set_eventloop_tick(timer_tick) TclTkLib.set_eventloop_tick(timer_tick) end def get_eventloop_tick() TclTkLib.get_eventloop_tick end def set_no_event_wait(wait) TclTkLib.set_no_even_wait(wait) end def get_no_event_wait() TclTkLib.get_no_eventloop_wait end def set_eventloop_weight(loop_max, no_event_tick) TclTkLib.set_eventloop_weight(loop_max, no_event_tick) end def get_eventloop_weight() TclTkLib.get_eventloop_weight end def restart(app_name = nil, keys = {}) TkCore::INTERP.init_ip_internal tk_call('set', 'argv0', app_name) if app_name if keys.kind_of?(Hash) # tk_call('set', 'argc', keys.size * 2) tk_call('set', 'argv', hash_kv(keys).join(' ')) end INTERP.restart nil end def event_generate(window, context, keys=nil) window = window.path if window.kind_of? TkObject if keys tk_call('event', 'generate', window, "<#{tk_event_sequence(context)}>", *hash_kv(keys)) else tk_call('event', 'generate', window, "<#{tk_event_sequence(context)}>") end end def messageBox(keys) tk_call 'tk_messageBox', *hash_kv(keys) end def getOpenFile(keys = nil) tk_call 'tk_getOpenFile', *hash_kv(keys) end def getSaveFile(keys = nil) tk_call 'tk_getSaveFile', *hash_kv(keys) end def chooseColor(keys = nil) tk_call 'tk_chooseColor', *hash_kv(keys) end def chooseDirectory(keys = nil) tk_call 'tk_chooseDirectory', *hash_kv(keys) end def ip_eval(cmd_string) res = INTERP._eval(cmd_string) if INTERP._return_value() != 0 fail RuntimeError, res, error_at end return res end def ip_invoke(*args) res = INTERP._invoke(*args) if INTERP._return_value() != 0 fail RuntimeError, res, error_at end return res end def tk_call(*args) puts args.inspect if $DEBUG args.collect! {|x|ruby2tcl(x)} args.compact! args.flatten! print "=> ", args.join(" ").inspect, "\n" if $DEBUG begin # res = INTERP._invoke(*args).taint res = INTERP._invoke(*args) # _invoke returns a TAINTED string rescue NameError => err # err = $! begin args.unshift "unknown" #res = INTERP._invoke(*args).taint res = INTERP._invoke(*args) # _invoke returns a TAINTED string rescue StandardError => err2 fail err2 unless /^invalid command/ =~ err2 fail err end end if INTERP._return_value() != 0 fail RuntimeError, res, error_at end print "==> ", res.inspect, "\n" if $DEBUG return res end end module TkPackage include TkCore extend TkPackage TkCommandNames = ['package'.freeze].freeze def add_path(path) Tk::AUTO_PATH.value = Tk::AUTO_PATH.to_a << path end def forget(package) tk_call('package', 'forget', package) nil end def names tk_split_simplelist(tk_call('package', 'names')) end def provide(package, version=nil) if version tk_call('package', 'provide', package, version) nil else tk_call('package', 'provide', package) end end def present(package, version=None) tk_call('package', 'present', package, version) end def present_exact(package, version) tk_call('package', 'present', '-exact', package, version) end def require(package, version=None) tk_call('package', 'require', package, version) end def require_exact(package, version) tk_call('package', 'require', '-exact', package, version) end def versions(package) tk_split_simplelist(tk_call('package', 'versions', package)) end def vcompare(version1, version2) Integer(tk_call('package', 'vcompare', version1, version2)) end def vsatisfies(version1, version2) bool(tk_call('package', 'vsatisfies', version1, version2)) end end module Tk include TkCore extend Tk TCL_VERSION = INTERP._invoke("info", "tclversion").freeze TCL_PATCHLEVEL = INTERP._invoke("info", "patchlevel").freeze TK_VERSION = INTERP._invoke("set", "tk_version").freeze TK_PATCHLEVEL = INTERP._invoke("set", "tk_patchLevel").freeze JAPANIZED_TK = (INTERP._invoke("info", "commands", "kanji") != "").freeze def Tk.const_missing(sym) case(sym) when :TCL_LIBRARY INTERP._invoke("set", "tcl_library").freeze when :TK_LIBRARY INTERP._invoke("set", "tk_library").freeze when :LIBRARY INTERP._invoke("info", "library").freeze #when :PKG_PATH, :PACKAGE_PATH, :TCL_PACKAGE_PATH # tk_split_simplelist(INTERP._invoke('set', 'tcl_pkgPath')) #when :LIB_PATH, :LIBRARY_PATH, :TCL_LIBRARY_PATH # tk_split_simplelist(INTERP._invoke('set', 'tcl_libPath')) when :PLATFORM, :TCL_PLATFORM Hash[*tk_split_simplelist(INTERP._invoke('array', 'get', 'tcl_platform'))] when :ENV Hash[*tk_split_simplelist(INTERP._invoke('array', 'get', 'env'))] #when :AUTO_PATH #<=== # tk_split_simplelist(INTERP._invoke('set', 'auto_path')) #when :AUTO_OLDPATH # tk_split_simplelist(INTERP._invoke('set', 'auto_oldpath')) when :AUTO_INDEX Hash[*tk_split_simplelist(INTERP._invoke('array', 'get', 'auto_index'))] when :PRIV, :PRIVATE, :TK_PRIV priv = {} if INTERP._invoke('info', 'vars', 'tk::Priv') != "" var_nam = 'tk::Priv' else var_nam = 'tkPriv' end Hash[*tk_split_simplelist(INTERP._invoke('array', 'get', var_nam))].each{|k,v| k.freeze case v when /^-?\d+$/ priv[k] = v.to_i when /^-?\d+\.?\d*(e[-+]?\d+)?$/ priv[k] = v.to_f else priv[k] = v.freeze end } priv else raise NameError, 'uninitialized constant Tk::' + sym.id2name end end def root TkRoot.new end def Tk.bell(nice = false) if nice tk_call 'bell', '-nice' else tk_call 'bell' end end def Tk.bell_on_display(win, nice = false) if nice tk_call('bell', '-displayof', win, '-nice') else tk_call('bell', '-displayof', win) end end def Tk.destroy(*wins) tk_call('destroy', *wins) end def Tk.exit tk_call('destroy', '.') end def Tk.current_grabs tk_split_list(tk_call('grab', 'current')) end def Tk.focus(display=nil) if display == nil window(tk_call('focus')) else window(tk_call('focus', '-displayof', display)) end end def Tk.focus_lastfor(win) window(tk_call('focus', '-lastfor', win)) end def Tk.focus_next(win) TkManageFocus.next(win) end def Tk.focus_prev(win) TkManageFocus.prev(win) end def Tk.strictMotif(bool=None) bool(tk_call('set', 'tk_strictMotif', bool)) end def Tk.show_kinsoku(mode='both') begin if /^8\.*/ === TK_VERSION && JAPANIZED_TK tk_split_simplelist(tk_call('kinsoku', 'show', mode)) end rescue end end def Tk.add_kinsoku(chars, mode='both') begin if /^8\.*/ === TK_VERSION && JAPANIZED_TK tk_split_simplelist(tk_call('kinsoku', 'add', mode, *(chars.split('')))) else [] end rescue [] end end def Tk.delete_kinsoku(chars, mode='both') begin if /^8\.*/ === TK_VERSION && JAPANIZED_TK tk_split_simplelist(tk_call('kinsoku', 'delete', mode, *(chars.split('')))) end rescue end end def Tk.toUTF8(str,encoding) INTERP._toUTF8(str,encoding) end def Tk.fromUTF8(str,encoding) INTERP._fromUTF8(str,encoding) end module Scrollable def xscrollcommand(cmd=Proc.new) configure_cmd 'xscrollcommand', cmd end def yscrollcommand(cmd=Proc.new) configure_cmd 'yscrollcommand', cmd end def xview(*index) v = tk_send('xview', *index) list(v) if index.size == 0 end def yview(*index) v = tk_send('yview', *index) list(v) if index.size == 0 end def xscrollbar(bar=nil) if bar @xscrollbar = bar @xscrollbar.orient 'horizontal' self.xscrollcommand {|*arg| @xscrollbar.set(*arg)} @xscrollbar.command {|*arg| self.xview(*arg)} end @xscrollbar end def yscrollbar(bar=nil) if bar @yscrollbar = bar @yscrollbar.orient 'vertical' self.yscrollcommand {|*arg| @yscrollbar.set(*arg)} @yscrollbar.command {|*arg| self.yview(*arg)} end @yscrollbar end end module Wm include TkComm TkCommandNames = ['wm'.freeze].freeze def aspect(*args) w = tk_call('wm', 'aspect', path, *args) if args.length == 0 list(w) else self end end def attributes(slot=nil,value=None) if slot == nil lst = tk_split_list(tk_call('wm', 'attributes', path)) info = {} while key = lst.shift info[key[1..-1]] = lst.shift end info elsif slot.kind_of? Hash tk_call('wm', 'attributes', path, *hash_kv(slot)) self elsif value == None tk_call('wm', 'attributes', path, "-#{slot}") else tk_call('wm', 'attributes', path, "-#{slot}", value) self end end def client(name=None) if name == None tk_call 'wm', 'client', path else name = '' if name == nil tk_call 'wm', 'client', path, name self end end def colormapwindows(*args) r = tk_call('wm', 'colormapwindows', path, *args) if args.size == 0 list(r) else self end end def wm_command(value=nil) if value tk_call('wm', 'command', path, value) self else procedure(tk_call('wm', 'command', path)) end end def deiconify(ex = true) tk_call('wm', 'deiconify', path) if ex self end def focusmodel(mode = nil) if mode tk_call 'wm', 'focusmodel', path, mode self else tk_call 'wm', 'focusmodel', path end end def frame tk_call('wm', 'frame', path) end def geometry(geom=nil) if geom tk_call('wm', 'geometry', path, geom) self else tk_call('wm', 'geometry', path) end end def grid(*args) w = tk_call('wm', 'grid', path, *args) if args.size == 0 list(w) else self end end def group(*args) w = tk_call('wm', 'group', path, *args) if args.size == 0 window(w) else self end end def iconbitmap(bmp=nil) if bmp tk_call 'wm', 'iconbitmap', path, bmp self else tk_call 'wm', 'iconbitmap', path end end def iconify(ex = true) tk_call('wm', 'iconify', path) if ex self end def iconmask(bmp=nil) if bmp tk_call 'wm', 'iconmask', path, bmp self else tk_call 'wm', 'iconmask', path end end def iconname(name=nil) if name tk_call 'wm', 'iconname', path, name self else tk_call 'wm', 'iconname', path end end def iconposition(*args) w = tk_call('wm', 'iconposition', path, *args) if args.size == 0 list(w) else self end end def iconwindow(*args) w = tk_call('wm', 'iconwindow', path, *args) if args.size == 0 window(w) else self end end def maxsize(*args) w = tk_call('wm', 'maxsize', path, *args) if args.size == 0 list(w) else self end end def minsize(*args) w = tk_call('wm', 'minsize', path, *args) if args.size == 0 list(w) else self end end def overrideredirect(bool=None) if bool == None bool(tk_call('wm', 'overrideredirect', path)) else tk_call 'wm', 'overrideredirect', path, bool self end end def positionfrom(who=None) if who == None r = tk_call('wm', 'positionfrom', path) (r == "")? nil: r else tk_call('wm', 'positionfrom', path, who) self end end def protocol(name=nil, cmd=nil) if cmd tk_call('wm', 'protocol', path, name, cmd) self elsif name result = tk_call('wm', 'protocol', path, name) (result == "")? nil : tk_tcl2ruby(result) else tk_split_simplelist(tk_call('wm', 'protocol', path)) end end def resizable(*args) w = tk_call('wm', 'resizable', path, *args) if args.length == 0 list(w).collect{|e| bool(e)} else self end end def sizefrom(who=None) if who == None r = tk_call('wm', 'sizefrom', path) (r == "")? nil: r else tk_call('wm', 'sizefrom', path, who) self end end def stackorder list(tk_call('wm', 'stackorder', path)) end def stackorder_isabove(win) bool(tk_call('wm', 'stackorder', path, 'isabove', win)) end def stackorder_isbelow(win) bool(tk_call('wm', 'stackorder', path, 'isbelow', win)) end def state(state=nil) if state tk_call 'wm', 'state', path, state self else tk_call 'wm', 'state', path end end def title(str=nil) if str tk_call('wm', 'title', path, str) self else tk_call('wm', 'title', path) end end def transient(master=nil) if master tk_call('wm', 'transient', path, master) self else window(tk_call('wm', 'transient', path, master)) end end def withdraw(ex = true) tk_call('wm', 'withdraw', path) if ex self end end end ########################################### # string with Tcl's encoding ########################################### module Tk class EncodedString < String @@enc_buf = '__rb_encoding_buffer__' def self.tk_escape(str) s = '"' + str.gsub(/[\[\]$"]/, '\\\\\&') + '"' TkCore::INTERP.__eval(Kernel.format('global %s; set %s %s', @@enc_buf, @@enc_buf, s)) end def self.new(str, enc = Tk.encoding_system) obj = super(self.tk_escape(str)) obj.instance_eval{@enc = enc} obj end def self.new_without_escape(str, enc = Tk.encoding_system) obj = super(str) obj.instance_eval{@enc = enc} obj end def encoding @enc end end def Tk.EncodedString(str, enc = Tk.encoding_system) Tk::EncodedString.new(str, enc) end class UTF8_String < EncodedString def self.new(str) super(str, 'utf-8') end def self.new_without_escape(str) super(str, 'utf-8') end end def Tk.UTF8_String(str) Tk::UTF8_String.new(str) end end ########################################### # convert kanji string to/from utf-8 ########################################### if /^8\.[1-9]/ =~ Tk::TCL_VERSION && !Tk::JAPANIZED_TK class TclTkIp # from tkencoding.rb by ttate@jaist.ac.jp alias __eval _eval alias __invoke _invoke attr_accessor :encoding def _eval(cmd) if defined? @encoding if cmd.kind_of?(Tk::EncodedString) _fromUTF8(__eval(_toUTF8(cmd, cmd.encoding)), @encoding) else _fromUTF8(__eval(_toUTF8(cmd, @encoding)), @encoding) end else __eval(cmd) end end def _invoke(*cmds) if defined? @encoding cmds = cmds.collect{|cmd| if cmd.kind_of?(Tk::EncodedString) _toUTF8(cmd, cmd.encoding) else _toUTF8(cmd, @encoding) end } _fromUTF8(__invoke(*cmds), @encoding) else __invoke(*cmds) end end end module Tk module Encoding extend Encoding TkCommandNames = ['encoding'.freeze].freeze def encoding=(name) TkCore::INTERP.encoding = name end def encoding TkCore::INTERP.encoding end def encoding_names tk_split_simplelist(tk_call('encoding', 'names')) end def encoding_system tk_call('encoding', 'system') end def encoding_system=(enc) tk_call('encoding', 'system', enc) end def encoding_convertfrom(str, enc=None) TkCore::INTERP.__invoke('encoding', 'convertfrom', enc, str) end alias encoding_convert_from encoding_convertfrom def encoding_convertto(str, enc=None) TkCore::INTERP.__invoke('encoding', 'convertto', enc, str) end alias encoding_convert_to encoding_convertto end extend Encoding end # estimate encoding case $KCODE when /^e/i # EUC Tk.encoding = 'euc-jp' when /^s/i # SJIS Tk.encoding = 'shiftjis' when /^u/i # UTF8 Tk.encoding = 'utf-8' else # NONE begin Tk.encoding = Tk.encoding_system rescue StandardError, NameError Tk.encoding = 'utf-8' end end else # dummy methods class TclTkIp alias __eval _eval alias __invoke _invoke end module Tk module Encoding extend Encoding def encoding=(name) nil end def encoding nil end def encoding_names nil end def encoding_system nil end def encoding_system=(enc) nil end def encoding_convertfrom(str, enc=None) str end alias encoding_convert_from encoding_convertfrom def encoding_convertto(str, enc=None) str end alias encoding_convert_to encoding_convertto end extend Encoding end end module TkBindCore def bind(context, cmd=Proc.new, args=nil) Tk.bind(self, context, cmd, args) end def bind_append(context, cmd=Proc.new, args=nil) Tk.bind_append(self, context, cmd, args) end def bind_remove(context) Tk.bind_remove(self, context) end def bindinfo(context=nil) Tk.bindinfo(self, context) end end class TkBindTag include TkBindCore #BTagID_TBL = {} BTagID_TBL = TkCore::INTERP.create_table Tk_BINDTAG_ID = ["btag".freeze, "00000".taint].freeze TkCore::INTERP.init_ip_env{ BTagID_TBL.clear } def TkBindTag.id2obj(id) BTagID_TBL[id]? BTagID_TBL[id]: id end def TkBindTag.new_by_name(name, *args, &b) return BTagID_TBL[name] if BTagID_TBL[name] self.new(*args, &b).instance_eval{ BTagID_TBL.delete @id @id = name BTagID_TBL[@id] = self } end def initialize(*args, &b) @id = Tk_BINDTAG_ID.join Tk_BINDTAG_ID[1].succ! BTagID_TBL[@id] = self bind(*args, &b) if args != [] end ALL = self.new_by_name('all') def name @id end def to_eval @id end def inspect Kernel.format "#", @id end end class TkBindTagAll", @id end end class TkVariable include Tk extend TkCore include Comparable #TkCommandNames = ['tkwait'.freeze].freeze TkCommandNames = ['vwait'.freeze].freeze #TkVar_CB_TBL = {} #TkVar_ID_TBL = {} TkVar_CB_TBL = TkCore::INTERP.create_table TkVar_ID_TBL = TkCore::INTERP.create_table Tk_VARIABLE_ID = ["v".freeze, "00000".taint].freeze TkCore::INTERP.add_tk_procs('rb_var', 'args', "ruby [format \"TkVariable.callback %%Q!%s!\" $args]") def TkVariable.callback(args) name1,name2,op = tk_split_list(args) if TkVar_CB_TBL[name1] _get_eval_string(TkVar_CB_TBL[name1].trace_callback(name2,op)) else '' end end def initialize(val="") @id = Tk_VARIABLE_ID.join Tk_VARIABLE_ID[1].succ! TkVar_ID_TBL[@id] = self @trace_var = nil @trace_elem = nil @trace_opts = nil =begin if val == [] # INTERP._eval(format('global %s; set %s(0) 0; unset %s(0)', # @id, @id, @id)) elsif val.kind_of?(Array) a = [] # val.each_with_index{|e,i| a.push(i); a.push(array2tk_list(e))} # s = '"' + a.join(" ").gsub(/[\[\]$"]/, '\\\\\&') + '"' val.each_with_index{|e,i| a.push(i); a.push(e)} s = '"' + array2tk_list(a).gsub(/[\[\]$"]/, '\\\\\&') + '"' INTERP._eval(format('global %s; array set %s %s', @id, @id, s)) elsif val.kind_of?(Hash) s = '"' + val.to_a.collect{|e| array2tk_list(e)}.join(" ")\ .gsub(/[\[\]$"]/, '\\\\\&') + '"' INTERP._eval(format('global %s; array set %s %s', @id, @id, s)) else s = '"' + _get_eval_string(val).gsub(/[\[\]$"]/, '\\\\\&') + '"' INTERP._eval(format('global %s; set %s %s', @id, @id, s)) end =end if val.kind_of?(Hash) s = '"' + val.to_a.collect{|e| array2tk_list(e)}.join(" ")\ .gsub(/[\[\]$"]/, '\\\\\&') + '"' INTERP._eval(Kernel.format('global %s; array set %s %s', @id, @id, s)) else s = '"' + _get_eval_string(val).gsub(/[\[\]$"]/, '\\\\\&') + '"' INTERP._eval(Kernel.format('global %s; set %s %s', @id, @id, s)) end end def wait(on_thread = false, check_root = false) if $SAFE >= 4 fail SecurityError, "can't wait variable at $SAFE >= 4" end if on_thread if check_root INTERP._thread_tkwait('variable', @id) else INTERP._thread_vwait(@id) end else if check_root INTERP._invoke('tkwait', 'variable', @id) else INTERP._invoke('vwait', @id) end end end def eventloop_wait(check_root = false) wait(false, check_root) end def thread_wait(check_root = false) wait(true, check_root) end def tkwait(on_thread = true) wait(on_thread, true) end def eventloop_tkwait wait(false, true) end def thread_tkwait wait(true, true) end def id @id end def value begin INTERP._eval(Kernel.format('global %s; set %s', @id, @id)) rescue if INTERP._eval(Kernel.format('global %s; array exists %s', @id, @id)) != "1" fail else Hash[*tk_split_simplelist(INTERP._eval(Kernel.format('global %s; array get %s', @id, @id)))] end end end def value=(val) begin s = '"' + _get_eval_string(val).gsub(/[\[\]$"]/, '\\\\\&') + '"' INTERP._eval(Kernel.format('global %s; set %s %s', @id, @id, s)) rescue if INTERP._eval(Kernel.format('global %s; array exists %s', @id, @id)) != "1" fail else if val == [] INTERP._eval(Kernel.format('global %s; unset %s; set %s(0) 0; unset %s(0)', @id, @id, @id, @id)) elsif val.kind_of?(Array) a = [] val.each_with_index{|e,i| a.push(i); a.push(array2tk_list(e))} s = '"' + a.join(" ").gsub(/[\[\]$"]/, '\\\\\&') + '"' INTERP._eval(Kernel.format('global %s; unset %s; array set %s %s', @id, @id, @id, s)) elsif val.kind_of?(Hash) s = '"' + val.to_a.collect{|e| array2tk_list(e)}.join(" ")\ .gsub(/[\[\]$"]/, '\\\\\&') + '"' INTERP._eval(Kernel.format('global %s; unset %s; array set %s %s', @id, @id, @id, s)) else fail end end end end def [](index) INTERP._eval(Kernel.format('global %s; set %s(%s)', @id, @id, _get_eval_string(index))) end def []=(index,val) INTERP._eval(Kernel.format('global %s; set %s(%s) %s', @id, @id, _get_eval_string(index), _get_eval_string(val))) end def numeric number(value) end def numeric=(val) case val when Numeric self.value=(val) when TkVariable self.value=(val.numeric) else raise ArgumentError, "Numeric is expected" end end def to_i number(value).to_i end def to_f number(value).to_f end def to_s string(value).to_s end def to_sym value.intern end def list tk_split_list(value) end alias to_a list def list=(val) case val when Array self.value=(val) when TkVariable self.value=(val.list) else raise ArgumentError, "Array is expected" end end def inspect Kernel.format "#", @id end def coerce(other) case other when TkVariable [other.value, self.value] when String [other, self.to_s] when Symbol [other, self.to_sym] when Integer [other, self.to_i] when Float [other, self.to_f] when Array [other, self.to_a] else [other, self.value] end end def &(other) if other.kind_of?(Array) self.to_a & other.to_a else self.to_i & other.to_i end end def |(other) if other.kind_of?(Array) self.to_a | other.to_a else self.to_i | other.to_i end end def +(other) case other when Array self.to_a + other when String self.value + other else begin number(self.value) + other rescue self.value + other.to_s end end end def -(other) if other.kind_of?(Array) self.to_a - other else number(self.value) - other end end def *(other) begin number(self.value) * other rescue self.value * other end end def /(other) number(self.value) / other end def %(other) begin number(self.value) % other rescue self.value % other end end def **(other) number(self.value) ** other end def =~(other) self.value =~ other end def ==(other) case other when TkVariable self.equal?(other) when String self.to_s == other when Symbol self.to_sym == other when Integer self.to_i == other when Float self.to_f == other when Array self.to_a == other when Hash self.value == other else false end end def zero? numeric.zero? end def nonzero? !(numeric.zero?) end def <=>(other) if other.kind_of?(TkVariable) begin val = other.numeric other = val rescue other = other.value end end if other.kind_of?(Numeric) begin return self.numeric <=> other rescue return self.value <=> other.to_s end else return self.value <=> other end end def to_eval @id end def unset(elem=nil) if elem INTERP._eval(Kernel.format('global %s; unset %s(%s)', @id, @id, tk_tcl2ruby(elem))) else INTERP._eval(Kernel.format('global %s; unset %s', @id, @id)) end end alias remove unset def trace_callback(elem, op) if @trace_var.kind_of? Array @trace_var.each{|m,e| e.call(self,elem,op) if m.index(op)} end if elem.kind_of? String if @trace_elem[elem].kind_of? Array @trace_elem[elem].each{|m,e| e.call(self,elem,op) if m.index(op)} end end end def trace(opts, cmd) @trace_var = [] if @trace_var == nil opts = ['r','w','u'].find_all{|c| opts.index(c)}.join('') @trace_var.unshift([opts,cmd]) if @trace_opts == nil TkVar_CB_TBL[@id] = self @trace_opts = opts Tk.tk_call('trace', 'variable', @id, @trace_opts, 'rb_var') else newopts = @trace_opts.dup opts.each_byte{|c| newopts += c.chr unless newopts.index(c)} if newopts != @trace_opts Tk.tk_call('trace', 'vdelete', @id, @trace_opts, 'rb_var') @trace_opts.replace(newopts) Tk.tk_call('trace', 'variable', @id, @trace_opts, 'rb_var') end end end def trace_element(elem, opts, cmd) @trace_elem = {} if @trace_elem == nil @trace_elem[elem] = [] if @trace_elem[elem] == nil opts = ['r','w','u'].find_all{|c| opts.index(c)}.join('') @trace_elem[elem].unshift([opts,cmd]) if @trace_opts == nil TkVar_CB_TBL[@id] = self @trace_opts = opts Tk.tk_call('trace', 'variable', @id, @trace_opts, 'rb_var') else newopts = @trace_opts.dup opts.each_byte{|c| newopts += c.chr unless newopts.index(c)} if newopts != @trace_opts Tk.tk_call('trace', 'vdelete', @id, @trace_opts, 'rb_var') @trace_opts.replace(newopts) Tk.tk_call('trace', 'variable', @id, @trace_opts, 'rb_var') end end end def trace_vinfo return [] unless @trace_var @trace_var.dup end def trace_vinfo_for_element(elem) return [] unless @trace_elem return [] unless @trace_elem[elem] @trace_elem[elem].dup end def trace_vdelete(opts,cmd) return unless @trace_var.kind_of? Array opts = ['r','w','u'].find_all{|c| opts.index(c)}.join('') idx = -1 newopts = '' @trace_var.each_with_index{|e,i| if idx < 0 && e[0] == opts && e[1] == cmd idx = i next end e[0].each_byte{|c| newopts += c.chr unless newopts.index(c)} } if idx >= 0 @trace_var.delete_at(idx) else return end @trace_elem.each{|elem| @trace_elem[elem].each{|e| e[0].each_byte{|c| newopts += c.chr unless newopts.index(c)} } } newopts = ['r','w','u'].find_all{|c| newopts.index(c)}.join('') if newopts != @trace_opts Tk.tk_call('trace', 'vdelete', @id, @trace_opts, 'rb_var') @trace_opts.replace(newopts) if @trace_opts != '' Tk.tk_call('trace', 'variable', @id, @trace_opts, 'rb_var') end end end def trace_vdelete_for_element(elem,opts,cmd) return unless @trace_elem.kind_of? Hash return unless @trace_elem[elem].kind_of? Array opts = ['r','w','u'].find_all{|c| opts.index(c)}.join('') idx = -1 @trace_elem[elem].each_with_index{|e,i| if idx < 0 && e[0] == opts && e[1] == cmd idx = i next end } if idx >= 0 @trace_elem[elem].delete_at(idx) else return end newopts = '' @trace_var.each{|e| e[0].each_byte{|c| newopts += c.chr unless newopts.index(c)} } @trace_elem.each{|elem| @trace_elem[elem].each{|e| e[0].each_byte{|c| newopts += c.chr unless newopts.index(c)} } } newopts = ['r','w','u'].find_all{|c| newopts.index(c)}.join('') if newopts != @trace_opts Tk.tk_call('trace', 'vdelete', @id, @trace_opts, 'rb_var') @trace_opts.replace(newopts) if @trace_opts != '' Tk.tk_call('trace', 'variable', @id, @trace_opts, 'rb_var') end end end end class TkVarAccess= Tk8.4a2 ? if slot conf = tk_split_list(tk_call('place', 'configure', win.epath, "-#{slot}") ) conf[0] = conf[0][1..-1] conf else tk_split_simplelist(tk_call('place', 'configure', win.epath)).collect{|conflist| conf = tk_split_simplelist(conflist) conf[0] = conf[0][1..-1] conf } end end def forget(win) tk_call 'place', 'forget', win end def info(win) ilist = list(tk_call('place', 'info', win.epath)) info = {} while key = ilist.shift info[key[1..-1]] = ilist.shift end return info end def slaves(master) list(tk_call('place', 'slaves', master.epath)) end module_function :configure, :configinfo, :forget, :info, :slaves end module TkOptionDB include Tk extend Tk TkCommandNames = ['option'.freeze].freeze module Priority WidgetDefault = 20 StartupFile = 40 UserDefault = 60 Interactive = 80 end def add(pat, value, pri=None) if $SAFE >= 4 fail SecurityError, "can't call 'TkOptionDB.add' at $SAFE >= 4" end tk_call 'option', 'add', pat, value, pri end def clear if $SAFE >= 4 fail SecurityError, "can't call 'TkOptionDB.crear' at $SAFE >= 4" end tk_call 'option', 'clear' end def get(win, name, klass) tk_call('option', 'get', win ,name, klass) end def readfile(file, pri=None) tk_call 'option', 'readfile', file, pri end module_function :add, :clear, :get, :readfile def read_entries(file, f_enc=nil) if TkCore::INTERP.safe? fail SecurityError, "can't call 'TkOptionDB.read_entries' on a safe interpreter" end i_enc = Tk.encoding() unless f_enc f_enc = i_enc end ent = [] cline = '' open(file, 'r') {|f| while line = f.gets cline += line.chomp! case cline when /\\$/ # continue cline.chop! next when /^!/ # coment cline = '' next when /^([^:]+):\s(.*)$/ pat = $1 val = $2 p "ResourceDB: #{[pat, val].inspect}" if $DEBUG pat = TkCore::INTERP._toUTF8(pat, f_enc) pat = TkCore::INTERP._fromUTF8(pat, i_enc) val = TkCore::INTERP._toUTF8(val, f_enc) val = TkCore::INTERP._fromUTF8(val, i_enc) ent << [pat, val] cline = '' else # unknown --> ignore cline = '' next end end } ent end module_function :read_entries def read_with_encoding(file, f_enc=nil, pri=None) # try to read the file as an OptionDB file readfile(file, pri).each{|pat, val| add(pat, val, pri) } =begin i_enc = Tk.encoding() unless f_enc f_enc = i_enc end cline = '' open(file, 'r') {|f| while line = f.gets cline += line.chomp! case cline when /\\$/ # continue cline.chop! next when /^!/ # coment cline = '' next when /^([^:]+):\s(.*)$/ pat = $1 val = $2 p "ResourceDB: #{[pat, val].inspect}" if $DEBUG pat = TkCore::INTERP._toUTF8(pat, f_enc) pat = TkCore::INTERP._fromUTF8(pat, i_enc) val = TkCore::INTERP._toUTF8(val, f_enc) val = TkCore::INTERP._fromUTF8(val, i_enc) add(pat, val, pri) cline = '' else # unknown --> ignore cline = '' next end end } =end end module_function :read_with_encoding # support procs on the resource database @@resource_proc_class = Class.new class << @@resource_proc_class private :new CARRIER = '.'.freeze METHOD_TBL = TkCore::INTERP.create_table ADD_METHOD = false SAFE_MODE = 4 def __closed_block_check__(str) depth = 0 str.scan(/[{}]/){|x| if x == "{" depth += 1 elsif x == "}" depth -= 1 end if depth <= 0 && !($' =~ /\A\s*\Z/) fail RuntimeError, "bad string for procedure : #{str.inspect}" end } str end def __check_proc_string__(str) # If you want to check the proc_string, do it in this method. # Please define this in the block given to 'new_proc_class' method. str end def method_missing(id, *args) res_proc = self::METHOD_TBL[id] unless res_proc.kind_of? Proc if id == :new || !(self::METHOD_TBL.has_key?(id) || self::ADD_METHOD) raise NoMethodError, "not support resource-proc '#{id.id2name}' for #{self.name}" end proc_str = TkOptionDB.get(self::CARRIER, id.id2name, '').strip proc_str = '{' + proc_str + '}' unless /\A\{.*\}\Z/ =~ proc_str proc_str = __closed_block_check__(proc_str) proc_str = __check_proc_string__(proc_str) res_proc = eval('Proc.new' + proc_str) self::METHOD_TBL[id] = res_proc end proc{ $SAFE = self::SAFE_MODE res_proc.call(*args) }.call end private :__closed_block_check__, :__check_proc_string__, :method_missing end @@resource_proc_class.freeze def __create_new_class(klass, func, safe = 4, add = false, parent = nil) klass = klass.to_s if klass.kind_of? Symbol unless (?A..?Z) === klass[0] fail ArgumentError, "bad string '#{klass}' for class name" end unless func.kind_of? Array fail ArgumentError, "method-list must be Array" end func_str = func.join(' ') if parent == nil install_win(parent) elsif parent <= @@resource_proc_class install_win(parent::CARRIER) else fail ArgumentError, "parent must be Resource-Proc class" end carrier = Tk.tk_call('frame', @path, '-class', klass) body = <<-"EOD" class #{klass} < TkOptionDB.module_eval('@@resource_proc_class') CARRIER = '#{carrier}'.freeze METHOD_TBL = TkCore::INTERP.create_table ADD_METHOD = #{add} SAFE_MODE = #{safe} %w(#{func_str}).each{|f| METHOD_TBL[f.intern] = nil } end EOD if parent.kind_of?(Class) && parent <= @@resource_proc_class parent.class_eval(body) eval(parent.name + '::' + klass) else eval(body) eval('TkOptionDB::' + klass) end end module_function :__create_new_class private_class_method :__create_new_class def __remove_methods_of_proc_class(klass) # for security, make these methods invalid class << klass attr_reader :class_eval, :name, :superclass, :ancestors, :const_defined?, :const_get, :const_set, :constants, :included_modules, :instance_methods, :method_defined?, :module_eval, :private_instance_methods, :protected_instance_methods, :public_instance_methods, :remove_const, :remove_method, :undef_method, :to_s, :inspect, :display, :method, :methods, :instance_eval, :instance_variables, :kind_of?, :is_a?, :private_methods, :protected_methods, :public_methods end end module_function :__remove_methods_of_proc_class private_class_method :__remove_methods_of_proc_class RAND_BASE_CNT = [0] RAND_BASE_HEAD = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' RAND_BASE_CHAR = RAND_BASE_HEAD + 'abcdefghijklmnopqrstuvwxyz0123456789_' def __get_random_basename name = '%s%03d' % [RAND_BASE_HEAD[rand(RAND_BASE_HEAD.size),1], RAND_BASE_CNT[0]] len = RAND_BASE_CHAR.size (6+rand(10)).times{ name << RAND_BASE_CHAR[rand(len),1] } RAND_BASE_CNT[0] = RAND_BASE_CNT[0] + 1 name end module_function :__get_random_basename private_class_method :__get_random_basename # define new proc class : # If you want to modify the new class or create a new subclass, # you must do such operation in the block parameter. # Because the created class is flozen after evaluating the block. def new_proc_class(klass, func, safe = 4, add = false, parent = nil, &b) new_klass = __create_new_class(klass, func, safe, add, parent) new_klass.class_eval(&b) if block_given? __remove_methods_of_proc_class(new_klass) new_klass.freeze new_klass end module_function :new_proc_class def eval_under_random_base(parent = nil, &b) new_klass = __create_new_class(__get_random_basename(), [], 4, false, parent) ret = new_klass.class_eval(&b) if block_given? __remove_methods_of_proc_class(new_klass) new_klass.freeze ret end module_function :eval_under_random_base def new_proc_class_random(klass, func, safe = 4, add = false, &b) eval_under_random_base(){ TkOption.new_proc_class(klass, func, safe, add, self, &b) } end module_function :new_proc_class_random end TkOption = TkOptionDB TkResourceDB = TkOptionDB module TkTreatFont def font_configinfo(name = nil) ret = TkFont.used_on(self.path) if ret == nil =begin if name ret = name else ret = TkFont.init_widget_font(self.path, self.path, 'configure') end =end ret = TkFont.init_widget_font(self.path, self.path, 'configure') end ret end alias fontobj font_configinfo def font_configure(slot) slot = _symbolkey2str(slot) if slot.key?('font') fnt = slot.delete('font') if fnt.kind_of? TkFont return fnt.call_font_configure(self.path, self.path,'configure',slot) else if fnt if (slot.key?('kanjifont') || slot.key?('latinfont') || slot.key?('asciifont')) fnt = TkFont.new(fnt) lfnt = slot.delete('latinfont') lfnt = slot.delete('asciifont') if slot.key?('asciifont') kfnt = slot.delete('kanjifont') fnt.latin_replace(lfnt) if lfnt fnt.kanji_replace(kfnt) if kfnt else slot['font'] = fnt tk_call(self.path, 'configure', *hash_kv(slot)) end end return self end end lfnt = slot.delete('latinfont') lfnt = slot.delete('asciifont') if slot.key?('asciifont') kfnt = slot.delete('kanjifont') if lfnt && kfnt return TkFont.new(lfnt, kfnt).call_font_configure(self.path, self.path, 'configure', slot) end latinfont_configure(lfnt) if lfnt kanjifont_configure(kfnt) if kfnt tk_call(self.path, 'configure', *hash_kv(slot)) if slot != {} self end def latinfont_configure(ltn, keys=nil) if (fobj = TkFont.used_on(self.path)) fobj = TkFont.new(fobj) # create a new TkFont object elsif Tk::JAPANIZED_TK fobj = fontobj # create a new TkFont object else tk_call(self.path, 'configure', '-font', ltn) return self end if fobj.kind_of?(TkFont) if ltn.kind_of? TkFont conf = {} ltn.latin_configinfo.each{|key,val| conf[key] = val} if keys fobj.latin_configure(conf.update(keys)) else fobj.latin_configure(conf) end else fobj.latin_replace(ltn) end end return fobj.call_font_configure(self.path, self.path, 'configure', {}) end alias asciifont_configure latinfont_configure def kanjifont_configure(knj, keys=nil) if (fobj = TkFont.used_on(self.path)) fobj = TkFont.new(fobj) # create a new TkFont object elsif Tk::JAPANIZED_TK fobj = fontobj # create a new TkFont object else tk_call(self.path, 'configure', '-font', knj) return self end if fobj.kind_of?(TkFont) if knj.kind_of? TkFont conf = {} knj.kanji_configinfo.each{|key,val| conf[key] = val} if keys fobj.kanji_configure(conf.update(keys)) else fobj.kanji_configure(conf) end else fobj.kanji_replace(knj) end end return fobj.call_font_configure(self.path, self.path, 'configure', {}) end def font_copy(window, tag=nil) if tag fnt = window.tagfontobj(tag).dup else fnt = window.fontobj.dup end fnt.call_font_configure(self.path, self.path, 'configure', {}) self end def latinfont_copy(window, tag=nil) fontobj.dup.call_font_configure(self.path, self.path, 'configure', {}) if tag fontobj.latin_replace(window.tagfontobj(tag).latin_font_id) else fontobj.latin_replace(window.fontobj.latin_font_id) end self end alias asciifont_copy latinfont_copy def kanjifont_copy(window, tag=nil) fontobj.dup.call_font_configure(self.path, self.path, 'configure', {}) if tag fontobj.kanji_replace(window.tagfontobj(tag).kanji_font_id) else fontobj.kanji_replace(window.fontobj.kanji_font_id) end self end end module TkTreatItemFont def __conf_cmd(idx) raise NotImplementedError, "need to define `__conf_cmd'" end def __item_pathname(tagOrId) raise NotImplementedError, "need to define `__item_pathname'" end private :__conf_cmd, :__item_pathname def tagfont_configinfo(tagOrId, name = nil) pathname = __item_pathname(tagOrId) ret = TkFont.used_on(pathname) if ret == nil =begin if name ret = name else ret = TkFont.init_widget_font(pathname, self.path, __conf_cmd(0), __conf_cmd(1), tagOrId) end =end ret = TkFont.init_widget_font(pathname, self.path, __conf_cmd(0), __conf_cmd(1), tagOrId) end ret end alias tagfontobj tagfont_configinfo def tagfont_configure(tagOrId, slot) pathname = __item_pathname(tagOrId) slot = _symbolkey2str(slot) if slot.key?('font') fnt = slot.delete('font') if fnt.kind_of? TkFont return fnt.call_font_configure(pathname, self.path, __conf_cmd(0), __conf_cmd(1), tagOrId, slot) else if fnt if (slot.key?('kanjifont') || slot.key?('latinfont') || slot.key?('asciifont')) fnt = TkFont.new(fnt) lfnt = slot.delete('latinfont') lfnt = slot.delete('asciifont') if slot.key?('asciifont') kfnt = slot.delete('kanjifont') fnt.latin_replace(lfnt) if lfnt fnt.kanji_replace(kfnt) if kfnt end slot['font'] = fnt tk_call(self.path, __conf_cmd(0), __conf_cmd(1), tagOrId, *hash_kv(slot)) end return self end end lfnt = slot.delete('latinfont') lfnt = slot.delete('asciifont') if slot.key?('asciifont') kfnt = slot.delete('kanjifont') if lfnt && kfnt return TkFont.new(lfnt, kfnt).call_font_configure(pathname, self.path, __conf_cmd(0), __conf_cmd(1), tagOrId, slot) end latintagfont_configure(tagOrId, lfnt) if lfnt kanjitagfont_configure(tagOrId, kfnt) if kfnt tk_call(self.path, __conf_cmd(0), __conf_cmd(1), tagOrId, *hash_kv(slot)) if slot != {} self end def latintagfont_configure(tagOrId, ltn, keys=nil) pathname = __item_pathname(tagOrId) if (fobj = TkFont.used_on(pathname)) fobj = TkFont.new(fobj) # create a new TkFont object elsif Tk::JAPANIZED_TK fobj = tagfontobj(tagOrId) # create a new TkFont object else tk_call(self.path, __conf_cmd(0), __conf_cmd(1), tagOrId, '-font', ltn) return self end if fobj.kind_of?(TkFont) if ltn.kind_of? TkFont conf = {} ltn.latin_configinfo.each{|key,val| conf[key] = val} if keys fobj.latin_configure(conf.update(keys)) else fobj.latin_configure(conf) end else fobj.latin_replace(ltn) end end return fobj.call_font_configure(pathname, self.path, __conf_cmd(0), __conf_cmd(1), tagOrId, {}) end alias asciitagfont_configure latintagfont_configure def kanjitagfont_configure(tagOrId, knj, keys=nil) pathname = __item_pathname(tagOrId) if (fobj = TkFont.used_on(pathname)) fobj = TkFont.new(fobj) # create a new TkFont object elsif Tk::JAPANIZED_TK fobj = tagfontobj(tagOrId) # create a new TkFont object else tk_call(self.path, __conf_cmd(0), __conf_cmd(1), tagOrId, '-font', knj) return self end if fobj.kind_of?(TkFont) if knj.kind_of? TkFont conf = {} knj.kanji_configinfo.each{|key,val| conf[key] = val} if keys fobj.kanji_configure(conf.update(keys)) else fobj.kanji_configure(conf) end else fobj.kanji_replace(knj) end end return fobj.call_font_configure(pathname, self.path, __conf_cmd(0), __conf_cmd(1), tagOrId, {}) end def tagfont_copy(tagOrId, window, wintag=nil) pathname = __item_pathname(tagOrId) if wintag fnt = window.tagfontobj(wintag).dup else fnt = window.fontobj.dup end fnt.call_font_configure(pathname, self.path, __conf_cmd(0), __conf_cmd(1), tagOrId, {}) return self end def latintagfont_copy(tagOrId, window, wintag=nil) pathname = __item_pathname(tagOrId) tagfontobj(tagOrId).dup.call_font_configure(pathname, self.path, __conf_cmd(0), __conf_cmd(1), tagOrId, {}) if wintag tagfontobj(tagOrId). latin_replace(window.tagfontobj(wintag).latin_font_id) else tagfontobj(tagOrId).latin_replace(window.fontobj.latin_font_id) end self end alias asciitagfont_copy latintagfont_copy def kanjitagfont_copy(tagOrId, window, wintag=nil) pathname = __item_pathname(tagOrId) tagfontobj(tagOrId).dup.call_font_configure(pathname, self.path, __conf_cmd(0), __conf_cmd(1), tagOrId, {}) if wintag tagfontobj(tagOrId). kanji_replace(window.tagfontobj(wintag).kanji_font_id) else tagfontobj(tagOrId).kanji_replace(window.fontobj.kanji_font_id) end self end end class TkObject 0 tk_call path, 'configure', *hash_kv(slot) end else if (slot == 'font' || slot == :font || slot == 'kanjifont' || slot == :kanjifont || slot == 'latinfont' || slot == :latinfont || slot == 'asciifont' || slot == :asciifont ) if value == None fontobj else font_configure({slot=>value}) end else tk_call path, 'configure', "-#{slot}", value end end self end def configure_cmd(slot, value) configure slot, install_cmd(value) end def configinfo(slot = nil) if slot == 'font' || slot == :font || slot == 'kanjifont' || slot == :kanjifont conf = tk_split_simplelist(tk_send('configure', "-#{slot}") ) conf[0] = conf[0][1..-1] conf[4] = fontobj(conf[4]) conf else if slot case slot.to_s when 'text', 'label', 'show', 'data', 'file' conf = tk_split_simplelist(tk_send('configure', "-#{slot}") ) else conf = tk_split_list(tk_send('configure', "-#{slot}") ) end conf[0] = conf[0][1..-1] conf else ret = tk_split_simplelist(tk_send('configure') ).collect{|conflist| conf = tk_split_simplelist(conflist) conf[0] = conf[0][1..-1] case conf[0] when 'text', 'label', 'show', 'data', 'file' else if conf[3] if conf[3].index('{') conf[3] = tk_split_list(conf[3]) else conf[3] = tk_tcl2ruby(conf[3]) end end if conf[4] if conf[4].index('{') conf[4] = tk_split_list(conf[4]) else conf[4] = tk_tcl2ruby(conf[4]) end end end conf } fontconf = ret.assoc('font') if fontconf ret.delete_if{|item| item[0] == 'font' || item[0] == 'kanjifont'} fontconf[4] = fontobj(fontconf[4]) ret.push(fontconf) else ret end end end end def event_generate(context, keys=nil) if keys tk_call('event', 'generate', path, "<#{tk_event_sequence(context)}>", *hash_kv(keys)) else tk_call('event', 'generate', path, "<#{tk_event_sequence(context)}>") end end def tk_trace_variable(v) unless v.kind_of?(TkVariable) fail(ArgumentError, Kernel.format("type error (%s); must be TkVariable object", v.class)) end v end private :tk_trace_variable def destroy # tk_call 'trace', 'vdelete', @tk_vn, 'w', @var_id if @var_id end end class TkWindowtarget} end tk_call 'pack', epath, *hash_kv(keys) self end def unpack tk_call 'pack', 'forget', epath self end alias pack_forget unpack def pack_config(slot, value=None) if slot.kind_of? Hash tk_call 'pack', 'configure', epath, *hash_kv(slot) else tk_call 'pack', 'configure', epath, "-#{slot}", value end end def pack_info() ilist = list(tk_call('pack', 'info', epath)) info = {} while key = ilist.shift info[key[1..-1]] = ilist.shift end return info end def pack_propagate(mode=None) if mode == None tk_call('pack', 'propagate', epath, mode) else bool(tk_call('pack', 'propagate', epath)) end end def pack_slaves() list(tk_call('pack', 'slaves', epath)) end def grid(keys = nil) tk_call 'grid', epath, *hash_kv(keys) self end def grid_in(target, keys = nil) if keys keys = keys.dup keys['in'] = target else keys = {'in'=>target} end tk_call 'grid', epath, *hash_kv(keys) self end def ungrid tk_call 'grid', 'forget', epath self end alias grid_forget ungrid def grid_bbox(*args) list(tk_call('grid', 'bbox', epath, *args)) end def grid_config(slot, value=None) if slot.kind_of? Hash tk_call 'grid', 'configure', epath, *hash_kv(slot) else tk_call 'grid', 'configure', epath, "-#{slot}", value end end def grid_columnconfig(index, keys) tk_call('grid', 'columnconfigure', epath, index, *hash_kv(keys)) end def grid_rowconfig(index, keys) tk_call('grid', 'rowconfigure', epath, index, *hash_kv(keys)) end def grid_columnconfiginfo(index, slot=nil) if slot tk_call('grid', 'columnconfigure', epath, index, "-#{slot}").to_i else ilist = list(tk_call('grid', 'columnconfigure', epath, index)) info = {} while key = ilist.shift info[key[1..-1]] = ilist.shift end info end end def grid_rowconfiginfo(index, slot=nil) if slot tk_call('grid', 'rowconfigure', epath, index, "-#{slot}").to_i else ilist = list(tk_call('grid', 'rowconfigure', epath, index)) info = {} while key = ilist.shift info[key[1..-1]] = ilist.shift end info end end def grid_info() list(tk_call('grid', 'info', epath)) end def grid_location(x, y) list(tk_call('grid', 'location', epath, x, y)) end def grid_propagate(mode=None) if mode == None tk_call('grid', 'propagate', epath, mode) else bool(tk_call('grid', 'propagate', epath)) end end def grid_remove() tk_call 'grid', 'remove', epath end def grid_size() tk_call 'grid', 'size', epath end def grid_slaves(args) list(tk_call('grid', 'slaves', epath, *hash_kv(args))) end def place(keys = nil) tk_call 'place', epath, *hash_kv(keys) self end def place_in(target, keys = nil) if keys keys = keys.dup keys['in'] = target else keys = {'in'=>target} end tk_call 'place', epath, *hash_kv(keys) self end def unplace tk_call 'place', 'forget', epath self end alias place_forget unplace def place_config(slot, value=None) if slot.kind_of? Hash tk_call 'place', 'configure', epath, *hash_kv(slot) else tk_call 'place', 'configure', epath, "-#{slot}", value end end def place_configinfo(slot = nil) # for >= Tk8.4a2 ? if slot conf = tk_split_list(tk_call('place', 'configure', epath, "-#{slot}") ) conf[0] = conf[0][1..-1] conf else tk_split_simplelist(tk_call('place', 'configure', epath)).collect{|conflist| conf = tk_split_simplelist(conflist) conf[0] = conf[0][1..-1] conf } end end def place_info() ilist = list(tk_call('place', 'info', epath)) info = {} while key = ilist.shift info[key[1..-1]] = ilist.shift end return info end def place_slaves() list(tk_call('place', 'slaves', epath)) end def focus(force=false) if force tk_call 'focus', '-force', path else tk_call 'focus', path end self end def grab(*args) if !args or args.length == 0 tk_call 'grab', 'set', path self elsif args.length == 1 case args[0] when 'global', :global #return(tk_call('grab', 'set', '-global', path)) tk_call('grab', 'set', '-global', path) return self when 'release', :release #return tk_call('grab', 'release', path) tk_call('grab', 'release', path) return self else val = tk_call('grab', args[0], path) end case args[0] when 'current', :current return window(val) when 'status', :status return val end self else fail ArgumentError, 'wrong # of args' end end def grab_current grab('current') end def grab_release grab('release') end def grab_set grab('set') end def grab_set_global grab('global') end def grab_status grab('status') end def lower(below=None) tk_call 'lower', epath, below self end def raise(above=None) tk_call 'raise', epath, above self end def command(cmd=Proc.new) configure_cmd 'command', cmd end def colormodel model=None tk_call 'tk', 'colormodel', path, model self end def caret(keys=nil) TkXIM.caret(path, keys) end def destroy super children = [] rexp = /^#{self.path}\.[^.]+$/ TkCore::INTERP.tk_windows.each{|path, obj| children << [path, obj] if path =~ rexp } if defined?(@cmdtbl) for id in @cmdtbl uninstall_cmd id end end children.each{|path, obj| if defined?(@cmdtbl) for id in @cmdtbl uninstall_cmd id end end TkCore::INTERP.tk_windows.delete(path) } begin tk_call 'destroy', epath rescue end uninstall_win end def wait_visibility(on_thread = true) if $SAFE >= 4 fail SecurityError, "can't wait visibility at $SAFE >= 4" end if on_thread INTERP._thread_tkwait('visibility', path) else INTERP._invoke('tkwait', 'visibility', path) end end def eventloop_wait_visibility wait_visibility(false) end def thread_wait_visibility wait_visibility(true) end alias wait wait_visibility alias tkwait wait_visibility alias eventloop_wait eventloop_wait_visibility alias eventloop_tkwait eventloop_wait_visibility alias eventloop_tkwait_visibility eventloop_wait_visibility alias thread_wait thread_wait_visibility alias thread_tkwait thread_wait_visibility alias thread_tkwait_visibility thread_wait_visibility def wait_destroy(on_thread = true) if $SAFE >= 4 fail SecurityError, "can't wait destroy at $SAFE >= 4" end if on_thread INTERP._thread_tkwait('window', epath) else INTERP._invoke('tkwait', 'window', epath) end end def eventloop_wait_destroy wait_destroy(false) end def thread_wait_destroy wait_destroy(true) end alias tkwait_destroy wait_destroy alias eventloop_tkwait_destroy eventloop_wait_destroy alias thread_tkwait_destroy thread_wait_destroy def bindtags(taglist=nil) if taglist fail ArgumentError, "taglist must be Array" unless taglist.kind_of? Array tk_call('bindtags', path, taglist) taglist else list(tk_call('bindtags', path)).collect{|tag| if tag.kind_of?(String) if cls = WidgetClassNames[tag] cls elsif btag = TkBindTag.id2obj(tag) btag else tag end else tag end } end end def bindtags=(taglist) bindtags(taglist) end def bindtags_shift taglist = bindtags tag = taglist.shift bindtags(taglist) tag end def bindtags_unshift(tag) bindtags(bindtags().unshift(tag)) end end class TkRoottrue, :widgetname=>'.') if keys # wm commands keys.each{|k,v| if v.kind_of? Array new.send(k,*v) else new.send(k,v) end } end ROOT[0] = new Tk_WINDOWS["."] = new end =end def TkRoot.new(keys=nil, &b) unless TkCore::INTERP.tk_windows['.'] TkCore::INTERP.tk_windows['.'] = super(:without_creating=>true, :widgetname=>'.') end root = TkCore::INTERP.tk_windows['.'] if keys # wm commands keys.each{|k,v| if v.kind_of? Array root.send(k,*v) else root.send(k,v) end } end root.instance_eval(&b) if block_given? root end WidgetClassName = 'Tk'.freeze WidgetClassNames[WidgetClassName] = self def create_self @path = '.' end def path "." end def TkRoot.destroy TkCore::INTERP._invoke('destroy', '.') end end class TkToplevelparent} end end if keys.key?('classname') keys['class'] = keys.delete('classname') end @classname = keys['class'] @colormap = keys['colormap'] @container = keys['container'] @visual = keys['visual'] if !@classname && my_class_name keys['class'] = @classname = my_class_name end if @classname.kind_of? TkBindTag @db_class = @classname @classname = @classname.id elsif @classname @db_class = TkDatabaseClass.new(@classname) else @db_class = self.class @classname = @db_class::WidgetClassName end super(keys) end def create_self(keys) if keys and keys != None tk_call 'frame', @path, *hash_kv(keys) else tk_call 'frame', @path end end def database_classname @classname end def self.database_class if self == WidgetClassNames[WidgetClassName] || self.name == '' self else TkDatabaseClass.new(self.name) end end def self.database_classname self.database_class.name end def self.bind(*args) if self == WidgetClassNames[WidgetClassName] || self.name == '' super(*args) else TkDatabaseClass.new(self.name).bind(*args) end end def self.bind_append(*args) if self == WidgetClassNames[WidgetClassName] || self.name == '' super(*args) else TkDatabaseClass.new(self.name).bind_append(*args) end end def self.bind_remove(*args) if self == WidgetClassNames[WidgetClassName] || self.name == '' super(*args) else TkDatabaseClass.new(self.name).bind_remove(*args) end end def self.bindinfo(*args) if self == WidgetClassNames[WidgetClassName] || self.name == '' super(*args) else TkDatabaseClass.new(self.name).bindinfo(*args) end end end class TkLabelFramevalue) end def configure(slot, value=None) if (slot == 'command' || slot == :command) configure('command'=>value) elsif slot.kind_of?(Hash) && (slot.key?('command') || slot.key?(:command)) slot = _symbolkey2str(slot) slot['command'] = _wrap_command_arg(slot.delete('command')) end super(slot, value) end def command(cmd=Proc.new) configure('command'=>cmd) end def get(x=None, y=None) number(tk_send('get', x, y)) end def coords(val=None) tk_split_list(tk_send('coords', val)) end def identify(x, y) tk_send('identify', x, y) end def set(val) tk_send("set", val) end def value get end def value= (val) set(val) end end class TkScrollbarval}) end else tk_call 'itemconfigure', index, "-#{key}", val end end self end def itemconfiginfo(index, key=nil) if key case key.to_s when 'text', 'label', 'show' conf = tk_split_simplelist(tk_send('itemconfigure',index,"-#{key}")) when 'font', 'kanjifont' conf = tk_split_simplelist(tk_send('itemconfigure',index,"-#{key}") ) conf[4] = tagfont_configinfo(index, conf[4]) else conf = tk_split_list(tk_send('itemconfigure',index,"-#{key}")) end conf[0] = conf[0][1..-1] conf else ret = tk_split_simplelist(tk_send('itemconfigure', index)).collect{|conflist| conf = tk_split_simplelist(conflist) conf[0] = conf[0][1..-1] case conf[0] when 'text', 'label', 'show' else if conf[3] if conf[3].index('{') conf[3] = tk_split_list(conf[3]) else conf[3] = tk_tcl2ruby(conf[3]) end end if conf[4] if conf[4].index('{') conf[4] = tk_split_list(conf[4]) else conf[4] = tk_tcl2ruby(conf[4]) end end end conf } fontconf = ret.assoc('font') if fontconf ret.delete_if{|item| item[0] == 'font' || item[0] == 'kanjifont'} fontconf[4] = tagfont_configinfo(index, fontconf[4]) ret.push(fontconf) else ret end end end end module TkTreatMenuEntryFont include TkTreatItemFont ItemCMD = ['entryconfigure'.freeze, TkComm::None].freeze def __conf_cmd(idx) ItemCMD[idx] end def __item_pathname(tagOrId) self.path + ';' + tagOrId.to_s end end class TkMenuval}) end else tk_call 'entryconfigure', index, "-#{key}", val end end self end def entryconfiginfo(index, key=nil) if key case key.to_s when 'text', 'label', 'show' conf = tk_split_simplelist(tk_send('entryconfigure',index,"-#{key}")) when 'font', 'kanjifont' conf = tk_split_simplelist(tk_send('entryconfigure',index,"-#{key}")) conf[4] = tagfont_configinfo(index, conf[4]) else conf = tk_split_list(tk_send('entryconfigure',index,"-#{key}")) end conf[0] = conf[0][1..-1] conf else ret = tk_split_simplelist(tk_send('entryconfigure', index)).collect{|conflist| conf = tk_split_simplelist(conflist) conf[0] = conf[0][1..-1] case conf[0] when 'text', 'label', 'show' else if conf[3] if conf[3].index('{') conf[3] = tk_split_list(conf[3]) else conf[3] = tk_tcl2ruby(conf[3]) end end if conf[4] if conf[4].index('{') conf[4] = tk_split_list(conf[4]) else conf[4] = tk_tcl2ruby(conf[4]) end end end conf } if fontconf ret.delete_if{|item| item[0] == 'font' || item[0] == 'kanjifont'} fontconf[4] = tagfont_configinfo(index, fontconf[4]) ret.push(fontconf) else ret end end end end class TkMenuClone return value of tk_optionMenu @path = path #TkComm::Tk_WINDOWS[@path] = self TkCore::INTERP.tk_windows[@path] = self end end def initialize(parent=nil, var=TkVariable.new, firstval=nil, *vals) if parent.kind_of? Hash keys = _symbolkey2str(parent) parent = keys['parent'] var = keys['variable'] if keys['variable'] firstval, *vals = keys['values'] end fail 'variable option must be TkVariable' unless var.kind_of? TkVariable @variable = var firstval = @variable.value unless firstval @variable.value = firstval install_win(if parent then parent.path end) @menu = OptionMenu.new(tk_call('tk_optionMenu', @path, @variable.id, firstval, *vals)) end def value @variable.value end def activate(index) @menu.activate(index) self end def add(value) @menu.add('radiobutton', 'variable'=>@variable, 'label'=>value, 'value'=>value) self end def index(index) @menu.index(index) end def invoke(index) @menu.invoke(index) end def insert(index, value) @menu.add(index, 'radiobutton', 'variable'=>@variable, 'label'=>value, 'value'=>value) self end def delete(index, last=None) @menu.delete(index, last) self end def yposition(index) @menu.yposition(index) end def menu @menu end def menucget(key) @menu.cget(key) end def menuconfigure(key, val=None) @menu.configure(key, val) self end def menuconfiginfo(key=nil) @menu.configinfo(key) end def entrycget(index, key) @menu.entrycget(index, key) end def entryconfigure(index, key, val=None) @menu.entryconfigure(index, key, val) self end def entryconfiginfo(index, key=nil) @menu.entryconfiginfo(index, key) end end module TkComposite include Tk extend Tk def initialize(parent=nil, *args) @delegates = {} if parent.kind_of? Hash keys = _symbolkey2str(parent) parent = keys.delete('parent') @frame = TkFrame.new(parent) @delegates['DEFAULT'] = @frame @path = @epath = @frame.path initialize_composite(keys) else @frame = TkFrame.new(parent) @delegates['DEFAULT'] = @frame @path = @epath = @frame.path initialize_composite(*args) end end def epath @epath end def initialize_composite(*args) end private :initialize_composite def delegate(option, *wins) if @delegates[option].kind_of?(Array) for i in wins @delegates[option].push(i) end else @delegates[option] = wins end end def configure(slot, value=None) if slot.kind_of? Hash slot.each{|slot,value| configure slot, value} else if @delegates and @delegates[slot] for i in @delegates[slot] if not i i = @delegates['DEFALUT'] redo else last = i.configure(slot, value) end end last else super end end end end module TkClipboard include Tk extend Tk TkCommandNames = ['clipboard'.freeze].freeze def self.clear(win=nil) if win tk_call 'clipboard', 'clear', '-displayof', win else tk_call 'clipboard', 'clear' end end def self.clear_on_display(win) tk_call 'clipboard', 'clear', '-displayof', win end def self.get(type=nil) if type tk_call 'clipboard', 'get', '-type', type else tk_call 'clipboard', 'get' end end def self.get_on_display(win, type=nil) if type tk_call 'clipboard', 'get', '-displayof', win, '-type', type else tk_call 'clipboard', 'get', '-displayof', win end end def self.set(data, keys=nil) clear append(data, keys) end def self.set_on_display(win, data, keys=nil) clear(win) append_on_display(win, data, keys) end def self.append(data, keys=nil) args = ['clipboard', 'append'] args += hash_kv(keys) args += ['--', data] tk_call(*args) end def self.append_on_display(win, data, keys=nil) args = ['clipboard', 'append', '-displayof', win] args += hash_kv(keys) args += ['--', data] tk_call(*args) end def clear TkClipboard.clear_on_display(self) self end def get(type=nil) TkClipboard.get_on_display(self, type) end def set(data, keys=nil) TkClipboard.set_on_display(self, data, keys) self end def append(data, keys=nil) TkClipboard.append_on_display(self, data, keys) self end end # widget_destroy_hook require 'tkvirtevent' TkBindTag::ALL.bind(TkVirtualEvent.new('Destroy'), proc{|xpath| path = xpath[1..-1] if (widget = TkCore::INTERP.tk_windows[path]) if widget.respond_to?(:__destroy_hook__) begin widget.__destroy_hook__ rescue Exception end end end }, 'x%W') # freeze core modules #TclTkLib.freeze #TclTkIp.freeze #TkUtil.freeze #TkKernel.freeze #TkComm.freeze #TkComm::Event.freeze #TkCore.freeze #Tk.freeze # autoload autoload :TkCanvas, 'tkcanvas' autoload :TkImage, 'tkcanvas' autoload :TkBitmapImage, 'tkcanvas' autoload :TkPhotoImage, 'tkcanvas' autoload :TkEntry, 'tkentry' autoload :TkSpinbox, 'tkentry' autoload :TkText, 'tktext' autoload :TkDialog, 'tkdialog' autoload :TkDialog2, 'tkdialog' autoload :TkWarning, 'tkdialog' autoload :TkWarning2, 'tkdialog' autoload :TkMenubar, 'tkmenubar' autoload :TkAfter, 'tkafter' autoload :TkTimer, 'tkafter' autoload :TkPalette, 'tkpalette' autoload :TkFont, 'tkfont' autoload :TkBgError, 'tkbgerror' autoload :TkManageFocus, 'tkmngfocus' autoload :TkPalette, 'tkpalette' autoload :TkWinDDE, 'tkwinpkg' autoload :TkWinRegistry, 'tkwinpkg' autoload :TkMacResource, 'tkmacpkg' autoload :TkConsole, 'tkconsole'