#!/usr/bin/env ruby

=begin
=Author
 Jeff Clement, Bluesine
 http://www.bluesine.com/archives/software/ruby
=Synopsis
 require "dialogs"
 dlg=Dialogs::Dialog.new("Sample Application")
 dlg.msgbox("Hello World")
 dlg.infobox("Good-bye")
 dlg.clean
 # a complete example is included with distribution.
=Description
Class (({Dialog})) is a simple interface to the Unix Dialog program by Savio Lam and Stuart Herbert.  I think Dialog is an awesome way of building simple user interfaces.  This library is written to look similar to the Python wrapper for Dialog written by Robb Schecter.
=Instance Methods
 --- Dialog#initialize(title)
     Takes one parameter defining the title of the application ( will be on the top of all Dialog windows ).  Also does a quick search to find location of Dialog program.
 --- Dialog#clear
     Clears the screen by calling 'Dialog --clear'.  This will not reset things like the background color.
     
     (({d.clear()}))
 --- Dialog#superclear
     Clears the screen by calling 'Dialog --clear'.  Then calling 'clear' and finally 'stty sane'.  This will make sure the terminal is cleaned up and working.
     
     (({d.superclear()}))
 --- Dialog#msgbox (text, height=10, width=40)
     Display a simple message box of given width and height (in characters) on the screen. 
     
     (({d.msgbox("This is a test")}))
 --- Dialog#yesno (text, height=10, width=40)
     Display a yesno box prompting user with a question.  Returns ((*true*)) if the user selects yes or ((*false*)) otherwise. 

     (({isGreat=d.yesno("Is this great?")}))
 --- Dialog#inputbox (text, init='', height=10, width=40)
     Display a input box with a text prompt given an initial string.  Returns array with [ERRNUM, [USERINPUT]].

     (({name=d.inputbox("What is your name?")[1][0]}))
 --- Dialog#textbox (file, height=20, width=60)
     Display a given file in a textbox.

     (({d.textbox("/etc/passwd")}))
 --- Dialog#menu (text, items, height=15, width=60)
     Display a menu from a list of items.  Will return an array of the form [ERRNUM, CHOOSENOBJECT].  The objects can be of anytype as long as they have a valid to_s method (ie. not limited to strings!)

     (({favColor=d.menu("Favorite color", %w{red green blue orange})[1]}))
 --- Dialog#checklist (text, items, selected=[], height=15, width=60)
     Display a list of items ( of any type ) as a series of checkboxes.  Returns array with [ERRNUM, [CHECKED ITEM1, CHECKED ITEM2...]].  The optional array selected defines default values (any objects which appear in the list are checked).

     (({favColors=d.checklist("Favorite color(s)", %w{red green blue orange},'red')}))
 --- Dialog#radiolist (text, items, selected=nil, height=15, width=60)
     Display a list of items ( of any type ) as a series of radioboxes.  Returns array with [ERRNUM, CHECKEDITEM].  The optional selected value defines the default object (if any).

     (({favColor=d.radiolist("Favorite color", %w{red green blue orange},'red')}))
 --- Dialog#scrollbox(title, text, height=20, width=60)
     Display large multiline messages using a textbox by writing text to a tempfile and calling textbox function.  Requires a title.  If passed an Array it will write each element to it's own line.  Otherwise it will just write to object to the file directly using it's own to_s function.

     (({d.scrollbox("Title", "Hello\nWorld\n123")}))
