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 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.255.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="&#9728;&#9731;&#9742;&#9745;&#9745;&#9760;&#9763;&#9774;&#9786;&#9730;".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
@@ -327,6 +327,7 @@ class Shoes
327
327
  end
328
328
 
329
329
  def items= items
330
+ @items.length.times{real.remove_text 0}
330
331
  @items = items
331
332
  items.each{|item| real.append_text item.to_s}
332
333
  end
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 IO.read(file)}}
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
- Shoes.app width: 275, height: 300, treeview: true do
6
- $app = app
7
- load './treeview.rb'
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
@@ -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
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 `gutter` method.
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.255.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-07-23 00:00:00.000000000Z
12
+ date: 2011-08-02 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: gtk2
16
- requirement: &14643048 !ruby/object:Gem::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: *14643048
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