ruby--ruby/ext/tk/sample/demos-jp/rmt

269 lines
6.8 KiB
Ruby

#!/usr/bin/env ruby
# rmt --
# This script implements a simple remote-control mechanism for
# Tk applications. It allows you to select an application and
# then type commands to that application.
require 'tk'
class Rmt
def initialize(parent=nil)
win = self
unless parent
parent = TkRoot.new
end
root = TkWinfo.toplevel(parent)
root.minsize(1,1)
# The instance variable below keeps track of the remote application
# that we're sending to. If it's an empty string then we execute
# the commands locally.
@app = 'local'
@mode = 'Ruby'
# The instance variable below keeps track of whether we're in the
# middle of executing a command entered via the text.
@executing = 0
# The instance variable below keeps track of the last command executed,
# so it can be re-executed in response to !! commands.
@lastCommand = ""
# Create menu bar. Arrange to recreate all the information in the
# applications sub-menu whenever it is cascaded to.
TkFrame.new(root, 'relief'=>'raised', 'bd'=>2) {|f|
pack('side'=>'top', 'fill'=>'x')
TkMenubutton.new(f, 'text'=>'File', 'underline'=>0) {|mb|
TkMenu.new(mb) {|mf|
mb.menu(mf)
TkMenu.new(mf) {|ma|
postcommand proc{win.fillAppsMenu ma}
mf.add('cascade', 'label'=>'Select Application',
'menu'=>ma, 'underline'=>0)
}
add('command', 'label'=>'Quit',
'command'=>proc{root.destroy}, 'underline'=>0)
}
pack('side'=>'left')
}
}
# Create text window and scrollbar.
@txt = TkText.new(root, 'relief'=>'sunken', 'bd'=>2, 'setgrid'=>true) {
yscrollbar(TkScrollbar.new(root){pack('side'=>'right', 'fill'=>'y')})
pack('side'=>'left')
}
@promptEnd = TkTextMark.new(@txt, 'insert')
# Create a binding to forward commands to the target application,
# plus modify many of the built-in bindings so that only information
# in the current command can be deleted (can still set the cursor
# earlier in the text and select and insert; just can't delete).
@txt.bindtags([@txt, TkText, root, 'all'])
@txt.bind('Return', proc{
@txt.set_insert('end - 1c')
@txt.insert('insert', "\n")
win.invoke
Tk.callback_break
})
@txt.bind('Delete', proc{
begin
@txt.tag_remove('sel', 'sel.first', @promptEnd)
rescue
end
if @txt.tag_nextrange('sel', '1.0', 'end') == []
if @txt.compare('insert', '<', @promptEnd)
Tk.callback_break
end
end
})
@txt.bind('BackSpace', proc{
begin
@txt.tag_remove('sel', 'sel.first', @promptEnd)
rescue
end
if @txt.tag_nextrange('sel', '1.0', 'end') == []
if @txt.compare('insert', '<', @promptEnd)
Tk.callback_break
end
end
})
@txt.bind('Control-d', proc{
if @txt.compare('insert', '<', @promptEnd)
Tk.callback_break
end
})
@txt.bind('Control-k', proc{
if @txt.compare('insert', '<', @promptEnd)
@txt.set_insert(@promptEnd)
end
})
@txt.bind('Control-t', proc{
if @txt.compare('insert', '<', @promptEnd)
Tk.callback_break
end
})
@txt.bind('Meta-d', proc{
if @txt.compare('insert', '<', @promptEnd)
Tk.callback_break
end
})
@txt.bind('Meta-BackSpace', proc{
if @txt.compare('insert', '<=', @promptEnd)
Tk.callback_break
end
})
@txt.bind('Control-h', proc{
if @txt.compare('insert', '<=', @promptEnd)
Tk.callback_break
end
})
@txt.tag_configure('bold', 'font'=>['Courier', 12, 'bold'])
@app = Tk.appname('rmt')
if (@app =~ /^rmt(.*)$/)
root.title("Tk Remote Controller#{$1}")
root.iconname("Tk Remote#{$1}")
end
prompt
@txt.focus
#@app = TkWinfo.appname(TkRoot.new)
end
def tkTextInsert(w,s)
return if s == ""
begin
if w.compare('sel.first','<=','insert') \
&& w.compare('sel.last','>=','insert')
w.tag_remove('sel', 'sel.first', @promptEnd)
w.delete('sel.first', 'sel.last')
end
rescue
end
w.insert('insert', s)
w.see('insert')
end
# The method below is used to print out a prompt at the
# insertion point (which should be at the beginning of a line
# right now).
def prompt
@txt.insert('insert', "#{@app}: ")
@promptEnd.set('insert')
@promptEnd.gravity = 'left'
@txt.tag_add('bold', "#{@promptEnd.path} linestart", @promptEnd)
end
# The method below executes a command (it takes everything on the
# current line after the prompt and either sends it to the remote
# application or executes it locally, depending on "app".
def invoke
cmd = @txt.get(@promptEnd, 'insert')
@executing += 1
case (@mode)
when 'Tcl'
if Tk.info('complete', cmd)
if (cmd == "!!\n")
cmd = @lastCommand
else
@lastCommand = cmd
end
begin
msg = Tk.appsend(@app, false, cmd)
rescue
msg = "Error: #{$!}"
end
@txt.insert('insert', msg + "\n") if msg != ""
prompt
@promptEnd.set('insert')
end
when 'Ruby'
if (cmd == "!!\n")
cmd = @lastCommand
end
complete = true
begin
eval("proc{#{cmd}}")
rescue
complete = false
end
if complete
@lastCommand = cmd
begin
# msg = Tk.appsend(@app, false,
# 'ruby',
# '"(' + cmd.gsub(/[][$"]/, '\\\\\&') + ').to_s"')
msg = Tk.rb_appsend(@app, false, cmd)
rescue
msg = "Error: #{$!}"
end
@txt.insert('insert', msg + "\n") if msg != ""
prompt
@promptEnd.set('insert')
end
end
@executing -= 1
@txt.yview_pickplace('insert')
end
# The following method is invoked to change the application that
# we're talking to. It also updates the prompt for the current
# command, unless we're in the middle of executing a command from
# the text item (in which case a new prompt is about to be output
# so there's no need to change the old one).
def newApp(appName, mode)
@app = appName
@mode = mode
if @executing == 0
@promptEnd.gravity = 'right'
@txt.delete("#{@promptEnd.path} linestart", @promptEnd)
@txt.insert(@promptEnd, "#{appName}: ")
@txt.tag_add('bold', "#{@promptEnd.path} linestart", @promptEnd)
@promptEnd.gravity = 'left'
end
end
# The method below will fill in the applications sub-menu with a list
# of all the applications that currently exist.
def fillAppsMenu(menu)
win = self
begin
menu.delete(0,'last')
rescue
end
TkWinfo.interps.sort.each{|ip|
begin
if Tk.appsend(ip, false, 'info commands ruby') == ""
mode = 'Tcl'
else
mode = 'Ruby'
end
menu.add('command', 'label'=>format("%s (#{mode}/Tk)", ip),
'command'=>proc{win.newApp ip, mode})
rescue
menu.add('command', 'label'=>format("%s (unknown Tk)", ip),
'command'=>proc{win.newApp ip, mode}, 'state'=>'disabled')
end
}
menu.add('command', 'label'=>format("local (Ruby/Tk)"),
'command'=>proc{win.newApp 'local', 'Ruby'})
end
end
Rmt.new
Tk.mainloop