green_shoes 0.255.0 → 0.263.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +4 -0
- data/VERSION +1 -1
- data/lib/green_shoes.rb +6 -0
- data/lib/plugins/httpd.rb +232 -0
- data/lib/plugins/systray.rb +91 -0
- data/lib/plugins/thread.rb +63 -0
- data/lib/plugins/treeview.rb +66 -0
- data/lib/shoes/app.rb +2 -0
- data/lib/shoes/basic.rb +1 -0
- data/lib/shoes/help.rb +1 -1
- data/lib/shoes/slot.rb +9 -0
- data/samples/face-crying.png +0 -0
- data/samples/face-smile-big.png +0 -0
- data/samples/sample53.rb +13 -3
- data/samples/sample54.rb +227 -0
- data/snapshots/sample53.png +0 -0
- data/snapshots/sample54.png +0 -0
- data/static/manual-en.txt +2 -10
- metadata +12 -5
- data/samples/treeview.rb +0 -60
data/README.md
CHANGED
|
@@ -69,11 +69,15 @@ Except:
|
|
|
69
69
|
- lib/ext/hpricot/(all) (c) 2008 why the lucky stiff
|
|
70
70
|
- lib/ext/projector/(all).rb (c) 2010 MIZUTANI Tociyuki
|
|
71
71
|
- lib/ext/highlighter/(all) (c) 2008 why the lucky stiff and 2011 Steve Klabnik
|
|
72
|
+
- lib/plugins/(httpd.rb, systray.rb, thread.rb) (c) 2011 Regis d'Aubarede
|
|
72
73
|
- samples/akatsukiface.png (c) 2010 MIZUTANI Tociyuki
|
|
73
74
|
- samples/class-book.yaml (c) 2008 why the lucky stiff
|
|
74
75
|
- samples/splash-hand.png (c) 2008 why the lucky stiff
|
|
75
76
|
- samples/loogink.png (c) 2008 Anita Kuno
|
|
76
77
|
- samples/cy.png (c) 2008 Anita Kuno
|
|
78
|
+
- samples/sample54.rb (c) 2011 Regis d'Aubarede
|
|
79
|
+
- samples/face-crying.png (c) 2011 Regis d'Aubarede
|
|
80
|
+
- samples/face-smile-big.png (c) 2011 Regis d'Aubarede
|
|
77
81
|
- static/Coolvetica.ttf (c) 1999 Ray Larabie
|
|
78
82
|
- static/Lacuna.ttf (c) 2003 Glashaus, designed by Peter Hoffman
|
|
79
83
|
- static/gshoes-icon.png (c) 2010 Zachary Scott
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.263.0
|
data/lib/green_shoes.rb
CHANGED
|
@@ -29,6 +29,7 @@ module Shoes
|
|
|
29
29
|
SPAN_FORM = {emphasis: :style, family: :font_family, weight: :weight, rise: :rise,
|
|
30
30
|
strikethrough: :strikethrough, strikecolor: :strikethrough_color, underline: :underline, undercolor: :underline_color}
|
|
31
31
|
WRAP = {word: Pango::WRAP_WORD, char: Pango::WRAP_CHAR, trim: Pango::ELLIPSIZE_END}
|
|
32
|
+
FONT_SIZE = {"xx-small" => 0.57, "x-small" => 0.64, "small" => 0.83, "medium" => 1.0, "large" => 1.2, "x-large" => 1.43, "xx-large" => 1.73}
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
class Object
|
|
@@ -52,6 +53,11 @@ require_relative 'shoes/projector'
|
|
|
52
53
|
require_relative 'shoes/download'
|
|
53
54
|
require_relative 'shoes/manual'
|
|
54
55
|
|
|
56
|
+
require_relative 'plugins/systray'
|
|
57
|
+
require_relative 'plugins/thread'
|
|
58
|
+
require_relative 'plugins/httpd'
|
|
59
|
+
require_relative 'plugins/treeview'
|
|
60
|
+
|
|
55
61
|
autoload :ChipMunk, File.join(Shoes::DIR, 'ext/chipmunk')
|
|
56
62
|
autoload :Bloops, File.join(Shoes::DIR, 'ext/bloops')
|
|
57
63
|
autoload :Projector, File.join(Shoes::DIR, 'ext/projector')
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
require 'socket'
|
|
2
|
+
require 'thread'
|
|
3
|
+
require 'timeout'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
#################### Tiny embeded webserver
|
|
7
|
+
class WebserverAbstract
|
|
8
|
+
def logg(*args) @cb_log && @cb_log.call(@name,*args) end
|
|
9
|
+
def info(txt) ; logg("nw>i>",txt) ; end
|
|
10
|
+
def error(txt) ; logg("nw>e>",txt) ; end
|
|
11
|
+
def unescape(string) ; string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2}))/n) { [$1.delete('%')].pack('H*') } ; end
|
|
12
|
+
def escape(string) ; string.gsub(/([^ \/a-zA-Z0-9_.-]+)/) { '%' + $1.unpack('H2' * $1.size).join('%').upcase }.tr(' ', '+'); end
|
|
13
|
+
def hescape(string) ; escape(string.gsub("/./","/").gsub("//","/")) ; end
|
|
14
|
+
def observe(sleeping,delta)
|
|
15
|
+
@tho=Thread.new do loop do
|
|
16
|
+
sleep(sleeping)
|
|
17
|
+
nowDelta=Time.now-delta
|
|
18
|
+
l=@th.select { |th,tm| (tm[0]<nowDelta) }
|
|
19
|
+
l.each { |th,tm| info("killing thread") ; th.kill; @th.delete(th) ; tm[1].close rescue nil }
|
|
20
|
+
end ; end
|
|
21
|
+
end
|
|
22
|
+
def initialize(port,root,name,cadence,timeout,options)
|
|
23
|
+
@cb_log= options["logg"]
|
|
24
|
+
@last_mtime=File.mtime(__FILE__)
|
|
25
|
+
@port=port
|
|
26
|
+
@root=root
|
|
27
|
+
@name=name
|
|
28
|
+
@rootd=root[-1,1]=="/" ? root : root+"/"
|
|
29
|
+
@timeout=timeout
|
|
30
|
+
@th={}
|
|
31
|
+
@cb={}
|
|
32
|
+
@redirect={}
|
|
33
|
+
@server = TCPServer.new('0.0.0.0', @port)
|
|
34
|
+
@server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
|
35
|
+
info(" serveur http #{port} on #{@rootd} ready!")
|
|
36
|
+
observe(cadence,timeout*2)
|
|
37
|
+
@thm=Thread.new {
|
|
38
|
+
loop { begin
|
|
39
|
+
while (session = @server.accept)
|
|
40
|
+
run(session)
|
|
41
|
+
end
|
|
42
|
+
rescue
|
|
43
|
+
error($!.to_s)
|
|
44
|
+
session.close raise nil
|
|
45
|
+
end
|
|
46
|
+
sleep(10); info("restart accept")
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
def run(session)
|
|
51
|
+
if ! File.exists?(@root)
|
|
52
|
+
sendError(session,500,txt="root directory unknown: #{@root}")
|
|
53
|
+
else
|
|
54
|
+
Thread.new(session) do |sess|
|
|
55
|
+
@th[Thread.current]=[Time.now,sess]
|
|
56
|
+
request(sess)
|
|
57
|
+
@th.delete(Thread.current)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
def serve(uri,&blk)
|
|
62
|
+
@cb[uri] = blk
|
|
63
|
+
end
|
|
64
|
+
def request(session)
|
|
65
|
+
request = session.gets
|
|
66
|
+
uri = (request.split(/\s+/)+['','',''])[1]
|
|
67
|
+
#info uri
|
|
68
|
+
service,param,*bidon=(uri+"?").split(/\?/)
|
|
69
|
+
params=Hash[*(param.split(/#/)[0].split(/[=&]/))] rescue {}
|
|
70
|
+
params.each { |k,v| params[k]=unescape(v) }
|
|
71
|
+
uri=unescape(service)[1..-1].gsub(/\.\./,"")
|
|
72
|
+
userpass=nil
|
|
73
|
+
if (buri=uri.split(/@/)).size>1
|
|
74
|
+
uri=buri[1..-1].join("@")
|
|
75
|
+
userpass=buri[0].split(/:/)
|
|
76
|
+
end
|
|
77
|
+
do_service(session,request,uri,userpass,params)
|
|
78
|
+
rescue
|
|
79
|
+
error("Error Web get on #{request}: \n #{$!.to_s} \n #{$!.backtrace.join("\n ")}" ) rescue nil
|
|
80
|
+
session.write "HTTP/1.1 501 NOK\r\nContent-type: text/html\r\n\r\n<html><head><title>WS</title></head><body>Error : #{$!}" rescue nil
|
|
81
|
+
ensure
|
|
82
|
+
session.close rescue nil
|
|
83
|
+
end
|
|
84
|
+
def redirect(o,d)
|
|
85
|
+
@redirect[o]=d
|
|
86
|
+
end
|
|
87
|
+
def do_service(session,request,service,user_passwd,params)
|
|
88
|
+
logg(session.peeraddr.last,request)
|
|
89
|
+
redir=@redirect["/"+service]
|
|
90
|
+
service=redir.gsub(/^\//,"") if @redirect[redir]
|
|
91
|
+
aservice=to_absolute(service)
|
|
92
|
+
if redir && ! @redirect[redir]
|
|
93
|
+
do_service(session,request,redir.gsub(/^\//,""),user_passwd,params)
|
|
94
|
+
elsif @cb["/"+service]
|
|
95
|
+
begin
|
|
96
|
+
code,type,data= @cb["/"+service].call(params)
|
|
97
|
+
if code==0 && data != '/'+service
|
|
98
|
+
do_service(session,request,data[1..-1],user_passwd,params)
|
|
99
|
+
else
|
|
100
|
+
code==200 ? sendData(session,type,data) : sendError(session,code,data)
|
|
101
|
+
end
|
|
102
|
+
rescue
|
|
103
|
+
logg session.peeraddr.last,"Error in get /#{service} : #{$!}"
|
|
104
|
+
sendError(session,501,$!.to_s)
|
|
105
|
+
end
|
|
106
|
+
elsif service =~ /^stop/
|
|
107
|
+
sendData(session,".html","Stopping...");
|
|
108
|
+
Thread.new() { sleep(0.1); stop_browser() }
|
|
109
|
+
elsif File.directory?(aservice)
|
|
110
|
+
sendData(session,".html",makeIndex(aservice))
|
|
111
|
+
elsif File.exists?(aservice)
|
|
112
|
+
sendFile(session,aservice)
|
|
113
|
+
else
|
|
114
|
+
info("unknown request serv=#{service} params=#{params.inspect} #{File.exists?(service)}")
|
|
115
|
+
sendError(session,500);
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
def stop_browser
|
|
119
|
+
info "exit on web demand !"
|
|
120
|
+
@serveur.close rescue nil
|
|
121
|
+
[@tho,@thm].each { |th| th.kill }
|
|
122
|
+
end
|
|
123
|
+
def makeIndex(adir)
|
|
124
|
+
dir=to_relative(adir)
|
|
125
|
+
dirs,files=Dir.glob(adir==@rootd ? "#{@rootd}*" : "#{adir}/*").sort.partition { |f| File.directory?(f)}
|
|
126
|
+
updir = hescape( dir.split(/\//)[0..-2].join("/"))
|
|
127
|
+
updir="/" if updir.length==0
|
|
128
|
+
up=(dir!="/") ? "<input type='button' onclick='location.href=\"#{updir}\"' value='Parent'>" : ""
|
|
129
|
+
"<html><head><title>#{dir}</title></head>\n<body><h3><center>#{@name} : #{dir[0..-1]}</center></h3>\n<hr>#{up}<br>#{to_table(dirs.map {|s| " <a href='#{hescape(to_relative(s))}'>"+File.basename(s)+"/"+"</a>\n"})}<hr>#{to_tableb(files) {|f| [" <a href='#{hescape(to_relative(f))}'>"+File.basename(f)+"</a>",n3(File.size(f)),File.mtime(f).strftime("%d/%m/%Y %H:%M:%S")]}}</body></html>"
|
|
130
|
+
end
|
|
131
|
+
def to_relative(f) f.gsub(/^#{@rootd}/,"/") end
|
|
132
|
+
def to_absolute(f) "#{@rootd}#{f.gsub(/^\//,'')}" end
|
|
133
|
+
def n3(n)
|
|
134
|
+
u=" B"
|
|
135
|
+
if n> 10000000
|
|
136
|
+
n=n/(1024*1024)
|
|
137
|
+
u=" MB"
|
|
138
|
+
elsif n> 100000
|
|
139
|
+
n=n/1024
|
|
140
|
+
u=" KB"
|
|
141
|
+
end
|
|
142
|
+
"<div style='width:100px;text-align:right;'>#{(n.round.to_i.to_s.reverse.gsub(/(\d\d\d)(?=\d)/,'\1 ' ).reverse) +u} | </div>"
|
|
143
|
+
end
|
|
144
|
+
def to_table(l)
|
|
145
|
+
"<table><tr>#{l.map {|s| "<td>#{s}</td>"}.join("</tr><tr>")}</tr></table>"
|
|
146
|
+
end
|
|
147
|
+
def to_tableb(l,&bl)
|
|
148
|
+
"<table><tr>#{l.map {|s| "<td>#{bl.call(s).join("</td><td>")}</td>"}.join("</tr><tr>")}</tr></table>"
|
|
149
|
+
end
|
|
150
|
+
def sendError(sock,no,txt=nil)
|
|
151
|
+
if txt
|
|
152
|
+
txt="<html><body><code><pre></pre>#{txt}</code></body></html>"
|
|
153
|
+
end
|
|
154
|
+
sock.write "HTTP/1.0 #{no} NOK\r\nContent-type: #{mime(".html")}\r\n\r\n <html><p>Error #{no} : #{txt}</p></html>"
|
|
155
|
+
end
|
|
156
|
+
def sendData(sock,type,content)
|
|
157
|
+
sock.write "HTTP/1.0 200 OK\r\nContent-type: #{mime(type)}\r\nContent-size: #{content.size}\r\n\r\n"
|
|
158
|
+
sock.write(content)
|
|
159
|
+
end
|
|
160
|
+
def sendFile(sock,filename)
|
|
161
|
+
s=File.size(filename)
|
|
162
|
+
if s < 0 || File.extname(filename).downcase==".lnk"
|
|
163
|
+
sendError(sock,500,"Error reading file #{filename} : (size=#{s})" )
|
|
164
|
+
return
|
|
165
|
+
end
|
|
166
|
+
sock.write "HTTP/1.0 200 OK\r\nContent-type: #{mime(filename)}\r\nContent-size: #{File.size(filename)}\r\nLast-Modified: #{httpdate(File.mtime(filename))}\r\nDate: #{httpdate(Time.now)}\r\n\r\n"
|
|
167
|
+
File.open(filename,"rb") do |f|
|
|
168
|
+
f.binmode; sock.binmode;
|
|
169
|
+
( sock.write(f.read(32000)) while (! f.eof? && ! sock.closed?) ) rescue nil
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
def httpdate( aTime ); (aTime||Time.now).gmtime.strftime( "%a, %d %b %Y %H:%M:%S GMT" ); end
|
|
173
|
+
def mime(string)
|
|
174
|
+
MIME[string.split(/\./).last] || "application/octet-stream"
|
|
175
|
+
end
|
|
176
|
+
LICON="☀☃☎☑☑☠☣☮☺☂".split(/;/).map {|c| c+";"}
|
|
177
|
+
MIME={"png" => "image/png", "gif" => "image/gif", "html" => "text/html","htm" => "text/html",
|
|
178
|
+
"js" => "text/javascript" ,"css" => "text/css","jpeg" => "image/jpeg" ,"jpg" => "image/jpeg" ,
|
|
179
|
+
"pdf"=> "application/pdf" , "svg" => "image/svg+xml","svgz" => "image/svg+xml",
|
|
180
|
+
"xml" => "text/xml" ,"xsl" => "text/xml" ,"bmp" => "image/bmp" ,"txt" => "text/plain" ,
|
|
181
|
+
"rb" => "text/plain" ,"pas" => "text/plain" ,"tcl" => "text/plain" ,"java" => "text/plain" ,
|
|
182
|
+
"c" => "text/plain" ,"h" => "text/plain" ,"cpp" => "text/plain", "xul" => "application/vnd.mozilla.xul+xml",
|
|
183
|
+
"doc" => "application/msword", "docx" => "application/msword","dot"=> "application/msword",
|
|
184
|
+
"xls" => "application/vnd.ms-excel","xla" => "application/vnd.ms-excel","xlt" => "application/vnd.ms-excel","xlsx" => "application/vnd.ms-excel",
|
|
185
|
+
"ppt" => "application/vnd.ms-powerpoint", "pptx" => "application/vnd.ms-powerpoint"
|
|
186
|
+
}
|
|
187
|
+
end # 161 loc webserver :)
|
|
188
|
+
|
|
189
|
+
class Webserver < WebserverAbstract
|
|
190
|
+
def initialize(port=7080,cadence=10,timeout=120)
|
|
191
|
+
super(port,Dir.getwd(),"",cadence,timeout)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
class WebserverRoot < WebserverAbstract
|
|
195
|
+
def initialize(port=7080,root=".",name="wwww",cadence=10,timeout=120,options={})
|
|
196
|
+
super(port,root,name,cadence,timeout,options)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class Shoes
|
|
202
|
+
class App
|
|
203
|
+
def web_server(port=9908,config={})
|
|
204
|
+
@ws=WebserverRoot.new(port,"/","green-shoes",10,300, {})
|
|
205
|
+
@html_menu="<div>"+config.map {|label,proc| "<a href='%s'>%s</a>" % [label,label.size>1 ? label[1..-1].capitalize : "Home"] }.join(" ")+"</div>"
|
|
206
|
+
lm=[]
|
|
207
|
+
config.each { |key,proc|
|
|
208
|
+
case key
|
|
209
|
+
when String
|
|
210
|
+
lm << [key,key.gsub(/\//,"")]
|
|
211
|
+
@ws.serve(key) {
|
|
212
|
+
[200,".html",@template_proc.call(proc.call(self))]
|
|
213
|
+
}
|
|
214
|
+
when :template
|
|
215
|
+
@template_proc=proc
|
|
216
|
+
when :menu
|
|
217
|
+
@html_menu="<div>"+proc.map {|label,href| "[<a href='%s'>%s</a>]" % [href,label] }.join(" ")+"</div>"
|
|
218
|
+
else
|
|
219
|
+
raise("option web serveur unknown : <#{key}>")
|
|
220
|
+
end
|
|
221
|
+
}
|
|
222
|
+
if ! config[:menu]
|
|
223
|
+
@html_menu="<hr><div>"+lm.map {|href,label| "<a href='%s'>%s</a>" % [href,label.size>1 ? label[1..-1].capitalize : "Home"] }.join(" ")+"</div>"
|
|
224
|
+
end
|
|
225
|
+
if ! config[:template]
|
|
226
|
+
@template_proc= proc {|body| "<html><body><center><h2> #{Time.now}</h2></center>"+@html_menu+"<hr>"+body+"<hr></body></html>"}
|
|
227
|
+
end
|
|
228
|
+
puts " WebServer ready on http://host:#{port}/"
|
|
229
|
+
@ws
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require 'gtk2'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
############################ SysTray ###############################
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module Gtk
|
|
9
|
+
class SysTray < StatusIcon
|
|
10
|
+
def initialize(window,title="title?",config)
|
|
11
|
+
$statusIcon=self
|
|
12
|
+
@popup_tray=Menu.new
|
|
13
|
+
@checkMenu={}
|
|
14
|
+
file= (config[:icon] && File.exist?(config[:icon])) ? config[:icon] : nil
|
|
15
|
+
alert("No icon defined for systray (or unknown file)") if ! file
|
|
16
|
+
config.each do |label,proc|
|
|
17
|
+
if Proc === proc
|
|
18
|
+
case label
|
|
19
|
+
when /^\+/
|
|
20
|
+
bshow = CheckMenuItem.new(label[1..-1])
|
|
21
|
+
@checkMenu[bshow]=bshow
|
|
22
|
+
bshow.signal_connect("toggled") { |w|
|
|
23
|
+
proc.call(! w.active?)
|
|
24
|
+
}
|
|
25
|
+
#TODO : get checkButton state to application closure, set state with closure return value
|
|
26
|
+
when /^-+/
|
|
27
|
+
bshow = SeparatorMenuItem.new
|
|
28
|
+
else
|
|
29
|
+
bshow = MenuItem.new(label)
|
|
30
|
+
bshow.signal_connect("activate") { proc.call(window.visible?) }
|
|
31
|
+
#TODO : icon in face of button
|
|
32
|
+
end
|
|
33
|
+
@popup_tray.append(bshow)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
if config[:quit]
|
|
37
|
+
@bquit_tray=ImageMenuItem.new(Stock::QUIT)
|
|
38
|
+
@bquit_tray.signal_connect("activate"){window.main_quit}
|
|
39
|
+
@popup_tray.append(SeparatorMenuItem.new)
|
|
40
|
+
@popup_tray.append(@bquit_tray)
|
|
41
|
+
end
|
|
42
|
+
@popup_tray.show_all
|
|
43
|
+
|
|
44
|
+
super()
|
|
45
|
+
self.pixbuf=if file then Gdk::Pixbuf.new(file) ;else nil ; end
|
|
46
|
+
self.tooltip=title||""
|
|
47
|
+
self.signal_connect('activate'){ window.visible? ? window.hide : window.show }
|
|
48
|
+
self.signal_connect('popup-menu'){|tray, button, time|
|
|
49
|
+
@popup_tray.popup(nil, nil, button, time)
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
=begin
|
|
55
|
+
syst_icon HAPPY_ICON,
|
|
56
|
+
syst_add_button "Edit configuration" {|state| edit_conf() } ,
|
|
57
|
+
syst_add_button "Execute Test" {|state| $win.show; update(true) },
|
|
58
|
+
syst_add_sepratator
|
|
59
|
+
syst_add_check "Option" {|state| },
|
|
60
|
+
syst_quit_button true
|
|
61
|
+
=end
|
|
62
|
+
class Shoes
|
|
63
|
+
class App
|
|
64
|
+
def systray
|
|
65
|
+
@systray_config={}
|
|
66
|
+
yield
|
|
67
|
+
@systray=::Gtk::SysTray.new(self.win,@owner.instance_variable_get('@title'),@systray_config)
|
|
68
|
+
end
|
|
69
|
+
def syst_icon(file) @systray_config[:icon]=file ; end
|
|
70
|
+
def syst_add_button(label,&prc) @systray_config[label]=prc ; end
|
|
71
|
+
def syst_add_sepratator() @systray_config["--#{@systray_config.size}"]= proc {} ; end
|
|
72
|
+
def syst_add_check(label,&prc) @systray_config["+"+label]=prc ; end
|
|
73
|
+
def syst_quit_button(yes) @systray_config[:quit]=yes ; end
|
|
74
|
+
def systray_setup(config)
|
|
75
|
+
@systray=::Gtk::SysTray.new(self.win,@owner.instance_variable_get('@title'),config)
|
|
76
|
+
end
|
|
77
|
+
def show_app()
|
|
78
|
+
win.deiconify
|
|
79
|
+
win.show
|
|
80
|
+
end
|
|
81
|
+
def hide_app
|
|
82
|
+
win.iconify
|
|
83
|
+
win.hide
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def close_dialog
|
|
87
|
+
win.destroy
|
|
88
|
+
Shoes.APPS.delete app
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
|
|
3
|
+
class Object
|
|
4
|
+
def invoke_in_shoes_mainloop(&blk)
|
|
5
|
+
return unless defined?($__shoes_app__)
|
|
6
|
+
return unless Shoes::App===$__shoes_app__
|
|
7
|
+
$__shoes_app__.get_queue().push( blk )
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class Shoes
|
|
12
|
+
class App
|
|
13
|
+
def get_queue()
|
|
14
|
+
@queue=Queue.new if ! defined?(@queue)
|
|
15
|
+
@queue
|
|
16
|
+
end
|
|
17
|
+
def define_async_thread_invoker(period=0.1)
|
|
18
|
+
$__shoes_app__=self
|
|
19
|
+
@queue=Queue.new if ! defined?(@queue)
|
|
20
|
+
every(period) do
|
|
21
|
+
while @queue.size>0
|
|
22
|
+
instance_eval &@queue.pop
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if defined?(WebserverRoot)
|
|
31
|
+
class Shoes
|
|
32
|
+
class App
|
|
33
|
+
def web_server(port=9908,config={})
|
|
34
|
+
@ws=WebserverRoot.new(port,"/","green-shoes",10,300, {})
|
|
35
|
+
@html_menu="<div>"+config.map {|label,proc| "<a href='%s'>%s</a>" % [label,label.size>1 ? label[1..-1].capitalize : "Home"] }.join(" ")+"</div>"
|
|
36
|
+
lm=[]
|
|
37
|
+
config.each { |key,proc|
|
|
38
|
+
case key
|
|
39
|
+
when String
|
|
40
|
+
lm << [key,key.gsub(/\//,"")]
|
|
41
|
+
@ws.serve(key) {
|
|
42
|
+
[200,".html",@template_proc.call(proc.call(self))]
|
|
43
|
+
}
|
|
44
|
+
when :template
|
|
45
|
+
@template_proc=proc
|
|
46
|
+
when :menu
|
|
47
|
+
@html_menu="<div>"+proc.map {|label,href| "[<a href='%s'>%s</a>]" % [href,label] }.join(" ")+"</div>"
|
|
48
|
+
else
|
|
49
|
+
raise("option web serveur unknown : <#{key}>")
|
|
50
|
+
end
|
|
51
|
+
}
|
|
52
|
+
if ! config[:menu]
|
|
53
|
+
@html_menu="<hr><div>"+lm.map {|href,label| "<a href='%s'>%s</a>" % [href,label.size>1 ? label[1..-1].capitalize : "Home"] }.join(" ")+"</div>"
|
|
54
|
+
end
|
|
55
|
+
if ! config[:template]
|
|
56
|
+
@template_proc= proc {|body| "<html><body><center><h2> #{Time.now}</h2></center>"+@html_menu+"<hr>"+body+"<hr></body></html>"}
|
|
57
|
+
end
|
|
58
|
+
puts " WebServer ready on http://host:#{port}/"
|
|
59
|
+
@ws
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
class Shoes
|
|
2
|
+
class App
|
|
3
|
+
def tree_view titles, tables
|
|
4
|
+
@tree_view_methods = titles.map{|s| s.downcase.gsub(' ', '_').to_sym}
|
|
5
|
+
@tree_view_row = Struct.new *@tree_view_methods
|
|
6
|
+
tree = mk_tree tables
|
|
7
|
+
@tree_view_paths, @tree_view_rows = [], {}
|
|
8
|
+
mk_path tree
|
|
9
|
+
tmp = tree.flatten
|
|
10
|
+
@tree_view_paths.each_with_index do |path, i|
|
|
11
|
+
@tree_view_rows[path] = tmp[i]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
titles.each_with_index do |name, i|
|
|
15
|
+
canvas.append_column Gtk::TreeViewColumn.new(name, Gtk::CellRendererText.new, "text" => i)
|
|
16
|
+
end
|
|
17
|
+
@tree_view_store = Gtk::TreeStore.new *Array.new(@tree_view_methods.length){String}
|
|
18
|
+
|
|
19
|
+
mk_column tree
|
|
20
|
+
canvas.model = @tree_view_store
|
|
21
|
+
|
|
22
|
+
canvas.signal_connect("cursor-changed") do |s|
|
|
23
|
+
yield @tree_view_rows[s.cursor[0].to_str]
|
|
24
|
+
end if block_given?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def mk_column vals, parent = nil
|
|
28
|
+
vals.each_with_index do |v, i|
|
|
29
|
+
if v.is_a? Array
|
|
30
|
+
mk_column v, parent
|
|
31
|
+
else
|
|
32
|
+
if i.zero?
|
|
33
|
+
parent = @tree_view_store.append parent
|
|
34
|
+
@tree_view_methods.each_with_index{|e, i| parent[i] = eval("v.#{e}").to_s}
|
|
35
|
+
else
|
|
36
|
+
child = @tree_view_store.append parent
|
|
37
|
+
@tree_view_methods.each_with_index{|e, i| child[i] = eval("v.#{e}").to_s}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def mk_tree tables
|
|
44
|
+
tables.map do |table|
|
|
45
|
+
if table.is_a? Array
|
|
46
|
+
mk_tree table
|
|
47
|
+
else
|
|
48
|
+
return @tree_view_row.new( *tables )
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def mk_path tree, path = []
|
|
54
|
+
tree.each_with_index do |t, i|
|
|
55
|
+
if t.is_a? Array
|
|
56
|
+
path.push(path.empty? ? i : (i.zero? ? i : i-1))
|
|
57
|
+
mk_path t, path
|
|
58
|
+
path.pop
|
|
59
|
+
else
|
|
60
|
+
tmp = i.zero? ? path : [path, i-1]
|
|
61
|
+
@tree_view_paths << tmp.flatten.join(':')
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/shoes/app.rb
CHANGED
|
@@ -122,6 +122,7 @@ class Shoes
|
|
|
122
122
|
SPAN_FORM.keys.+([:stroke, :fill]).each{|k| args.delete k}
|
|
123
123
|
text, attr_list = make_pango_attr args[:markup]
|
|
124
124
|
args[:size] ||= font_size
|
|
125
|
+
(args[:size] = font_size * FONT_SIZE[args[:size]]) if FONT_SIZE[args[:size]]
|
|
125
126
|
args[:font] ||= (@font_family or 'sans')
|
|
126
127
|
args[:align] ||= 'left'
|
|
127
128
|
line_height = args[:size] * 2
|
|
@@ -271,6 +272,7 @@ class Shoes
|
|
|
271
272
|
tv = Gtk::TextView.new
|
|
272
273
|
tv.wrap_mode = Gtk::TextTag::WRAP_WORD
|
|
273
274
|
tv.buffer.text = args[:text].to_s
|
|
275
|
+
tv.modify_font(Pango::FontDescription.new(args[:font])) if args[:font]
|
|
274
276
|
|
|
275
277
|
eb = Gtk::ScrolledWindow.new
|
|
276
278
|
eb.set_size_request args[:width], args[:height]
|
data/lib/shoes/basic.rb
CHANGED
data/lib/shoes/help.rb
CHANGED
|
@@ -180,7 +180,7 @@ class Manual < Shoes
|
|
|
180
180
|
stack width: 80 do
|
|
181
181
|
inscription file[0...-3]
|
|
182
182
|
img = image File.join(DIR, "../snapshots/#{file[0..-3]}png"), width: 50, height: 50
|
|
183
|
-
img.click{Dir.chdir(File.join DIR, '../samples'){instance_eval
|
|
183
|
+
img.click{Dir.chdir(File.join DIR, '../samples'){instance_eval(IO.read(file),file,0)}}
|
|
184
184
|
para
|
|
185
185
|
end
|
|
186
186
|
end
|
data/lib/shoes/slot.rb
CHANGED
|
@@ -19,8 +19,10 @@ class Shoes
|
|
|
19
19
|
@app.cslot = self
|
|
20
20
|
@contents = []
|
|
21
21
|
(@parent.contents << self) unless @nocontrol
|
|
22
|
+
@shows = true
|
|
22
23
|
if block_given?
|
|
23
24
|
if args[:hidden]
|
|
25
|
+
@shows = false
|
|
24
26
|
BASIC_ATTRIBUTES_DEFAULT.merge! hidden: true
|
|
25
27
|
SLOT_ATTRIBUTES_DEFAULT.merge! hidden: true
|
|
26
28
|
@hidden_flag = true unless @parent.instance_variable_get '@hidden'
|
|
@@ -120,11 +122,18 @@ class Shoes
|
|
|
120
122
|
|
|
121
123
|
def show
|
|
122
124
|
@contents.each &:show
|
|
125
|
+
@shows = true
|
|
123
126
|
self
|
|
124
127
|
end
|
|
125
128
|
|
|
126
129
|
def hide
|
|
127
130
|
@contents.each &:hide
|
|
131
|
+
@shows = false
|
|
132
|
+
self
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def toggle
|
|
136
|
+
@shows ? hide : show
|
|
128
137
|
self
|
|
129
138
|
end
|
|
130
139
|
end
|
|
Binary file
|
|
Binary file
|
data/samples/sample53.rb
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
require '../lib/green_shoes'
|
|
2
2
|
|
|
3
|
+
titles = ['True or False', 'Count', 'bla bla bla']
|
|
4
|
+
tables = [[[true, 4, 'Cleaning Supplies'], [true, 1, 'Paper Towels'], [true, 3, 'Toilet Paper']],
|
|
5
|
+
[[true, 7, 'Food'], [true, 2, 'Bread'],
|
|
6
|
+
[[false, 1, 'Butter'], [[true, 2, 'Color'], [false, 1, 'red'], [true, 1, 'blue']], [false, 1, 'Shop'], [true, 1, 'City']],
|
|
7
|
+
[true, 1, 'Milk'], [false, 3, 'Chips'], [true, 4, 'Soda']],
|
|
8
|
+
[[true, 40, 'Cleaning Supplies'], [true, 10, 'Paper Towels'], [true, 30, 'Toilet Paper']]]
|
|
9
|
+
|
|
3
10
|
Shoes.app do
|
|
4
11
|
button 'Open TreeView Window' do
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
12
|
+
window width: 300, height: 400, treeview: true do
|
|
13
|
+
tree_view titles, tables do |tv|
|
|
14
|
+
owner.append do
|
|
15
|
+
owner.para [tv.true_or_false, tv.count, tv.bla_bla_bla]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
8
18
|
end
|
|
9
19
|
end
|
|
10
20
|
end
|
data/samples/sample54.rb
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#####################################################################################
|
|
2
|
+
# S U R V E I L L A N C E / ruby-shoes tools for watching home servers
|
|
3
|
+
#####################################################################################
|
|
4
|
+
|
|
5
|
+
$PERIODE=10
|
|
6
|
+
$config=[
|
|
7
|
+
["Serveur Search","geturl","http://localhost:8080"],
|
|
8
|
+
["Shares" ,"dir" ,"D:/"],
|
|
9
|
+
["Codeur" ,"tcpping","10.177.235.202:80"],
|
|
10
|
+
["Proxy" ,"geturl","http://10.153.32.75:80"],
|
|
11
|
+
]
|
|
12
|
+
ENV.delete('proxy_http')
|
|
13
|
+
|
|
14
|
+
##__EEND__##################################################################################
|
|
15
|
+
require '../lib/green_shoes'
|
|
16
|
+
#require 'green_shoes'
|
|
17
|
+
|
|
18
|
+
require 'tmpdir'
|
|
19
|
+
require 'open-uri'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
dsrc=File.dirname(__FILE__)
|
|
23
|
+
HAPPY_ICON ="#{dsrc}/face-smile-big.png"
|
|
24
|
+
UNHAPPY_ICON ="#{dsrc}/face-crying.png"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
$CONFIG_FILE="#{Dir.tmpdir}/surv_config.rb"
|
|
28
|
+
$toBeFirstEdit=false
|
|
29
|
+
unless File.exist?($CONFIG_FILE)
|
|
30
|
+
content=File.read(__FILE__).split(/##__EEND__/).first
|
|
31
|
+
File.open($CONFIG_FILE,"w") { |f| f.write(content)}
|
|
32
|
+
puts "configuration file #{$CONFIG_FILE} created !"
|
|
33
|
+
$toBeFirstEdit=true
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
$errorDetected=true
|
|
40
|
+
|
|
41
|
+
################## Dialog for edit configuration ####################
|
|
42
|
+
class ::Shoes
|
|
43
|
+
class App
|
|
44
|
+
def edit_conf()
|
|
45
|
+
::Shoes.app({:title=> "Config",:width=>600,:height=>650}) do
|
|
46
|
+
t= File.read($CONFIG_FILE)
|
|
47
|
+
stack do
|
|
48
|
+
flow :width=>1.0 do
|
|
49
|
+
background "#C0C0C0".."#A0A0A0"
|
|
50
|
+
para "This application do periodique request on LAN. You can: "
|
|
51
|
+
para "<b>*</b> specifies in $config the list of request : name,method, parameters."
|
|
52
|
+
para "<b>*</b> define periode in $periode (warning of memory leaks)"
|
|
53
|
+
para "This data are saved in file \n '#{$CONFIG_FILE}'."
|
|
54
|
+
para
|
|
55
|
+
end
|
|
56
|
+
@edit_box=edit_box( {:width=>1.0,:height=>370, :scroll=> true, :text=>t, :font => "Courier"} )
|
|
57
|
+
flow :width=> 400 do
|
|
58
|
+
button "Save",:width => 200 do
|
|
59
|
+
begin
|
|
60
|
+
eval @edit_box.text
|
|
61
|
+
$app.update(true)
|
|
62
|
+
File.open($CONFIG_FILE,"w") { |f| f.write(@edit_box.text)}
|
|
63
|
+
alert("Tested and saved!")
|
|
64
|
+
rescue Exception=> e
|
|
65
|
+
alert "Syntaxe Error : #{$!}"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
button "Quit",:width => 200 do
|
|
69
|
+
self.close_dialog
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
p 2
|
|
76
|
+
#----------------- Surveillances actions --------------------------------
|
|
77
|
+
# do_<ACTIONNAME>() >> raise Exception if NOK
|
|
78
|
+
|
|
79
|
+
def do_geturl(param)
|
|
80
|
+
open(param).close
|
|
81
|
+
end
|
|
82
|
+
def do_dir(param)
|
|
83
|
+
raise "not found" if ! File.directory?(param)
|
|
84
|
+
end
|
|
85
|
+
def do_snmp(param)
|
|
86
|
+
raise("not implemnted!")
|
|
87
|
+
end
|
|
88
|
+
def do_tcpping(param)
|
|
89
|
+
ip,port=param.split(/:/)
|
|
90
|
+
raise("ping") unless tping(ip,port.to_i,0.5)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def tping(host,port, timeout=5)
|
|
94
|
+
s=nil
|
|
95
|
+
begin
|
|
96
|
+
s = TCPSocket.new(host, port.to_s)
|
|
97
|
+
return(true)
|
|
98
|
+
rescue Exception => e
|
|
99
|
+
return false
|
|
100
|
+
ensure
|
|
101
|
+
(s.close if s ) rescue nil
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
#--------------------------- Surveillance Engine ---------------------------
|
|
105
|
+
# actions runs in paralleles, result is sorted by config order
|
|
106
|
+
# a queue is used for collect actions results (array are not thread-safe)
|
|
107
|
+
p 3
|
|
108
|
+
def do_surveillance(expand)
|
|
109
|
+
no0=0
|
|
110
|
+
lth=[]
|
|
111
|
+
queue=Queue.new
|
|
112
|
+
$config.each do |text0,comm0,param0|
|
|
113
|
+
lth<<Thread.new(text0,comm0,param0,no0) do |text,comm,param,no|
|
|
114
|
+
begin
|
|
115
|
+
send("do_#{comm}",param)
|
|
116
|
+
queue.push([no,false,text])
|
|
117
|
+
rescue
|
|
118
|
+
queue.push([no,true,text])
|
|
119
|
+
puts $!.to_s + " " + $!.backtrace.join("\n ")
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
no0+=1
|
|
123
|
+
end
|
|
124
|
+
lth.map(&:join)
|
|
125
|
+
r=[]
|
|
126
|
+
while queue.size>0 && (m=queue.pop)
|
|
127
|
+
r << m
|
|
128
|
+
end
|
|
129
|
+
return ( r.sort { |a,b| a[0]<=>b[0] } )
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def update(expand)
|
|
135
|
+
Thread.new {
|
|
136
|
+
now=Time.now.to_f
|
|
137
|
+
r=do_surveillance(expand) rescue p($!) ;
|
|
138
|
+
invoke_in_shoes_mainloop { do_raffraichissement(r,expand,now) }
|
|
139
|
+
}
|
|
140
|
+
end
|
|
141
|
+
def do_raffraichissement(r,expand,now)
|
|
142
|
+
nbError=0
|
|
143
|
+
app.clear do
|
|
144
|
+
border "#000000"
|
|
145
|
+
r.each do |mess|
|
|
146
|
+
no,err=*mess
|
|
147
|
+
flow {
|
|
148
|
+
background( !err ? ("#40A0A0".."#70C0C0") : ("#F0A0A0".."#F0C0C0") )
|
|
149
|
+
para" "+$config[no][0] + (err ? " : NOK" : " : ok")
|
|
150
|
+
nbError+=1 if err
|
|
151
|
+
} if err or expand
|
|
152
|
+
end
|
|
153
|
+
flow {
|
|
154
|
+
background "#B0F0F0".."#A0F0F0"
|
|
155
|
+
para " "+Time.now.to_s.split(/ /)[1] + " ("+((Time.now.to_f-now)*1000).round.to_s+" ms)"
|
|
156
|
+
}
|
|
157
|
+
end
|
|
158
|
+
# mettre en avant-plan la fenetre si detection d'apparition d'erreur(s)
|
|
159
|
+
if nbError>0
|
|
160
|
+
if ! $errorDetected
|
|
161
|
+
$statusIcon.blinking=true
|
|
162
|
+
$statusIcon.visible=true
|
|
163
|
+
$statusIcon.file=UNHAPPY_ICON
|
|
164
|
+
$win.show
|
|
165
|
+
$win.iconify
|
|
166
|
+
$win.deiconify
|
|
167
|
+
$errorDetected=true
|
|
168
|
+
end
|
|
169
|
+
else
|
|
170
|
+
if $errorDetected
|
|
171
|
+
$statusIcon.blinking=false
|
|
172
|
+
$errorDetected=false
|
|
173
|
+
$statusIcon.file=HAPPY_ICON
|
|
174
|
+
end
|
|
175
|
+
$win.hide if ! expand
|
|
176
|
+
end
|
|
177
|
+
h=24*(1+(expand ? $config.size : nbError))
|
|
178
|
+
$win.default_height=h
|
|
179
|
+
#$win.move(1080,960-h) : TODO: get screen-size...
|
|
180
|
+
$win.move(20,20)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
$statusIcon=nil
|
|
186
|
+
$win=nil
|
|
187
|
+
$p={}
|
|
188
|
+
$h=(($config.size+1)*24)
|
|
189
|
+
::Shoes.app :title=>"Surveillance", :left=>-1, :top=>-1, :width => 200, :height => $h do
|
|
190
|
+
$app=self
|
|
191
|
+
define_async_thread_invoker()
|
|
192
|
+
|
|
193
|
+
font "Arial"
|
|
194
|
+
$win=win
|
|
195
|
+
background "#40A0A0".."#A0F0F0"
|
|
196
|
+
border black
|
|
197
|
+
@st=stack(:width=> 1.0 , :height => 1.0) { border black }
|
|
198
|
+
every($PERIODE) { update(false) }
|
|
199
|
+
click { @st.clear ; timer(0.01) { update(true) } }
|
|
200
|
+
win.set_decorated(false)
|
|
201
|
+
#win.move(1080,960-$h)
|
|
202
|
+
win.move(20,20)
|
|
203
|
+
|
|
204
|
+
systray do
|
|
205
|
+
syst_icon HAPPY_ICON
|
|
206
|
+
syst_add_button "Edit configuration" do |state| edit_conf() end
|
|
207
|
+
syst_add_button "Execute Test" do |state| $win.show; update(true) end
|
|
208
|
+
syst_add_sepratator
|
|
209
|
+
syst_add_check "Option" do |state| alert("Test checkButon: " +state.to_s) end
|
|
210
|
+
syst_quit_button true
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
web_server(9990,{
|
|
214
|
+
"/" => proc {|app|
|
|
215
|
+
ret=do_surveillance(true).map { |mess| no,err,text=*mess ; text.split(/\b*:\b*/) }
|
|
216
|
+
@ws.to_tableb(ret) { |a| [a[0]," : ",a[1]] }
|
|
217
|
+
},
|
|
218
|
+
"/config" => proc {|app| @ws.to_tableb($config) { |a| a }},
|
|
219
|
+
"/stop" => proc {|app| Thread.new { sleep(1) ; exit!} ; "..."},
|
|
220
|
+
"/show" => proc {|app| Thread.new { break ; $win.show ; $win.deiconify ; } ; "ok"},# break!!! : don't gtk/shoes stuff in none main thread...
|
|
221
|
+
:template => proc { |body| "<html><head></head><body><h2><center>Surveillance @ "+Time.now.to_s+"</center></h2><hr>"+@html_menu+"<hr>"+body + "<hr></body></html>" },
|
|
222
|
+
:menu => { "Status"=> "/" , "Config" => "/config" , "Exit" => "/stop", "Home" => "http://localhost"}
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
edit_conf if $toBeFirstEdit
|
|
226
|
+
update(true)
|
|
227
|
+
end
|
data/snapshots/sample53.png
CHANGED
|
Binary file
|
|
Binary file
|
data/static/manual-en.txt
CHANGED
|
@@ -1518,7 +1518,7 @@ asterisk) is show for each letter typed.
|
|
|
1518
1518
|
|
|
1519
1519
|
=== :size » a number ===
|
|
1520
1520
|
|
|
1521
|
-
For: ''banner, caption, inscription, para, subtitle, tagline, title''.
|
|
1521
|
+
For: ''banner, caption, inscription, para, span, subtitle, tagline, title''.
|
|
1522
1522
|
|
|
1523
1523
|
Sets the pixel size for the font used inside this text block.
|
|
1524
1524
|
|
|
@@ -1532,8 +1532,6 @@ Font size may also be augmented, through use of the following strings:
|
|
|
1532
1532
|
* "x-large" - 143% of present size.
|
|
1533
1533
|
* "xx-large" - 173% of present size.
|
|
1534
1534
|
|
|
1535
|
-
'''Note:''' Green Shoes doesn't support as the above string style settings.
|
|
1536
|
-
|
|
1537
1535
|
=== :state » a string ===
|
|
1538
1536
|
|
|
1539
1537
|
For: ''button, check, edit_box, edit_line, list_box, radio''.
|
|
@@ -2607,8 +2605,6 @@ slot, you'll need to use `scroll_height()` to get the full size of the slot.
|
|
|
2607
2605
|
|
|
2608
2606
|
Hides the slot, so that it can't be seen. See also [[Position.show]] and [[Position.toggle]].
|
|
2609
2607
|
|
|
2610
|
-
'''Note:''' Green Shoes doesn't support `hide` method.
|
|
2611
|
-
|
|
2612
2608
|
=== left() » a number ===
|
|
2613
2609
|
|
|
2614
2610
|
The left pixel location of the slot. Also known as the x-axis coordinate.
|
|
@@ -2618,7 +2614,7 @@ The left pixel location of the slot. Also known as the x-axis coordinate.
|
|
|
2618
2614
|
Moves the slot to specific coordinates, the (left, top) being the upper left
|
|
2619
2615
|
hand corner of the slot.
|
|
2620
2616
|
|
|
2621
|
-
'''Note:''' Green Shoes doesn't support `
|
|
2617
|
+
'''Note:''' Green Shoes doesn't support `move` method.
|
|
2622
2618
|
|
|
2623
2619
|
=== remove() » self ===
|
|
2624
2620
|
|
|
@@ -2672,8 +2668,6 @@ Scrolls the slot to a certain coordinate. This must be between zero and
|
|
|
2672
2668
|
Reveals the slot, if it is hidden. See also [[Position.hide]] and
|
|
2673
2669
|
[[Position.toggle]].
|
|
2674
2670
|
|
|
2675
|
-
'''Note:''' Green Shoes doesn't support `show` method.
|
|
2676
|
-
|
|
2677
2671
|
=== style() » styles ===
|
|
2678
2672
|
|
|
2679
2673
|
Calling the `style` method with no arguments returns a hash of the styles
|
|
@@ -2713,8 +2707,6 @@ example, there is a `width` method, thus there is also a `width` style.
|
|
|
2713
2707
|
|
|
2714
2708
|
Hides the slot, if it is shown. Or shows the slot, if it is hidden.
|
|
2715
2709
|
|
|
2716
|
-
'''Note:''' Green Shoes doesn't support `toggle` method.
|
|
2717
|
-
|
|
2718
2710
|
=== top() » a number ===
|
|
2719
2711
|
|
|
2720
2712
|
The top pixel location of the slot. Also known as the y-axis coordinate.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: green_shoes
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.263.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,11 +9,11 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2011-
|
|
12
|
+
date: 2011-08-02 00:00:00.000000000Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: gtk2
|
|
16
|
-
requirement: &
|
|
16
|
+
requirement: &15873072 !ruby/object:Gem::Requirement
|
|
17
17
|
none: false
|
|
18
18
|
requirements:
|
|
19
19
|
- - ! '>='
|
|
@@ -21,7 +21,7 @@ dependencies:
|
|
|
21
21
|
version: '0'
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
|
-
version_requirements: *
|
|
24
|
+
version_requirements: *15873072
|
|
25
25
|
description: Green Shoes is one of colorful Shoes, written in pure Ruby with Ruby/GTK2.
|
|
26
26
|
email: ashbbb@gmail.com
|
|
27
27
|
executables: []
|
|
@@ -61,6 +61,10 @@ files:
|
|
|
61
61
|
- lib/ext/projector/matrix3d.rb
|
|
62
62
|
- lib/ext/projector/projector.rb
|
|
63
63
|
- lib/green_shoes.rb
|
|
64
|
+
- lib/plugins/httpd.rb
|
|
65
|
+
- lib/plugins/systray.rb
|
|
66
|
+
- lib/plugins/thread.rb
|
|
67
|
+
- lib/plugins/treeview.rb
|
|
64
68
|
- lib/shoes/anim.rb
|
|
65
69
|
- lib/shoes/app.rb
|
|
66
70
|
- lib/shoes/basic.rb
|
|
@@ -81,6 +85,8 @@ files:
|
|
|
81
85
|
- samples/akatsukiface.png
|
|
82
86
|
- samples/class-book.yaml
|
|
83
87
|
- samples/cy.png
|
|
88
|
+
- samples/face-crying.png
|
|
89
|
+
- samples/face-smile-big.png
|
|
84
90
|
- samples/loogink.png
|
|
85
91
|
- samples/potato_chopping/1258_s001.gif
|
|
86
92
|
- samples/potato_chopping/1258_s002.gif
|
|
@@ -192,6 +198,7 @@ files:
|
|
|
192
198
|
- samples/sample51.rb
|
|
193
199
|
- samples/sample52.rb
|
|
194
200
|
- samples/sample53.rb
|
|
201
|
+
- samples/sample54.rb
|
|
195
202
|
- samples/sample6.rb
|
|
196
203
|
- samples/sample7.rb
|
|
197
204
|
- samples/sample8.rb
|
|
@@ -199,7 +206,6 @@ files:
|
|
|
199
206
|
- samples/sample99.rb
|
|
200
207
|
- samples/shell.png
|
|
201
208
|
- samples/splash-hand.png
|
|
202
|
-
- samples/treeview.rb
|
|
203
209
|
- snapshots/helloworld.png
|
|
204
210
|
- snapshots/mini-hh.png
|
|
205
211
|
- snapshots/sample1.png
|
|
@@ -254,6 +260,7 @@ files:
|
|
|
254
260
|
- snapshots/sample51.png
|
|
255
261
|
- snapshots/sample52.png
|
|
256
262
|
- snapshots/sample53.png
|
|
263
|
+
- snapshots/sample54.png
|
|
257
264
|
- snapshots/sample6.png
|
|
258
265
|
- snapshots/sample7.png
|
|
259
266
|
- snapshots/sample8.png
|
data/samples/treeview.rb
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# The following code was quoted from http://ruby-gnome2.sourceforge.jp/hiki.cgi?tut-gtk2-treev-trees
|
|
2
|
-
# and edited a little bit for Green Shoes
|
|
3
|
-
|
|
4
|
-
def setup_tree_view(treeview)
|
|
5
|
-
renderer = Gtk::CellRendererText.new
|
|
6
|
-
column = Gtk::TreeViewColumn.new("Buy", renderer, "text" => $buy_index)
|
|
7
|
-
treeview.append_column(column)
|
|
8
|
-
renderer = Gtk::CellRendererText.new
|
|
9
|
-
column = Gtk::TreeViewColumn.new("Count", renderer, "text" => $qty_index)
|
|
10
|
-
treeview.append_column(column)
|
|
11
|
-
renderer = Gtk::CellRendererText.new
|
|
12
|
-
column = Gtk::TreeViewColumn.new("Product", renderer, "text" => $prod_index)
|
|
13
|
-
treeview.append_column(column)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
class GroceryItem
|
|
17
|
-
attr_accessor :product_type, :buy, :quantity, :product
|
|
18
|
-
def initialize(t,b,q,p)
|
|
19
|
-
@product_type, @buy, @quantity, @product = t, b, q, p
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
$buy_index = 0; $qty_index = 1; $prod_index = 2
|
|
23
|
-
$p_category = 0; $p_child = 1
|
|
24
|
-
|
|
25
|
-
list = Array.new
|
|
26
|
-
list[0] = GroceryItem.new($p_category, true, 0, "Cleaning Supplies")
|
|
27
|
-
list[1] = GroceryItem.new($p_child, true, 1, "Paper Towels")
|
|
28
|
-
list[2] = GroceryItem.new($p_child, true, 3, "Toilet Paper")
|
|
29
|
-
list[3] = GroceryItem.new($p_category, true, 0, "Food")
|
|
30
|
-
list[4] = GroceryItem.new($p_child, true, 2, "Bread")
|
|
31
|
-
list[5] = GroceryItem.new($p_child, false, 1, "Butter")
|
|
32
|
-
list[6] = GroceryItem.new($p_child, true, 1, "Milk")
|
|
33
|
-
list[7] = GroceryItem.new($p_child, false, 3, "Chips")
|
|
34
|
-
list[8] = GroceryItem.new($p_child, true, 4, "Soda")
|
|
35
|
-
|
|
36
|
-
setup_tree_view($app.canvas)
|
|
37
|
-
|
|
38
|
-
store = Gtk::TreeStore.new(TrueClass, Integer, String)
|
|
39
|
-
parent = child = nil
|
|
40
|
-
|
|
41
|
-
list.each_with_index do |e, i|
|
|
42
|
-
if (e.product_type == $p_category)
|
|
43
|
-
j = i + 1
|
|
44
|
-
while j < list.size && list[j].product_type != $p_category
|
|
45
|
-
list[i].quantity += list[j].quantity if list[j].buy
|
|
46
|
-
j += 1
|
|
47
|
-
end
|
|
48
|
-
parent = store.append(nil)
|
|
49
|
-
parent[$buy_index] = list[i].buy
|
|
50
|
-
parent[$qty_index] = list[i].quantity
|
|
51
|
-
parent[$prod_index] = list[i].product
|
|
52
|
-
else
|
|
53
|
-
child = store.append(parent)
|
|
54
|
-
child[$buy_index] = list[i].buy
|
|
55
|
-
child[$qty_index] = list[i].quantity
|
|
56
|
-
child[$prod_index] = list[i].product
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
$app.canvas.model = store
|