#!/usr/bin/python

#      Ubuntu tutor; a program to introduce new users to Ubuntu.
#      Copyright (C) 2005  Michael Sheldon (mts4 at aber dot ac dot uk)       
#                                                                              
#      This program is free software; you can redistribute it and/or           
#      modify it under the terms of the GNU General Public License             
#      as published by the Free Software Foundation; either version 2       
#      of the License, or (at your option) any later version.               
#                                                                           
#      This program is distributed in the hope that it will be useful,      
#      but WITHOUT ANY WARRANTY; without even the implied warranty of       
#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        
#      GNU General Public License for more details.                         
#                                                                           
#      You should have received a copy of the GNU General Public License    
#      along with this program; if not, write to the Free Software          
#      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,           
#      MA  02110-1301, USA                                                  
		  
import pygtk
pygtk.require("2.0")
import gtk, gtk.glade, pango, gobject, libxml2, ogg.vorbis, ao, time
from threading import Thread

class Audio(Thread):
	
	def __init__(self, file, parent):
		Thread.__init__(self)
		self.running = True
		self.dev = ao.AudioDevice('esd')
		self.audiofile = ogg.vorbis.VorbisFile(file)
		self.parent = parent

	def __del__(self):
		self.stop()		

	def stop(self):
		self.running = False

	def run(self):
		while self.running:
			buf, bytes, bit = self.audiofile.read(1024)
			if bytes != 0:
				self.dev.play(buf, bytes)
			else:
				self.running = False


