#!/usr/bin/env ruby version_number = "1.3.1" # Loads the ncurses-ruby module and imports "Ncurses" into the # current namespace. It stops the program if loading the # ncurses-ruby module fails. def load_ncurses begin require "ncurses" include Ncurses rescue LoadError $stderr.print < COLOR_WHITE, "yellow" => COLOR_YELLOW, "red" => COLOR_RED, "green" => COLOR_GREEN, "blue" => COLOR_BLUE, "cyan" => COLOR_CYAN, "magenta" => COLOR_MAGENTA, "black" => COLOR_BLACK, "default" => -1 } colors[color] end # Maps color name to a color pair index def ColorMap.get_color_pair(color) colors = { "white" => 1, "yellow" => 2, "red" => 3, "green" => 4, "blue" => 5, "cyan" => 6, "magenta" => 7, "black" => 8, "default" =>-1} colors[color] end end # Opens a TPP source file, and splits it into the different pages. class FileParser def initialize(filename) @filename = filename @pages = [] end # Parses the specified file and returns an array of Page objects def get_pages begin f = File.open(@filename) rescue $stderr.puts "Error: couldn't open file: #{$!}" Kernel.exit(1) end number_pages = 0 cur_page = Page.new("slide " + (number_pages + 1).to_s) f.each_line do |line| line.chomp! case line when /^--##/ # ignore comments when /^--newpage/ @pages << cur_page number_pages += 1 name = line.sub(/^--newpage/,"") if name == "" then name = "slide " + (number_pages+1).to_s else name.strip! end cur_page = Page.new(name) else cur_page.add_line(line) end # case end # each @pages << cur_page end end # class FileParser # Represents a page (aka `slide') in TPP. A page consists of a title and one or # more lines. class Page def initialize(title) @lines = [] @title = title @cur_line = 0 @eop = false end # Appends a line to the page, but only if _line_ is not null def add_line(line) @lines << line if line end # Returns the next line. In case the last line is hit, then the end-of-page marker is set. def next_line line = @lines[@cur_line] @cur_line += 1 if @cur_line >= @lines.size then @eop = true end return line end # Returns whether end-of-page has been reached. def eop? @eop end # Resets the end-of-page marker and sets the current line marker to the first line def reset_eop @cur_line = 0 @eop = false end # Returns all lines in the page. def lines @lines end # Returns the page's title def title @title end end # Implements a generic visualizer from which all other visualizers need to be # derived. If Ruby supported abstract methods, all the do_* methods would be # abstract. class TppVisualizer def initialize # nothing end # Splits a line into several lines, where each of the result lines is at most # _width_ characters long, caring about word boundaries, and returns an array # of strings. def split_lines(text,width) lines = [] if text then begin i = width if text.length <= i then # text length is OK -> add it to array and stop splitting lines << text text = "" else # search for word boundary (space actually) while i > 0 and text[i] != ' '[0] do i -= 1 end # if we can't find any space character, simply cut it off at the maximum width if i == 0 then i = width end # extract line x = text[0..i-1] # remove extracted line text = text[i+1..-1] # added line to array lines << x end end while text.length > 0 end return lines end def do_footer(footer_text) $stderr.puts "Error: TppVisualizer#do_footer has been called directly." Kernel.exit(1) end def do_header(header_text) $stderr.puts "Error: TppVisualizer#do_header has been called directly." Kernel.exit(1) end def do_refresh $stderr.puts "Error: TppVisualizer#do_refresh has been called directly." Kernel.exit(1) end def new_page $stderr.puts "Error: TppVisualizer#new_page has been called directly." Kernel.exit(1) end def do_heading(text) $stderr.puts "Error: TppVisualizer#do_heading has been called directly." Kernel.exit(1) end def do_withborder $stderr.puts "Error: TppVisualizer#do_withborder has been called directly." Kernel.exit(1) end def do_horline $stderr.puts "Error: TppVisualizer#do_horline has been called directly." Kernel.exit(1) end def do_color(text) $stderr.puts "Error: TppVisualizer#do_color has been called directly." Kernel.exit(1) end def do_center(text) $stderr.puts "Error: TppVisualizer#do_center has been called directly." Kernel.exit(1) end def do_right(text) $stderr.puts "Error: TppVisualizer#do_right has been called directly." Kernel.exit(1) end def do_exec(cmdline) $stderr.puts "Error: TppVisualizer#do_exec has been called directly." Kernel.exit(1) end def do_wait $stderr.puts "Error: TppVisualizer#do_wait has been called directly." Kernel.exit(1) end def do_beginoutput $stderr.puts "Error: TppVisualizer#do_beginoutput has been called directly." Kernel.exit(1) end def do_beginshelloutput $stderr.puts "Error: TppVisualizer#do_beginshelloutput has been called directly." Kernel.exit(1) end def do_endoutput $stderr.puts "Error: TppVisualizer#do_endoutput has been called directly." Kernel.exit(1) end def do_endshelloutput $stderr.puts "Error: TppVisualizer#do_endshelloutput has been called directly." Kernel.exit(1) end def do_sleep(time2sleep) $stderr.puts "Error: TppVisualizer#do_sleep has been called directly." Kernel.exit(1) end def do_boldon $stderr.puts "Error: TppVisualizer#do_boldon has been called directly." Kernel.exit(1) end def do_boldoff $stderr.puts "Error: TppVisualizer#do_boldoff has been called directly." Kernel.exit(1) end def do_revon $stderr.puts "Error: TppVisualizer#do_revon has been called directly." Kernel.exit(1) end def do_revoff $stderr.puts "Error: TppVisualizer#do_revoff has been called directly." Kernel.exit(1) end def do_ulon $stderr.puts "Error: TppVisualizer#do_ulon has been called directly." Kernel.exit(1) end def do_uloff $stderr.puts "Error: TppVisualizer#do_uloff has been called directly." Kernel.exit(1) end def do_beginslideleft $stderr.puts "Error: TppVisualizer#do_beginslideleft has been called directly." Kernel.exit(1) end def do_endslide $stderr.puts "Error: TppVisualizer#do_endslide has been called directly." Kernel.exit(1) end def do_command_prompt $stderr.puts "Error: TppVisualizer#do_command_prompt has been called directly." Kernel.exit(1) end def do_beginslideright $stderr.puts "Error: TppVisualizer#do_beginslideright has been called directly." Kernel.exit(1) end def do_beginslidetop $stderr.puts "Error: TppVisualizer#do_beginslidetop has been called directly." Kernel.exit(1) end def do_beginslidebottom $stderr.puts "Error: TppVisualizer#do_beginslidebottom has been called directly." Kernel.exit(1) end def do_sethugefont $stderr.puts "Error: TppVisualizer#do_sethugefont has been called directly." Kernel.exit(1) end def do_huge(text) $stderr.puts "Error: TppVisualizer#do_huge has been called directly." Kernel.exit(1) end def print_line(line) $stderr.puts "Error: TppVisualizer#print_line has been called directly." Kernel.exit(1) end def do_title(title) $stderr.puts "Error: TppVisualizer#do_title has been called directly." Kernel.exit(1) end def do_author(author) $stderr.puts "Error: TppVisualizer#do_author has been called directly." Kernel.exit(1) end def do_date(date) $stderr.puts "Error: TppVisualizer#do_date has been called directly." Kernel.exit(1) end def do_bgcolor(color) $stderr.puts "Error: TppVisualizer#do_bgcolor has been called directly." Kernel.exit(1) end def do_fgcolor(color) $stderr.puts "Error: TppVisualizer#do_fgcolor has been called directly." Kernel.exit(1) end def do_color(color) $stderr.puts "Error: TppVisualizer#do_color has been called directly." Kernel.exit(1) end # Receives a _line_, parses it if necessary, and dispatches it # to the correct method which then does the correct processing. # It returns whether the controller shall wait for input. def visualize(line,eop) case line when /^--heading / text = line.sub(/^--heading /,"") do_heading(text) when /^--withborder/ do_withborder when /^--horline/ do_horline when /^--color / text = line.sub(/^--color /,"") text.strip! do_color(text) when /^--center / text = line.sub(/^--center /,"") do_center(text) when /^--right / text = line.sub(/^--right /,"") do_right(text) when /^--exec / cmdline = line.sub(/^--exec /,"") do_exec(cmdline) when /^---/ do_wait return true when /^--beginoutput/ do_beginoutput when /^--beginshelloutput/ do_beginshelloutput when /^--endoutput/ do_endoutput when /^--endshelloutput/ do_endshelloutput when /^--sleep / time2sleep = line.sub(/^--sleep /,"") do_sleep(time2sleep) when /^--boldon/ do_boldon when /^--boldoff/ do_boldoff when /^--revon/ do_revon when /^--revoff/ do_revoff when /^--ulon/ do_ulon when /^--uloff/ do_uloff when /^--beginslideleft/ do_beginslideleft when /^--endslideleft/, /^--endslideright/, /^--endslidetop/, /^--endslidebottom/ do_endslide when /^--beginslideright/ do_beginslideright when /^--beginslidetop/ do_beginslidetop when /^--beginslidebottom/ do_beginslidebottom when /^--sethugefont / params = line.sub(/^--sethugefont /,"") do_sethugefont(params.strip) when /^--huge / figlet_text = line.sub(/^--huge /,"") do_huge(figlet_text) when /^--footer / @footer_txt = line.sub(/^--footer /,"") do_footer(@footer_txt) when /^--header / @header_txt = line.sub(/^--header /,"") do_header(@header_txt) when /^--title / title = line.sub(/^--title /,"") do_title(title) when /^--author / author = line.sub(/^--author /,"") do_author(author) when /^--date / date = line.sub(/^--date /,"") if date == "today" then date = Time.now.strftime("%b %d %Y") elsif date =~ /^today / then date = Time.now.strftime(date.sub(/^today /,"")) end do_date(date) when /^--bgcolor / color = line.sub(/^--bgcolor /,"").strip do_bgcolor(color) when /^--fgcolor / color = line.sub(/^--fgcolor /,"").strip do_fgcolor(color) when /^--color / color = line.sub(/^--color /,"").strip do_color(color) else print_line(line) end return false end def close # nothing end end # Implements an interactive visualizer which builds on top of ncurses. class NcursesVisualizer < TppVisualizer def initialize @figletfont = "standard" Ncurses.initscr Ncurses.curs_set(0) Ncurses.cbreak # unbuffered input Ncurses.noecho # turn off input echoing Ncurses.stdscr.intrflush(false) Ncurses.stdscr.keypad(true) @screen = Ncurses.stdscr setsizes Ncurses.start_color() Ncurses.use_default_colors() do_bgcolor("black") #do_fgcolor("white") @fgcolor = ColorMap.get_color_pair("white") @voffset = 5 @indent = 3 @cur_line = @voffset @output = @shelloutput = false end def get_key ch = @screen.getch case ch when Ncurses::KEY_RIGHT return :keyright when Ncurses::KEY_DOWN return :keydown when Ncurses::KEY_LEFT return :keyleft when Ncurses::KEY_UP return :keyup when Ncurses::KEY_RESIZE return :keyresize else return ch end end def clear @screen.clear @screen.refresh end def setsizes @termwidth = Ncurses.getmaxx(@screen) @termheight = Ncurses.getmaxy(@screen) end def do_refresh @screen.refresh end def do_withborder @withborder = true draw_border end def do_command_prompt() message = "Press any key to continue :)" cursor_pos = 0 max_len = 50 prompt = "tpp@localhost:~ $ " string = "" window = @screen.dupwin Ncurses.overwrite(window,@screen) # overwrite @screen with window Ncurses.curs_set(1) Ncurses.echo window.move(@termheight/4,1) window.clrtoeol() window.clrtobot() window.mvaddstr(@termheight/4,1,prompt) # add the prompt string loop do window.mvaddstr(@termheight/4,1+prompt.length,string) # add the code window.move(@termheight/4,1+prompt.length+cursor_pos) # move cursor to the end of code ch = window.getch case ch when Ncurses::KEY_ENTER, ?\n, ?\r Ncurses.curs_set(0) Ncurses.noecho rc = Kernel.system(string) if not rc then @screen.mvaddstr(@termheight/4+1,1,"Error: exec \"#{string}\" failed with error code #{$?}") @screen.mvaddstr(@termheight-2,@termwidth/2-message.length/2,message) end if rc then @screen.mvaddstr(@termheight-2,@termwidth/2-message.length/2,message) ch = Ncurses.getch() @screen.refresh end return when Ncurses::KEY_LEFT cursor_pos = [0, cursor_pos-1].max # jump one character to the left when Ncurses::KEY_RIGHT cursor_pos = [0, cursor_pos+1].max # jump one character to the right when Ncurses::KEY_BACKSPACE string = string[0...([0, cursor_pos-1].max)] + string[cursor_pos..-1] cursor_pos = [0, cursor_pos-1].max window.mvaddstr(@termheight/4, 1+prompt.length+string.length, " ") when " "[0]..255 if (cursor_pos < max_len) string[cursor_pos,0] = ch.chr cursor_pos += 1 else Ncurses.beep end else Ncurses.beep end end Ncurses.curs_set(0) end def draw_border @screen.move(0,0) @screen.addstr(".") (@termwidth-2).times { @screen.addstr("-") }; @screen.addstr(".") @screen.move(@termheight-2,0) @screen.addstr("`") (@termwidth-2).times { @screen.addstr("-") }; @screen.addstr("'") 1.upto(@termheight-3) do |y| @screen.move(y,0) @screen.addstr("|") end 1.upto(@termheight-3) do |y| @screen.move(y,@termwidth-1) @screen.addstr("|") end end def new_page @cur_line = @voffset @output = @shelloutput = false setsizes @screen.clear end def do_heading(line) @screen.attron(Ncurses::A_BOLD) print_heading(line) @screen.attroff(Ncurses::A_BOLD) end def do_horline @screen.attron(Ncurses::A_BOLD) @termwidth.times do |x| @screen.move(@cur_line,x) @screen.addstr("-") end @screen.attroff(Ncurses::A_BOLD) end def print_heading(text) width = @termwidth - 2*@indent lines = split_lines(text,width) lines.each do |l| @screen.move(@cur_line,@indent) x = (@termwidth - l.length)/2 @screen.move(@cur_line,x) @screen.addstr(l) @cur_line += 1 end end def do_center(text) width = @termwidth - 2*@indent if @output or @shelloutput then width -= 2 end lines = split_lines(text,width) lines.each do |l| @screen.move(@cur_line,@indent) if @output or @shelloutput then @screen.addstr("| ") end x = (@termwidth - l.length)/2 @screen.move(@cur_line,x) @screen.addstr(l) if @output or @shelloutput then @screen.move(@cur_line,@termwidth - @indent - 2) @screen.addstr(" |") end @cur_line += 1 end end def do_right(text) width = @termwidth - 2*@indent if @output or @shelloutput then width -= 2 end lines = split_lines(text,width) lines.each do |l| @screen.move(@cur_line,@indent) if @output or @shelloutput then @screen.addstr("| ") end x = (@termwidth - l.length - 5) @screen.move(@cur_line,x) @screen.addstr(l) if @output or @shelloutput then @screen.addstr(" |") end @cur_line += 1 end end def show_help_page help_text = [ "tpp help", "", "space bar ............................... display next entry within page", "space bar, cursor-down, cursor-right .... display next page", "b, cursor-up, cursor-left ............... display previous page", "q, Q .................................... quit tpp", "j, J .................................... jump directly to page", "l, L .................................... reload current file", "s, S .................................... jump to the first page", "e, E .................................... jump to the last page", "c, C .................................... start command line", "?, h .................................... this help screen" ] @screen.clear y = @voffset help_text.each do |line| @screen.move(y,@indent) @screen.addstr(line) y += 1 end @screen.move(@termheight - 2, @indent) @screen.addstr("Press any key to return to slide") @screen.refresh end def do_exec(cmdline) rc = Kernel.system(cmdline) if not rc then # @todo: add error message end end def do_wait # nothing end def do_beginoutput @screen.move(@cur_line,@indent) @screen.addstr(".") (@termwidth - @indent*2 - 2).times { @screen.addstr("-") } @screen.addstr(".") @output = true @cur_line += 1 end def do_beginshelloutput @screen.move(@cur_line,@indent) @screen.addstr(".") (@termwidth - @indent*2 - 2).times { @screen.addstr("-") } @screen.addstr(".") @shelloutput = true @cur_line += 1 end def do_endoutput if @output then @screen.move(@cur_line,@indent) @screen.addstr("`") (@termwidth - @indent*2 - 2).times { @screen.addstr("-") } @screen.addstr("'") @output = false @cur_line += 1 end end def do_title(title) do_boldon do_center(title) do_boldoff do_center("") end def do_footer(footer_txt) @screen.move(@termheight - 3, (@termwidth - footer_txt.length)/2) @screen.addstr(footer_txt) end def do_header(header_txt) @screen.move(@termheight - @termheight+1, (@termwidth - header_txt.length)/2) @screen.addstr(header_txt) end def do_author(author) do_center(author) do_center("") end def do_date(date) do_center(date) do_center("") end def do_endshelloutput if @shelloutput then @screen.move(@cur_line,@indent) @screen.addstr("`") (@termwidth - @indent*2 - 2).times { @screen.addstr("-") } @screen.addstr("'") @shelloutput = false @cur_line += 1 end end def do_sleep(time2sleep) Kernel.sleep(time2sleep.to_i) end def do_boldon @screen.attron(Ncurses::A_BOLD) end def do_boldoff @screen.attroff(Ncurses::A_BOLD) end def do_revon @screen.attron(Ncurses::A_REVERSE) end def do_revoff @screen.attroff(Ncurses::A_REVERSE) end def do_ulon @screen.attron(Ncurses::A_UNDERLINE) end def do_uloff @screen.attroff(Ncurses::A_UNDERLINE) end def do_beginslideleft @slideoutput = true @slidedir = "left" end def do_endslide @slideoutput = false end def do_beginslideright @slideoutput = true @slidedir = "right" end def do_beginslidetop @slideoutput = true @slidedir = "top" end def do_beginslidebottom @slideoutput = true @slidedir = "bottom" end def do_sethugefont(params) @figletfont = params end def do_huge(figlet_text) output_width = @termwidth - @indent output_width -= 2 if @output or @shelloutput op = IO.popen("figlet -f #{@figletfont} -w #{output_width} -k \"#{figlet_text}\"","r") op.readlines.each do |line| print_line(line) end op.close end def do_bgcolor(color) bgcolor = ColorMap.get_color(color) or COLOR_BLACK Ncurses.init_pair(1, COLOR_WHITE, bgcolor) Ncurses.init_pair(2, COLOR_YELLOW, bgcolor) Ncurses.init_pair(3, COLOR_RED, bgcolor) Ncurses.init_pair(4, COLOR_GREEN, bgcolor) Ncurses.init_pair(5, COLOR_BLUE, bgcolor) Ncurses.init_pair(6, COLOR_CYAN, bgcolor) Ncurses.init_pair(7, COLOR_MAGENTA, bgcolor) Ncurses.init_pair(8, COLOR_BLACK, bgcolor) if @fgcolor then Ncurses.bkgd(Ncurses.COLOR_PAIR(@fgcolor)) else Ncurses.bkgd(Ncurses.COLOR_PAIR(1)) end end def do_fgcolor(color) @fgcolor = ColorMap.get_color_pair(color) Ncurses.attron(Ncurses.COLOR_PAIR(@fgcolor)) end def do_color(color) num = ColorMap.get_color_pair(color) Ncurses.attron(Ncurses.COLOR_PAIR(num)) end def type_line(l) l.each_byte do |x| @screen.addstr(x.chr) @screen.refresh() r = rand(20) time_to_sleep = (5 + r).to_f / 250; # puts "#{time_to_sleep} #{r}" Kernel.sleep(time_to_sleep) end end def slide_text(l) return if l == "" case @slidedir when "left" xcount = l.length-1 while xcount >= 0 @screen.move(@cur_line,@indent) @screen.addstr(l[xcount..l.length-1]) @screen.refresh() time_to_sleep = 1.to_f / 20 Kernel.sleep(time_to_sleep) xcount -= 1 end when "right" (@termwidth - @indent).times do |pos| @screen.move(@cur_line,@termwidth - pos - 1) @screen.clrtoeol() maxpos = (pos >= l.length-1) ? l.length-1 : pos @screen.addstr(l[0..pos]) @screen.refresh() time_to_sleep = 1.to_f / 20 Kernel.sleep(time_to_sleep) end # do when "top" # ycount = @cur_line new_scr = @screen.dupwin 1.upto(@cur_line) do |i| Ncurses.overwrite(new_scr,@screen) # overwrite @screen with new_scr @screen.move(i,@indent) @screen.addstr(l) @screen.refresh() Kernel.sleep(1.to_f / 10) end when "bottom" new_scr = @screen.dupwin (@termheight-1).downto(@cur_line) do |i| Ncurses.overwrite(new_scr,@screen) @screen.move(i,@indent) @screen.addstr(l) @screen.refresh() Kernel.sleep(1.to_f / 10) end end end def print_line(line) width = @termwidth - 2*@indent if @output or @shelloutput then width -= 2 end lines = split_lines(line,width) lines.each do |l| @screen.move(@cur_line,@indent) if (@output or @shelloutput) and ! @slideoutput then @screen.addstr("| ") end if @shelloutput and (l =~ /^\$/ or l=~ /^%/ or l =~ /^#/) then # allow sh and csh style prompts type_line(l) elsif @slideoutput then slide_text(l) else @screen.addstr(l) end if (@output or @shelloutput) and ! @slideoutput then @screen.move(@cur_line,@termwidth - @indent - 2) @screen.addstr(" |") end @cur_line += 1 end end def close Ncurses.nocbreak Ncurses.endwin end def read_newpage(pages,current_page) page = [] @screen.clear() col = 0 line = 2 pages.each_index do |i| @screen.move(line,col*15 + 2) if current_page == i then @screen.printw("%2d %s <=",i+1,pages[i].title[0..80]) else @screen.printw("%2d %s",i+1,pages[i].title[0..80]) end line += 1 if line >= @termheight - 3 then line = 2 col += 1 end end prompt = "jump to slide: " prompt_indent = 12 @screen.move(@termheight - 2, @indent + prompt_indent) @screen.addstr(prompt) # @screen.refresh(); Ncurses.echo @screen.scanw("%d",page) Ncurses.noecho @screen.move(@termheight - 2, @indent + prompt_indent) (prompt.length + page[0].to_s.length).times { @screen.addstr(" ") } if page[0] then return page[0] - 1 end return -1 # invalid page end def store_screen @screen.dupwin end def restore_screen(s) Ncurses.overwrite(s,@screen) end def draw_slidenum(cur_page,max_pages,eop) @screen.move(@termheight - 2, @indent) @screen.attroff(Ncurses::A_BOLD) # this is bad @screen.addstr("[slide #{cur_page}/#{max_pages}]") if @footer_txt.to_s.length > 0 then do_footer(@footer_txt) end if @header_txt.to_s.length > 0 then do_header(@header_txt) end if eop then draw_eop_marker end end def draw_eop_marker @screen.move(@termheight - 2, @indent - 1) @screen.attron(A_BOLD) @screen.addstr("*") @screen.attroff(A_BOLD) end end # Implements a visualizer which converts TPP source to LaTeX-beamer source (http://latex-beamer.sf.net/ class LatexVisualizer < TppVisualizer def initialize(outputfile) @filename = outputfile begin @f = File.open(@filename,"w+") rescue $stderr.print "Error: couldn't open file: #{$!}" Kernel.exit(1) end @slide_open = false @verbatim_open = false @width = 50 @title = @date = @author = false @begindoc = false @f.puts '% Filename: tpp.tex % Purpose: template file for tpp latex export % Authors: (c) Andreas Gredler, Michael Prokop http://grml.org/ % License: This file is licensed under the GPL v2. % Latest change: Fre Apr 15 20:34:37 CEST 2005 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \documentclass{beamer} \mode { \usetheme{Montpellier} \setbeamercovered{transparent} } \usepackage[german]{babel} \usepackage{umlaut} \usepackage[latin1]{inputenc} \usepackage{times} \usepackage[T1]{fontenc} ' end def do_footer(footer_text) end def do_header(header_text) end def do_refresh end def try_close if @verbatim_open then @f.puts '\end{verbatim}' @verbatim_open = false end if @slide_open then @f.puts '\end{frame}' @slide_open = false end end def new_page try_close end def do_heading(text) try_close @f.puts "\\section{#{text}}" end def do_withborder end def do_horline end def do_color(text) end def do_center(text) print_line(text) end def do_right(text) print_line(text) end def do_exec(cmdline) end def do_wait end def do_beginoutput # TODO: implement output stuff end def do_beginshelloutput end def do_endoutput end def do_endshelloutput end def do_sleep(time2sleep) end def do_boldon end def do_boldoff end def do_revon end def do_command_prompt end def do_revoff end def do_ulon end def do_uloff end def do_beginslideleft end def do_endslide end def do_beginslideright end def do_beginslidetop end def do_beginslidebottom end def do_sethugefont(text) end def do_huge(text) end def try_open if not @begindoc then @f.puts '\begin{document}' @begindoc = true end if not @slide_open then @f.puts '\begin{frame}[fragile]' @slide_open = true end if not @verbatim_open then @f.puts '\begin{verbatim}' @verbatim_open = true end end def try_intro if @author and @title and @date and not @begindoc then @f.puts '\begin{document}' @begindoc = true end if @author and @title and @date then @f.puts '\begin{frame} \titlepage \end{frame}' end end def print_line(line) try_open split_lines(line,@width).each do |l| @f.puts "#{l}" end end def do_title(title) @f.puts "\\title[#{title}]{#{title}}" @title = true try_intro end def do_author(author) @f.puts "\\author{#{author}}" @author = true try_intro end def do_date(date) @f.puts "\\date{#{date}}" @date = true try_intro end def do_bgcolor(color) end def do_fgcolor(color) end def do_color(color) end def close try_close @f.puts '\end{document} %%%%% END OF FILE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%' @f.close end end # Implements a generic controller from which all other controllers need to be derived. class TppController def initialize $stderr.puts "Error: TppController.initialize has been called directly!" Kernel.exit(1) end def close $stderr.puts "Error: TppController.close has been called directly!" Kernel.exit(1) end def run $stderr.puts "Error: TppController.run has been called directly!" Kernel.exit(1) end end # Implements a non-interactive controller for ncurses. Useful for displaying # unattended presentation. class AutoplayController < TppController def initialize(filename,secs,visualizer_class) @filename = filename @vis = visualizer_class.new @seconds = secs @cur_page = 0 end def close @vis.close end def run begin @reload_file = false parser = FileParser.new(@filename) @pages = parser.get_pages if @cur_page >= @pages.size then @cur_page = @pages.size - 1 end @vis.clear @vis.new_page do_run end while @reload_file end def do_run loop do wait = false @vis.draw_slidenum(@cur_page + 1, @pages.size, false) # read and visualize lines until the visualizer says "stop" or we reached end of page begin line = @pages[@cur_page].next_line eop = @pages[@cur_page].eop? wait = @vis.visualize(line,eop) end while not wait and not eop # draw slide number on the bottom left and redraw: @vis.draw_slidenum(@cur_page + 1, @pages.size, eop) @vis.do_refresh if eop then if @cur_page + 1 < @pages.size then @cur_page += 1 else @cur_page = 0 end @pages[@cur_page].reset_eop @vis.new_page end Kernel.sleep(@seconds) end # loop end end # Implements an interactive controller which feeds the visualizer until it is # told to stop, and then reads a key press and executes the appropiate action. class InteractiveController < TppController def initialize(filename,visualizer_class) @filename = filename @vis = visualizer_class.new @cur_page = 0 end def close @vis.close end def run begin @reload_file = false parser = FileParser.new(@filename) @pages = parser.get_pages if @cur_page >= @pages.size then @cur_page = @pages.size - 1 end @vis.clear @vis.new_page do_run end while @reload_file end def do_run loop do wait = false @vis.draw_slidenum(@cur_page + 1, @pages.size, false) # read and visualize lines until the visualizer says "stop" or we reached end of page begin line = @pages[@cur_page].next_line eop = @pages[@cur_page].eop? wait = @vis.visualize(line,eop) end while not wait and not eop # draw slide number on the bottom left and redraw: @vis.draw_slidenum(@cur_page + 1, @pages.size, eop) @vis.do_refresh # read a character from the keyboard # a "break" in the when means that it breaks the loop, i.e. goes on with visualizing lines loop do ch = @vis.get_key case ch when 'q'[0], 'Q'[0] # 'Q'uit return when 'r'[0], 'R'[0] # 'R'edraw slide changed_page = true # @todo: actually implement redraw when 'e'[0], 'E'[0] @cur_page = @pages.size - 1 break when 's'[0], 'S'[0] @cur_page = 0 break when 'j'[0], 'J'[0] # 'J'ump to slide screen = @vis.store_screen p = @vis.read_newpage(@pages,@cur_page) if p >= 0 and p < @pages.size @cur_page = p @pages[@cur_page].reset_eop @vis.new_page else @vis.restore_screen(screen) end break when 'l'[0], 'L'[0] # re'l'oad current file @reload_file = true return when 'c'[0], 'C'[0] # command prompt screen = @vis.store_screen @vis.do_command_prompt @vis.clear @vis.restore_screen(screen) when '?'[0], 'h'[0] screen = @vis.store_screen @vis.show_help_page ch = @vis.get_key @vis.clear @vis.restore_screen(screen) when :keyright, :keydown, ' '[0] if @cur_page + 1 < @pages.size and eop then @cur_page += 1 @pages[@cur_page].reset_eop @vis.new_page end break when 'b'[0], 'B'[0], :keyleft, :keyup if @cur_page > 0 then @cur_page -= 1 @pages[@cur_page].reset_eop @vis.new_page end break when :keyresize @vis.setsizes end end end # loop end end # Implements a visualizer which converts TPP source to a nicely formatted text # file which can e.g. be used as handout. class TextVisualizer < TppVisualizer def initialize(outputfile) @filename = outputfile begin @f = File.open(@filename,"w+") rescue $stderr.print "Error: couldn't open file: #{$!}" Kernel.exit(1) end @output_env = false @title = @author = @date = false @figletfont = "small" @width = 80 end def do_footer(footer_text) end def do_header(header_text) end def do_refresh end def new_page @f.puts "--------------------------------------------" end def do_heading(text) @f.puts "\n" split_lines(text,@width).each do |l| @f.puts "#{l}\n" end @f.puts "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" end def do_withborder end def do_horline @f.puts "********************************************" end def do_color(text) end def do_exec(cmdline) end def do_wait end def do_beginoutput @f.puts ".---------------------------" @output_env = true end def do_beginshelloutput do_beginoutput end def do_endoutput @f.puts "`---------------------------" @output_env = false end def do_endshelloutput do_endoutput end def do_sleep(time2sleep) end def do_boldon end def do_boldoff end def do_revon end def do_command_prompt end def do_revoff end def do_ulon end def do_uloff end def do_beginslideleft end def do_endslide end def do_beginslideright end def do_beginslidetop end def do_beginslidebottom end def do_sethugefont(text) @figletfont = text end def do_huge(text) output_width = @width output_width -= 2 if @output_env op = IO.popen("figlet -f #{@figletfont} -w @output_width -k \"#{text}\"","r") op.readlines.each do |line| print_line(line) end op.close end def print_line(line) lines = split_lines(line,@width) lines.each do |l| if @output_env then @f.puts "| #{l}" else @f.puts "#{l}" end end end def do_center(text) lines = split_lines(text,@width) lines.each do |line| spaces = (@width - line.length) / 2 spaces = 0 if spaces < 0 spaces.times { line = " " + line } print_line(line) end end def do_right(text) lines = split_lines(text,@width) lines.each do |line| spaces = @width - line.length spaces = 0 if spaces < 0 spaces.times { line = " " + line } print_line(line) end end def do_title(title) @f.puts "Title: #{title}" @title = true if @title and @author and @date then @f.puts "\n\n" end end def do_author(author) @f.puts "Author: #{author}" @author = true if @title and @author and @date then @f.puts "\n\n" end end def do_date(date) @f.puts "Date: #{date}" @date = true if @title and @author and @date then @f.puts "\n\n" end end def do_bgcolor(color) end def do_fgcolor(color) end def do_color(color) end def close @f.close end end # Implements a non-interactive controller to control non-interactive # visualizers (i.e. those that are used for converting TPP source code into # another format) class ConversionController < TppController def initialize(input,output,visualizer_class) parser = FileParser.new(input) @pages = parser.get_pages @vis = visualizer_class.new(output) end def run @pages.each do |p| begin line = p.next_line eop = p.eop? @vis.visualize(line,eop) end while not eop end end def close @vis.close end end # Prints a nicely formatted usage message. def usage $stderr.puts "usage: #{$0} [-t -o ] \n" $stderr.puts "\t -t \tset filetype as output format" $stderr.puts "\t -o \twrite output to file " $stderr.puts "\t -s \twait seconds between slides (with -t autoplay)" $stderr.puts "\t --version\tprint the version" $stderr.puts "\t --help\t\tprint this help" $stderr.puts "\n\t currently available types: ncurses (default), autoplay, latex, txt" Kernel.exit(1) end ################################ # Here starts the main program # ################################ input = nil output = nil type = "ncurses" time = 1 skip_next = false ARGV.each_index do |i| if skip_next then skip_next = false else if ARGV[i] == '-v' or ARGV[i] == '--version' then printf "tpp - text presentation program %s\n", version_number Kernel.exit(1) elsif ARGV[i] == '-h' or ARGV[i] == '--help' then usage elsif ARGV[i] == '-t' then type = ARGV[i+1] skip_next = true elsif ARGV[i] == '-o' then output = ARGV[i+1] skip_next = true elsif ARGV[i] == "-s" then time = ARGV[i+1].to_i skip_next = true elsif input == nil then input = ARGV[i] end if output!=nil and output==input then $stderr.puts "Don't use the input file name as the output filename to prevent overwriting it. \n" Kernel.exit(1) end end end if input == nil then usage end ctrl = nil case type when "ncurses" load_ncurses ctrl = InteractiveController.new(input,NcursesVisualizer) when "autoplay" load_ncurses ctrl = AutoplayController.new(input,time,NcursesVisualizer) when "txt" if output == nil then usage else ctrl = ConversionController.new(input,output,TextVisualizer) end when "latex" if output == nil then usage else ctrl = ConversionController.new(input,output,LatexVisualizer) end else usage end # case ctrl.run ctrl.close