green_shoes 0.255.0 → 0.263.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|