#!/usr/bin/env ruby # # This script implements the "ss" application. "ss" implements # a presentation slide-show based on HTML slides. # require 'tk' require 'tkextlib/tkHTML' root = TkRoot.new(:title=>'HTML File Viewer', :iconname=>'HV') fswin = nil html = nil html_fs = nil hotkey = {} file = ARGV[0] # These are images to use with the actual image specified in a # "<img>" markup can't be found. # biggray = TkPhotoImage.new(:data=><<'EOD') R0lGODdhPAA+APAAALi4uAAAACwAAAAAPAA+AAACQISPqcvtD6OctNqLs968+w+G4kiW5omm 6sq27gvH8kzX9o3n+s73/g8MCofEovGITCqXzKbzCY1Kp9Sq9YrNFgsAO/// EOD smgray = TkPhotoImage.new(:data=><<'EOD') R0lGODdhOAAYAPAAALi4uAAAACwAAAAAOAAYAAACI4SPqcvtD6OctNqLs968+w+G4kiW5omm 6sq27gvH8kzX9m0VADv/ EOD # # A font chooser routine. # # html[:fontcommand] = pick_font pick_font = proc{|size, attrs| # puts "FontCmd: #{size} #{attrs}" [ ((attrs =~ /fixed/)? 'courier': 'charter'), (12 * (1.2**(size.to_f - 4.0))).to_i, ((attrs =~ /italic/)? 'italic': 'roman'), ((attrs =~ /bold/)? 'bold': 'normal') ].join(' ') } # This routine is called to pick fonts for the fullscreen view. # baseFontSize = 24 pick_font_fs = proc{|size, attrs| # puts "FontCmd: #{size} #{attrs}" [ ((attrs =~ /fixed/)? 'courier': 'charter'), (baseFontSize * (1.2**(size.to_f - 4.0))).to_i, ((attrs =~ /italic/)? 'italic': 'roman'), ((attrs =~ /bold/)? 'bold': 'normal') ].join(' ') } # # hyper_cmd = proc{|*args| puts "HyperlinkCommand: #{args.inspect}" } # This routine is called to run an applet # applet_arg = TkVarAccess.new_hash('AppletArg') run_applet = proc{|size, w, arglist| applet_arg.value = Hash[*simplelist(arglist)] return unless applet_arg.key?('src') src = html.remove(applet_arg['src']) applet_arg['window'] = w applet_arg['fontsize'] = size begin Tk.load_tclscript(src) rescue => e puts "Applet error: #{e.message}" end } # # form_cmd = proc{|n, cmd, *args| } # # images = {} old_imgs = {} big_imgs = {} # # move_big_image = proc{|b| return unless big_imgs.key?(b) b.copy(big_imgs[b]) big_imgs[b].delete big_imgs.delete(b) } image_cmd = proc{|hs, *args| fn = args[0] if old_imgs.key?(fn) return (images[fn] = old_imgs.delete(fn)) end begin img = TkPhotoImage.new(:file=>fn) rescue return ((hs)? smallgray: biggray) end if hs img2 = TkPhotoImage.new img2.copy(img, :subsample=>[2,2]) img.delete img = img2 end if img.width * img.height > 20000 b = TkPhotoImage.new(:width=>img.width, :height=>img.height) big_imgs[b] = img img = b Tk.after_idle(proc{ move_big_image.call(b) }) end images[fn] = img img } # # This routine is called for every <SCRIPT> markup # script_cmd = proc{|*args| # puts "ScriptCmd: #{args.inspect}" } # This routine is called for every <APPLET> markup # applet_cmd = proc{|w, arglist| # puts "AppletCmd: w=#{w} arglist=#{arglist}" #TkLabel.new(w, :text=>"The Applet #{w}", :bd=>2, :relief=>raised) } # This binding fires when there is a click on a hyperlink # href_binding = proc{|w, x, y| lst = w.href(x, y) unless lst.empty? process_url.call(lst) end } # # last_dir = Dir.pwd sel_load = proc{ filetypes = [ ['Html Files', ['.html', '.htm']], ['All Files', '*'] ] f = Tk.getOpenFile(:initialdir=>last_dir, :filetypes=>filetypes) if f != '' load_file.call(f) last_dir = File.dirname(f) end } # Clear the screen. # clear_screen = proc{ if html_fs && html_fs.exist? w = html_fs else w = html end w.clear old_imgs.clear big_imgs.clear hotkey.clear images.each{|k, v| old_imgs[k] = v } images.clear } # Read a file # read_file = proc{|name| begin fp = open(name, 'r') ret = fp.read(File.size(name)) rescue ret = nil fp = nil Tk.messageBox(:icon=>'error', :message=>"fail to open '#{name}'", :type=>:ok) ensure fp.close if fp end ret } # Process the given URL # process_url = proc{|url| case url[0] when /^file:/ load_file.call(url[0][5..-1]) when /^exec:/ Tk.ip_eval(url[0][5..-1].tr('\\', ' ')) else load_file.call(url[0]) end } # Load a file into the HTML widget # last_file = '' load_file = proc{|name| return unless (doc = read_file.call(name)) clear_screen.call last_file = name if html_fs && html_fs.exist? w = html_fs else w = html end w.configure(:base=>name) w.parse(doc) w.configure(:cursor=>'top_left_arrow') old_imgs.clear } # Refresh the current file. # refresh = proc{|*args| load_file.call(last_file) if last_file } # This routine is called whenever a "<meta>" markup is seen. # meta = proc{|w, tag, alist| v = Hash[*simplelist(alist)] if v.kye?('key') && v.key?('href') hotkey[v['key']] = w.resolve(v['href']) end if v.kye?('next') hotkey['Down'] =v['next'] end if v.kye?('prev') hotkey['Up'] =v['prev'] end if v.kye?('other') hotkey['o'] =v['other'] end } # Go from full-screen mode back to window mode. # fullscreen_off = proc{ fswin.destroy root.deiconify Tk.update root.raise html.clipwin.focus clear_screen.call old_imgs.clear refresh.call } # Go from window mode to full-screen mode. # fullscreen = proc{ if fswin && fswin.exist? fswin.deiconify Tk.update fswin.raise return end width = root.winfo_screenwidth height = root.winfo_screenheight fswin = TkToplevel.new(:overrideredirect=>true, :geometry=>"#{width}x#{height}+0+0") html_fs = Tk::HTML_Widget.new(fswin, :padx=>5, :pady=>9, :formcommand=>form_cmd, :imagecommand=>proc{image_cmd.call(0)}, :scriptcommand=>script_cmd, :appletcommand=>applet_cmd, :hyperlinkcommand=>hyper_cmd, :bg=>'white', :tablerelief=>:raised, :appletcommand=>proc{|*args| run_applet('big', *args) }, :fontcommand=>pick_font_fs, :cursor=>:tcross) { pack(:fill=>:both, :expand=>true) token_handler('meta', proc{|*args| meta.call(self, *args)}) } clear_screen.call old_imgs.clear refresh.call Tk.update html_fs.clipwin.focus } # # key_block = false key_press = proc{|w, keysym| return if key_block key_block = true Tk.after(250, proc{key_block = false}) if hotkey.key?(keysym) process_url.call(hotkey[keysym]) end case keysym when 'Escape' if fswin && fswin.exist? fullscreen_off.call else fullscreen.call end end } Tk::HTML_Widget::ClippingWindow.bind('1', key_press, '%W Down') Tk::HTML_Widget::ClippingWindow.bind('3', key_press, '%W Up') Tk::HTML_Widget::ClippingWindow.bind('2', key_press, '%w Down') Tk::HTML_Widget::ClippingWindow.bind('KeyPress', key_press, '%W %K') ############################################ # # Build the half-size view of the page # menu_spec = [ [['File', 0], ['Open', sel_load, 0], ['Full Screen', fullscreen, 0], ['Refresh', refresh, 0], '---', ['Exit', proc{exit}, 1]] ] mbar = root.add_menubar(menu_spec) html = Tk::HTML_Widget.new(:width=>512, :height=>384, :padx=>5, :pady=>9, :formcommand=>form_cmd, :imagecommand=>proc{|*args| image_cmd.call(1, *args) }, :scriptcommand=>script_cmd, :appletcommand=>applet_cmd, :hyperlinkcommand=>hyper_cmd, :fontcommand=>pick_font, :appletcommand=>proc{|*args| run_applet.call('small', *args) }, :bg=>'white', :tablerelief=>:raised) html.token_handler('meta', proc{|*args| meta.call(html, *args)}) vscr = html.yscrollbar(TkScrollbar.new) hscr = html.xscrollbar(TkScrollbar.new) Tk.grid(html, vscr, :sticky=>:news) Tk.grid(hscr, :sticky=>:ew) Tk.root.grid_columnconfigure(0, :weight=>1) Tk.root.grid_columnconfigure(1, :weight=>0) Tk.root.grid_rowconfigure(0, :weight=>1) Tk.root.grid_rowconfigure(1, :weight=>0) ############################################ html.clipwin.focus # If an arguent was specified, read it into the HTML widget. # Tk.update if file && file != "" load_file.call(file) end ############################################ Tk.mainloop