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 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