=ToDo
* implement some reasonable version of the gauge
* better documentation
* proper terminal dimensions
=License
This software is distributed under the Bluesine public license.  See included LICENSE file or ((<URL:http://www.bluesine.com/license>)).
=Version Info
$Id: dialogs.rb,v 1.1 2001/01/01 23:28:35 jsc Exp jsc $
=end

require "tempfile"

module Dialogs

class Dialog

	def perform(cmd)
	options = if /^--title / =~ cmd then "" else "--title '#{@title}'" end
	tf=Tempfile.new("dialog")
	torun=@DIALOG+' '+options+' '+cmd+" 2> #{tf.path}"
	system(torun)
	tf.open
	[$?,tf.readlines()]
	end

	def perform_no_options(cmd)
	torun=@DIALOG+' '+cmd
	system(torun)
	$?
	end

	def checkHW(height, width)
	if height<1 or width<1 or height>@height or width>@width then
		raise "DialogSizeError"
	end
	end

	def msgbox(text, height=10, width=40)
	checkHW(height, width)
	perform "--msgbox '#{text}' #{height} #{width}" 
	end

	def infobox(text, height=10, width=40)
	checkHW(height, width)
	perform "--infobox '#{text}' #{height} #{width}"
	end

	def yesno(text, height=10, width=40)
	checkHW(height, width)
	if perform ("--yesno '#{text}' #{height} #{width} ")[0]==0 then
		true
	else
		false
	end	
	end

	def inputbox(text, init='', height=10, width=40)
	checkHW(height, width)
	perform("--inputbox '#{text}' #{height} #{width} '#{init}'")
	end

	def textbox(file, height=20, width=60)
	checkHW(height, width)
	perform("--title '#{file}' --textbox '#{file}' #{height} #{width}")
	end

	def menu(text, items, height=15, width=60)
	checkHW(height, width)
	choices=""
	cnt=0
	items.each { |choice| choices+=" #{cnt+=1} #{choice}" }
	code, ret = perform "--menu '#{text}' #{height} #{width} #{height-8} #{choices}"
	[code, items[ret[0].to_i-1]]
	end

	def checklist(text, items, selected=[], height=15, width=60)
	checkHW(height, width)
	choices=""
	cnt=0
	items.each { |choice| choices+=" #{cnt+=1} #{choice} "+(selected.index(choice)==nil ? "off" : "on")}
	code, ret = perform "--checklist '#{text}' #{height} #{width} #{height-8} #{choices}"
	out=[]
	if ret[0] != nil then ret[0].split.each{|id| out.push(items[id[1..-1].to_i-1])} end
	[code,out]
	end

	def radiolist(text, items, selected=nil, height=15, width=60)
	checkHW(height, width)
	choices=""
	cnt=0
	items.each { |choice| choices+=" #{cnt+=1} #{choice} "+(selected==choice ? "on" : "off")}
	code, ret = perform "--radiolist '#{text}' #{height} #{width} #{height-8} #{choices}"
	[code,items[ret[0].to_i-1]]
	end

	def scrollbox(title, text, height=20, width=60)
	checkHW(height, width)
	tf=Tempfile.new("scrollbox")
	if text.type==Array then
		text.each { |line| tf.write(line+"\n") }
	else
		tf.write text
	end
	tf.open
	perform("--title '#{title}' --textbox '#{tf.path}' #{height} #{width}")
	end

	def clear
	perform_no_options "--clear"
	end

	def superclear
	perform_no_options "--clear"
	system("clear")
	system("sttysane")
	end
	
	def initialize(title)
	@DIALOG='/usr/bin/env dialog'
	system("#{@DIALOG} 2> /dev/null")
	if $?!=65280 then
		raise "DialogNotFound"
	end
	@title=title
	@height=1000 # I don't know how to determine this yet so this is just a temp value until I figure it out.
	@width=1000
	end

	public :initialize, :msgbox, :infobox, :yesno, :inputbox, :textbox, :menu, :checklist, :radiolist, :scrollbox
	private :perform, :perform_no_options
end

end

# test code
if $0 =~ /dialogs.rb/ then
d=Dialogs::Dialog.new("Test Application")
d.msgbox("Welcome to the rubyDialogs Demo")
q0=d.inputbox("What is your name?")[1][0]
q1=d.yesno("Are you interested in using Dialog with Ruby?")
q2=d.radiolist("What is your favorite OOSL?", %w{Ruby Perl Python}, 'Ruby')[1]
q3=d.checklist("Which editors do you use?",%w{Vi Emacs SciTE Notepad Joe}, ['Vi', 'Emacs'])[1]
q4=d.menu("Which OS do you run?",%w{Linux BSD Windows Mac Other})[1]
results=<<END
RESULTS
===================
0. Your Name
	#{q0}

1. Interested in Ruby and Dialog?
	#{q1 ? "Yes" : "No"}

2. Favorite object-oriented-scripting-language?
	#{q2}

3. Editors of choice?
#{out=""; q3.each{|editor| out+="\t"+editor+"\n"}; out[0..-2]}

4. OS of choice?
	#{q4}

Thank you!
END
d.scrollbox("Test Results", results)
d.infobox("(C)2000 Jeff Clement",3,40)
end