class Tutor(gtk.Window):
	
	def __init__(self, tutorial, base = False):
		self.mainGlade = gtk.glade.XML("ubuntu-tutor.glade", "winMain", None)
																
		signals = {
			"on_winMain_destroy" : self.quit, 
			"on_btnNext_clicked" : self.next,
			"on_btnPrev_clicked" : self.prev,
			"on_drwOutput_expose_event" : self.expose_event,
			"on_evnDraw_button_press_event" : self.draw_clicked,
		}

		self.mainGlade.signal_autoconnect(signals)
		self.audio = None
		self.load_tutorial(tutorial)
		self.show_page(0)
		self.base = base
	
	def quit(self, widget):
		if self.audio:
			self.audio.stop()
		if self.base:
			gtk.main_quit()

	def next(self, widget=None):
		self.show_page(self.page_number + 1)

	def prev(self, widget=None):
		self.show_page(self.page_number - 1)

	def load_tutorial(self, file):
		self.tutorial = []
		try:
			reader = libxml2.newTextReaderFilename(file)
		except:
			print "Unable to open " + file
			return
			
		ret = reader.Read()
		currentElem = None
		page = {}
		while ret == 1:
			if reader.NodeType() == 1 and reader.Name() == "page": #<page>
				page = {}
				page["links"] = []
			
			elif reader.NodeType() == 15 and reader.Name() == "page": #</page>
				self.tutorial.append(page)
			
			elif reader.NodeType() == 1:
				currentElem = {}
				currentElem["name"] = reader.Name()
				if reader.NodeType() == 1:
				                while reader.MoveToNextAttribute():
							currentElem[reader.Name()] = reader.Value()

			elif reader.NodeType() == 3 and reader.Name() == "#text" and currentElem:
					currentElem["value"] = reader.Value()

			elif reader.NodeType() == 15 and reader.Depth() == 2 and currentElem:
				if currentElem["name"] == "link":
					page["links"].append(currentElem)
				else:
					page[currentElem["name"]] = currentElem
				currentElem = None
			elif reader.NodeType() == 14 and currentElem: #handle <cursor /> as well as <cursor></cursor>
				page[currentElem["name"]] = currentElem
				currentElem = None
			
			ret = reader.Read()

		if ret != 0:
			print "Failed to parse " + file

	def show_page(self, page_number):
		try:
			page = self.tutorial[page_number]
		except:
			print "No such page"
			return

		self.page_number = page_number	
		btnPrev = self.mainGlade.get_widget("btnPrev")
		btnNext = self.mainGlade.get_widget("btnNext")
		if page_number > 0:
			btnPrev.set_sensitive(1)
		else:
			btnPrev.set_sensitive(0)

		if page_number < len(self.tutorial) - 1:
			btnNext.set_sensitive(1)
		else:
			btnNext.set_sensitive(0)

		if page_number == 0 and len(self.tutorial) == 1:
			btnNext.hide()
			btnPrev.hide()
		else:
			btnNext.show()
			btnPrev.show()
			
	
		txtOutput = self.mainGlade.get_widget("txtOuput")
		bufOutput = txtOutput.get_buffer()
		scrText = self.mainGlade.get_widget("scrText")
		try:
			bufOutput.set_text(page["text"]["value"])
			scrText.show()
		except:
			scrText.hide()
			winMain = self.mainGlade.get_widget("winMain")
			winMain.resize(1, 1) #Set minimum size


		drwOutput = self.mainGlade.get_widget("drwOutput")
		try:
			drwOutput.image = gtk.gdk.pixbuf_new_from_file(page["image"]["value"])
			width, height = drwOutput.size_request()
			if drwOutput.image.get_width() != width or drwOutput.image.get_width() != height:
				drwOutput.image = drwOutput.image.scale_simple(width, height, gtk.gdk.INTERP_BILINEAR)
			drwOutput.show()
		except:
			drwOutput.image = None
			drwOutput.hide()
		self.gc = drwOutput.window.new_gc()

		drwOutput.links = []
		pc = drwOutput.create_pango_context()
		for link in page["links"]:
			try:
				link["x"] = int(link["x"])
				link["y"] = int(link["y"])
				link["pango"] = pango.Layout(pc)
				link["pango"].set_text(link["title"])
				if link.has_key("colour"):
					link["colour"] = gtk.gdk.color_parse(link["colour"])
				else:
					link["colour"] = gtk.gdk_color_parse("#000000")
				drwOutput.links.append(link)
			except:
				pass
		try:
			cursor = {}
			cursor["image"] = gtk.gdk.pixbuf_new_from_file("cursor.png")
			cursor["x1"] = int(page["cursor"]["x1"])
			cursor["y1"] = int(page["cursor"]["y1"])
			cursor["x2"] = int(page["cursor"]["x2"])
			cursor["y2"] = int(page["cursor"]["y2"])
			cursor["x"] = int(page["cursor"]["x1"]) #Working x,y values for interpolated results
			cursor["y"] = int(page["cursor"]["y1"])
			cursor["speed"] = float(page["cursor"]["speed"]) / 100
			gobject.timeout_add(33, self.cursor_update, drwOutput)
			drwOutput.cursor = cursor
		except:
			drwOutput.cursor = None
		self.expose_event(drwOutput)

		try:
			if self.audio:
				self.audio.stop()
			self.audio = Audio(page["audio"]["value"], self)
			self.audio.start()
		except:
			if self.audio:
				self.audio.stop()

		try:
			if page_number < len(self.tutorial) - 1: #Don't try and go forward on last page
				secs = int(page["time"]["value"]) * 1000
				gobject.timeout_add(secs, self.next)
		except:
			pass
		
		
	def expose_event(self, widget, event = None):
		if event:
			top, left, width, height = event.area
		else:
			top, left, width, height = 0, 0, -1, -1
		self.draw_image(widget, top, left, width, height)

	def draw_image(self, widget, top, left, width, height):
		if widget.image:
			if top < 0:	top = 0
			if left < 0:	left = 0
			widget.window.draw_pixbuf(self.gc, widget.image, top, left, top, left, width, height)
			for link in widget.links:
				widget.window.draw_layout(self.gc, link["x"], link["y"], link["pango"], link["colour"])
			if widget.cursor:
				cursor = widget.cursor
				widget.window.draw_pixbuf(self.gc, cursor["image"], 0, 0, cursor["x"], cursor["y"])

	def cursor_update(self, widget):
		if widget.cursor:
			cursor = widget.cursor
			if not cursor.has_key("interp"):
				cursor["interp"] = 0
			cursor["x"] = int(cursor["x1"] + (cursor["x2"] - cursor["x1"]) * cursor["interp"])
			cursor["y"] = int(cursor["y1"] + (cursor["y2"] - cursor["y1"]) * cursor["interp"])
			cursor["interp"] += cursor["speed"]
			self.draw_image(widget, cursor["x"] - 32, cursor["y"] - 32, 64, 64)
			if cursor["interp"] < 1:
				gobject.timeout_add(33, self.cursor_update, widget)

	def draw_clicked(self, widget, event):
		drw = widget.child
		if event.button == 1:
			x = event.x
			y = event.y
			#Find out what link was clicked
			for link in drw.links:
				lw, lh = link["pango"].get_pixel_size()
				lx = link["x"]
				ly = link["y"]
				lx2 = lx + lw
				ly2 = ly + lh
				if x > lx and x < lx2 and y > ly and y < ly2:
					Tutor(link["value"])

app = Tutor("tutorials/start.xml", True)
gtk.threads_init()
gtk.main()

