#! /usr/local/bin/python

# www13.py -- display the contents of a URL in a Text widget
# - set window title
# - make window resizable
# - update display while reading
# - vertical scroll bar
# - rewritten as class
# - editable url entry and reload button
# - error dialog
# - menu bar; added 'master' option to constructor
# - Added HTML parser

import sys
import urllib
from Tkinter import *
import Dialog
import tkfmt
import htmllib

def main():
	if len(sys.argv) != 2 or sys.argv[1][:1] == '-':
		print "Usage:", sys.argv[0], "url"
		sys.exit(2)
	url = sys.argv[1]
	tk = Tk()
	tk.withdraw()
	viewer = Viewer(tk)
	viewer.load(url)
	viewer.go()

class Viewer:

	def __init__(self, master = None):
		# Create root window
		if master is None:
			self.root = self.master = Tk()
		else:
			self.master = master
			self.root = Toplevel(self.master)
		self.root.minsize(1, 1)

		# Create menu bar
		self.mbar = Frame(self.root,
				  {'relief': 'raised',
				   'border': 2})
		self.mbar.pack({'fill': 'x'})

		# Create File menu
		self.filebutton = Menubutton(self.mbar, {'text': 'File'})
		self.filebutton.pack({'side': 'left'})

		self.filemenu = Menu(self.filebutton)
		self.filebutton['menu'] = self.filemenu

		# Create Edit menu
		self.editbutton = Menubutton(self.mbar, {'text': 'Edit'})
		self.editbutton.pack({'side': 'left'})

		self.editmenu = Menu(self.editbutton)
		self.editbutton['menu'] = self.editmenu

		# Magic so you can swipe from one button to the next
		self.mbar.tk_menuBar(self.filebutton, self.editbutton)

		# Populate File menu
		self.filemenu.add('command', {'label': 'New',
					      'command': self.new_command})
		self.filemenu.add('command', {'label': 'Open...',
					      'command': self.open_command})
		self.filemenu.add('command', {'label': 'Clone',
					      'command': self.clone_command})
		self.filemenu.add('separator')
		self.filemenu.add('command', {'label': 'Close',
					      'command': self.close_command})
		self.filemenu.add('command', {'label': 'Quit',
					      'command': self.quit_command})

		# Populate Edit menu
		pass

		# Create topframe for the entry and button
		self.topframe = Frame(self.root)
		self.topframe.pack({'fill': 'x'})

		# Create a label in front of the entry
		self.urllabel = Label(self.topframe, {'text': 'URL:'})
		self.urllabel.pack({'side': 'left'})

		# Create the entry containing the URL
		self.entry = Entry(self.topframe,
				   {'relief': 'sunken', 'border': 2})
		self.entry.pack({'side': 'left', 'fill': 'x', 'expand': 1})
		self.entry.bind('<Return>', self.loadit)

		# Create the button
		self.reload = Button(self.topframe,
				     {'text': 'Reload',
				      'command': self.reload})
		self.reload.pack({'side': 'right'})

		# Create botframe for the text and scrollbar
		self.botframe = Frame(self.root)
		self.botframe.pack({'fill': 'both', 'expand': 1})

		# The Scrollbar *must* be created first
		self.vbar = Scrollbar(self.botframe)
		self.vbar.pack({'fill': 'y', 'side': 'right'})
		self.text = Text(self.botframe)
		self.text.pack({'expand': 1, 'fill': 'both', 'side': 'left'})

		# Link Text widget and Scrollbar
		self.text['yscrollcommand'] = (self.vbar, 'set')
		self.vbar['command'] = (self.text, 'yview')

		self.url = None

	def load(self, url):
		# Load a new URL into the window
		fp, url = self.urlopen(url)
		if not fp:
			return

		self.url = url

		self.root.title(url)

		self.entry.delete('0', 'end')
		self.entry.insert('end', url)

		self.text.delete('0.0', 'end')

		f = tkfmt.TkFormatter(self.text)
		p = htmllib.FormattingParser(f, htmllib.X11Stylesheet)

		while 1:
			line = fp.readline()
			if not line: break
			if line[-2:] == '\r\n': line = line[:-2] + '\n'
			p.feed(line)
			self.root.update_idletasks()

		p.close()

		fp.close()

	def urlopen(self, url):
		# Open a URL --
		# return (fp, url) if successful
		# display dialog and return (None, url) for errors
		try:
			fp = urllib.urlopen(url)
		except IOError, msg:
			import types
			if type(msg) == types.TupleType and len(msg) == 4:
				if msg[1] == 302:
					m = msg[3]
					if m.has_key('location'):
						url = m['location']
						return self.urlopen(url)
					elif m.has_key('uri'):
						url = m['uri']
						return self.urlopen(url)
			self.errordialog(IOError, msg)
			fp = None
		return fp, url

	def errordialog(self, exc, msg):
		# Display an error dialog -- return when the user clicks OK
		Dialog.Dialog(self.root, {
			'text': str(msg),
			'title': exc,
			'bitmap': 'error',
			'default': 0,
			'strings': ('OK',),
			})

	def go(self):
		# Start Tk main loop
		self.root.mainloop()

	def reload(self, *args):
		# Callback for Reload button
		if self.url:
			self.load(self.url)

	def loadit(self, *args):
		# Callback for <Return> event in entry
		self.load(self.entry.get())

	def new_command(self):
		# File/New...
		Viewer(self.master)

	def clone_command(self):
		# File/Clone
		v = Viewer(self.master)
		v.load(self.url)

	def open_command(self):
		# File/Open...
		print "File/Open...: Not implemented"

	def close_command(self):
		# File/Close
		self.destroy()

	def quit_command(self):
		# File/Quit
		self.root.quit()

	def destroy(self):
		# Destroy this window
		self.root.destroy()
		if self.master is not self.root and not self.master.children:
			self.master.quit()

main()
