# Based on iwidgets2.2.0/scrolledtext.itk code.   

import Tkinter
import Pmw

class ScrolledText(Pmw.MegaWidget):
    def __init__(self, parent = None, **kw):

	# Define the megawidget options.
	INITOPT = Pmw.INITOPT
	optiondefs = (
	    ('labelmargin',    0,            INITOPT),
	    ('labelpos',       None,         INITOPT),
	    ('scrollmargin',   2,            INITOPT),
	    ('vscrollmode',   'dynamic',     self._vscrollMode),
	    ('hscrollmode',   'dynamic',     self._hscrollMode),
	)
	self.defineoptions(kw, optiondefs)

	# Initialise the base class (after defining the options).
	Pmw.MegaWidget.__init__(self, parent)

	# Create the components.
	interior = self.interior()

	# Create the text widget.
	self._textbox = self.createcomponent('text',
		(), None,
		Tkinter.Text, (interior,),
		xscrollcommand=self._scrollTextX,
		yscrollcommand=self._scrollTextY)
	self._textbox.grid(row = 2, column = 2, sticky = 'news')
	interior.grid_rowconfigure(2, weight = 1, minsize = 0)
	interior.grid_columnconfigure(2, weight = 1, minsize = 0)

	# Create the vertical scrollbar
	self._vertScrollbar = self.createcomponent('vertscrollbar',
		(), 'Scrollbar',
		Tkinter.Scrollbar, (interior,),
		orient='vertical', command=self._textbox.yview)

	# Create the horizontal scrollbar
	self._horizScrollbar = self.createcomponent('horizscrollbar',
		(), 'Scrollbar',
		Tkinter.Scrollbar, (interior,),
	        orient='horizontal', command=self._textbox.xview)

	self.createlabel(interior, childCols = 3, childRows = 3)

	# Initialise instance variables.
	self._vertMode = None
	self._horizMode = None
	self._horizRecurse = 0

	# Check keywords and initialise options.
	self.initialiseoptions(ScrolledText)

    # ======================================================================

    # Configuration methods.

    def _vscrollMode(self):
	mode = self['vscrollmode']

	if mode == 'static':
	    self._vertScrollbarDisplay(1)
	elif mode == 'dynamic' or mode == 'none':
	    self._vertScrollbarDisplay(0)
	else:
	    message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode
	    raise ValueError, message

    def _hscrollMode(self):
	mode = self['hscrollmode']

	if mode == 'static':
	    self._horizScrollbarDisplay(1)
	elif mode == 'dynamic' or mode == 'none':
	    self._horizScrollbarDisplay(0)
	else:
	    message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode
	    raise ValueError, message

    # ======================================================================

    # Public methods.

    def clear(self):
	self._textbox.delete('1.0', 'end')

    def importfile(self, fileName, where = 'end'):
	file = open(fileName, 'r')
	self._textbox.insert(where, file.read())
	file.close()

    def exportfile(self, fileName):
	file = open(fileName, 'w')
	file.write(self._textbox.get('1.0', 'end'))
	file.close()

    def settext(self, text):
	disabled = (self._textbox.cget('state') == 'disabled')
	if disabled:
	    self._textbox.configure(state='normal')
	self._textbox.delete('0.0', 'end')
	self._textbox.insert('end', text)
	if disabled:
	    self._textbox.configure(state='disabled')

    # Override Tkinter.Text get method, so that if it is called with
    # no arguments, return all text (consistent with other widgets).
    def get(self, first=None, last=None):
	if not first and not last:
	    return self._textbox.get('1.0', 'end')
	else:
	    return self._textbox.get(first, last)


    # ======================================================================

    # Private methods.

    def _vertScrollbarDisplay(self, mode):
	if mode != self._vertMode:
	    self._vertMode = mode

	    margin = self['scrollmargin']
	    interior = self.interior()

	    if self._vertMode:
		self._vertScrollbar.grid(row = 2, column = 4, sticky = 'news')
		interior.grid_columnconfigure(3, minsize = margin)
	    else:
		self._vertScrollbar.grid_forget()
		interior.grid_columnconfigure(3, minsize = 0)

    def _horizScrollbarDisplay(self, mode):
	if mode != self._horizMode:
	    self._horizMode = mode

	    margin = self['scrollmargin']
	    interior = self.interior()

	    if self._horizMode:
		self._horizScrollbar.grid(row = 4, column = 2, sticky = 'news')
		interior.grid_rowconfigure(3, minsize = margin)
	    else:
		self._horizScrollbar.grid_forget()
		interior.grid_rowconfigure(3, minsize = 0)

    def _scrollTextX(self, first, last):
	self._horizScrollbar.set(first, last)

	# Infinite recursion must be avoided for dynamic horizontal scrollbars.
	# A max depth of 1 is allowed for hiding the scrollbar, 2 for showing
	# it, so that showing always 'wins'!
	if self['hscrollmode'] == 'dynamic':
	    if first == '0' and last == '1':
		if self._horizMode:
		    if self._horizRecurse < 2:
			self._horizRecurse = self._horizRecurse + 1
			self._horizScrollbarDisplay(0)
			self.update_idletasks()
			self._horizRecurse = self._horizRecurse - 1
	    else:
		if not self._horizMode:
		    if self._horizRecurse < 3:
			self._horizRecurse = self._horizRecurse + 1
			self._horizScrollbarDisplay(1)
			self.update_idletasks()
			self._horizRecurse = self._horizRecurse - 1

    def _scrollTextY(self, first, last):
	# The text widget calls this just after it has been created,
	# with arguments ('0', '0').
	if first == '0' and last == '0':
	    return
	self._vertScrollbar.set(first, last)

	if self['vscrollmode'] == 'dynamic':
	    if first == '0' and last == '1':
		self._vertScrollbarDisplay(0)
	    else:
		self._vertScrollbarDisplay(1)

    # Need to explicitly forward this to override the stupid
    # (grid_)bbox method inherited from Tkinter.Frame.Grid.
    def bbox(self, index):
	return self._textbox.bbox(index)

Pmw.forwardmethods(ScrolledText, Tkinter.Text, '_textbox')