Initial revision
diff --git a/Lib/lib-stdwin/formatter.py b/Lib/lib-stdwin/formatter.py
new file mode 100644
index 0000000..9bfbe6b
--- /dev/null
+++ b/Lib/lib-stdwin/formatter.py
@@ -0,0 +1,204 @@
+# A class to help applications that do fancy text formatting.
+# You create an instance each time you must redraw the window.
+# Set the initial left, top and right coordinates;
+# then feed it words, font changes and vertical movements.
+#
+# This class should eventually be extended to support much fancier
+# formatting, along the lines of TeX; for now, a very simple model
+# is sufficient.
+#
+class formatter():
+	#
+	# Initialize a formatter instance.
+	# Pass the window's drawing object, and left, top, right
+	# coordinates of the drawing space as arguments.
+	#
+	def init(self, (d, left, top, right)):
+		self.d = d		# Drawing object
+		self.left = left	# Left margin
+		self.right = right	# Right margin
+		self.v = top		# Top of current line
+		self.center = 0
+		self.justify = 1
+		self.setfont('')	# Current font
+		self._reset()		# Prepare for new line
+		return self
+	#
+	# Reset for start of fresh line.
+	#
+	def _reset(self):
+		self.boxes = []		# Boxes and glue still to be output
+		self.sum_width = 0	# Total width of boxes
+		self.sum_space = 0	# Total space between boxes
+		self.sum_stretch = 0	# Total stretch for space between boxes
+		self.max_ascent = 0	# Max ascent of current line
+		self.max_descent = 0	# Max descent of current line
+		self.avail_width = self.right - self.left
+		self.hang_indent = 0
+	#
+	# Set the current font, and compute some values from it.
+	#
+	def setfont(self, font):
+		self.font = font
+		self.d.setfont(font)
+		self.font_space = self.d.textwidth(' ')
+		self.font_ascent = self.d.baseline()
+		self.font_descent = self.d.lineheight() - self.font_ascent
+	#
+	# Add a word to the list of boxes; first flush if line is full.
+	# Space and stretch factors are expressed in fractions
+	# of the current font's space width.
+	# (Two variations: one without, one with explicit stretch factor.)
+	#
+	def addword(self, (word, spacefactor)):
+		self.addwordstretch(word, spacefactor, spacefactor)
+	#
+	def addwordstretch(self, (word, spacefactor, stretchfactor)):
+		width = self.d.textwidth(word)
+		if width > self.avail_width:
+			self._flush(1)
+		space = int(float(self.font_space) * float(spacefactor))
+		stretch = int(float(self.font_space) * float(stretchfactor))
+		box = (self.font, word, width, space, stretch)
+		self.boxes.append(box)
+		self.sum_width = self.sum_width + width
+		self.sum_space = self.sum_space + space
+		self.sum_stretch = self.sum_stretch + stretch
+		self.max_ascent = max(self.font_ascent, self.max_ascent)
+		self.max_descent = max(self.font_descent, self.max_descent)
+		self.avail_width = self.avail_width - width - space
+	#
+	# Flush current line and start a new one.
+	# Flushing twice is harmless (i.e. does not introduce a blank line).
+	# (Two versions: the internal one has a parameter for justification.)
+	#
+	def flush(self):
+		self._flush(0)
+	#
+	def _flush(self, justify):
+		if not self.boxes:
+			return
+		#
+		# Compute amount of stretch needed.
+		#
+		if justify and self.justify or self.center:
+			#
+			# Compute extra space to fill;
+			# this is avail_width plus glue from last box.
+			# Also compute available stretch.
+			#
+			last_box = self.boxes[len(self.boxes)-1]
+			font, word, width, space, stretch = last_box
+			tot_extra = self.avail_width + space
+			tot_stretch = self.sum_stretch - stretch
+		else:
+			tot_extra = tot_stretch = 0
+		#
+		# Output the boxes.
+		#
+		baseline = self.v + self.max_ascent
+		h = self.left + self.hang_indent
+		if self.center:
+			h = h + tot_extra / 2
+			tot_extra = tot_stretch = 0
+		for font, word, width, space, stretch in self.boxes:
+			self.d.setfont(font)
+			v = baseline - self.d.baseline()
+			self.d.text((h, v), word)
+			h = h + width + space
+			if tot_extra > 0 and tot_stretch > 0:
+				extra = stretch * tot_extra / tot_stretch
+				h = h + extra
+				tot_extra = tot_extra - extra
+				tot_stretch = tot_stretch - stretch
+		#
+		# Prepare for next line.
+		#
+		self.v = baseline + self.max_descent
+		self.d.setfont(self.font)
+		self._reset()
+	#
+	# Add vertical space; first flush.
+	# Vertical space is expressed in fractions of the current
+	# font's line height.
+	#
+	def vspace(self, dy):
+		self.flush()
+		dy = int(float(dy) * float(self.d.lineheight()))
+		self.v = self.v + dy
+	#
+	# Set temporary (hanging) indent, for paragraph start.
+	# First flush.
+	#
+	def tempindent(self, space):
+		self.flush()
+		hang = int(float(self.font_space) * float(space))
+		self.hang_indent = hang
+		self.avail_width = self.avail_width - hang
+	#
+	# Add (permanent) left indentation.  First flush.
+	#
+	def addleftindent(self, space):
+		self.flush()
+		self.left = self.left \
+			+ int(float(self.font_space) * float(space))
+		self._reset()
+	#
+
+
+# Test procedure
+#
+def test():
+	import stdwin
+	from stdwinevents import *
+	try:
+		import mac
+		# Mac font assignments:
+		font1 = 'times', '', 12
+		font2 = 'times', 'b', 14
+	except NameError:
+		# X11R4 font assignments
+		font1 = '*times-medium-r-*-120-*'
+		font2 = '*times-bold-r-*-140-*'
+	words = \
+	    ['The','quick','brown','fox','jumps','over','the','lazy','dog.']
+	words = words * 2
+	stage = 0
+	stages = [(0,0,'ragged'), (1,0,'justified'), (0,1,'centered')]
+	justify, center, title = stages[stage]
+	stdwin.setdefwinsize(300,200)
+	w = stdwin.open(title)
+	winsize = w.getwinsize()
+	while 1:
+		type, window, detail = stdwin.getevent()
+		if type = WE_CLOSE:
+			break
+		elif type = WE_SIZE:
+			newsize = w.getwinsize()
+			if newsize <> winsize:
+				w.change((0,0), winsize)
+				winsize = newsize
+				w.change((0,0), winsize)
+		elif type = WE_MOUSE_DOWN:
+			stage = (stage + 1) % len(stages)
+			justify, center, title = stages[stage]
+			w.settitle(title)
+			w.change((0, 0), (1000, 1000))
+		elif type = WE_DRAW:
+			width, height = winsize
+			f = formatter().init(w.begindrawing(), 0, 0, width)
+			f.center = center
+			f.justify = justify
+			if not center:
+				f.tempindent(5)
+			for font in font1, font2, font1:
+				f.setfont(font)
+				for word in words:
+					space = 1 + (word[-1:] = '.')
+					f.addword(word, space)
+					if center and space > 1:
+						f.flush()
+			f.flush()
+			height = f.v
+			del f
+			w.setdocsize(0, height)
diff --git a/Lib/stdwin/formatter.py b/Lib/stdwin/formatter.py
new file mode 100755
index 0000000..9bfbe6b
--- /dev/null
+++ b/Lib/stdwin/formatter.py
@@ -0,0 +1,204 @@
+# A class to help applications that do fancy text formatting.
+# You create an instance each time you must redraw the window.
+# Set the initial left, top and right coordinates;
+# then feed it words, font changes and vertical movements.
+#
+# This class should eventually be extended to support much fancier
+# formatting, along the lines of TeX; for now, a very simple model
+# is sufficient.
+#
+class formatter():
+	#
+	# Initialize a formatter instance.
+	# Pass the window's drawing object, and left, top, right
+	# coordinates of the drawing space as arguments.
+	#
+	def init(self, (d, left, top, right)):
+		self.d = d		# Drawing object
+		self.left = left	# Left margin
+		self.right = right	# Right margin
+		self.v = top		# Top of current line
+		self.center = 0
+		self.justify = 1
+		self.setfont('')	# Current font
+		self._reset()		# Prepare for new line
+		return self
+	#
+	# Reset for start of fresh line.
+	#
+	def _reset(self):
+		self.boxes = []		# Boxes and glue still to be output
+		self.sum_width = 0	# Total width of boxes
+		self.sum_space = 0	# Total space between boxes
+		self.sum_stretch = 0	# Total stretch for space between boxes
+		self.max_ascent = 0	# Max ascent of current line
+		self.max_descent = 0	# Max descent of current line
+		self.avail_width = self.right - self.left
+		self.hang_indent = 0
+	#
+	# Set the current font, and compute some values from it.
+	#
+	def setfont(self, font):
+		self.font = font
+		self.d.setfont(font)
+		self.font_space = self.d.textwidth(' ')
+		self.font_ascent = self.d.baseline()
+		self.font_descent = self.d.lineheight() - self.font_ascent
+	#
+	# Add a word to the list of boxes; first flush if line is full.
+	# Space and stretch factors are expressed in fractions
+	# of the current font's space width.
+	# (Two variations: one without, one with explicit stretch factor.)
+	#
+	def addword(self, (word, spacefactor)):
+		self.addwordstretch(word, spacefactor, spacefactor)
+	#
+	def addwordstretch(self, (word, spacefactor, stretchfactor)):
+		width = self.d.textwidth(word)
+		if width > self.avail_width:
+			self._flush(1)
+		space = int(float(self.font_space) * float(spacefactor))
+		stretch = int(float(self.font_space) * float(stretchfactor))
+		box = (self.font, word, width, space, stretch)
+		self.boxes.append(box)
+		self.sum_width = self.sum_width + width
+		self.sum_space = self.sum_space + space
+		self.sum_stretch = self.sum_stretch + stretch
+		self.max_ascent = max(self.font_ascent, self.max_ascent)
+		self.max_descent = max(self.font_descent, self.max_descent)
+		self.avail_width = self.avail_width - width - space
+	#
+	# Flush current line and start a new one.
+	# Flushing twice is harmless (i.e. does not introduce a blank line).
+	# (Two versions: the internal one has a parameter for justification.)
+	#
+	def flush(self):
+		self._flush(0)
+	#
+	def _flush(self, justify):
+		if not self.boxes:
+			return
+		#
+		# Compute amount of stretch needed.
+		#
+		if justify and self.justify or self.center:
+			#
+			# Compute extra space to fill;
+			# this is avail_width plus glue from last box.
+			# Also compute available stretch.
+			#
+			last_box = self.boxes[len(self.boxes)-1]
+			font, word, width, space, stretch = last_box
+			tot_extra = self.avail_width + space
+			tot_stretch = self.sum_stretch - stretch
+		else:
+			tot_extra = tot_stretch = 0
+		#
+		# Output the boxes.
+		#
+		baseline = self.v + self.max_ascent
+		h = self.left + self.hang_indent
+		if self.center:
+			h = h + tot_extra / 2
+			tot_extra = tot_stretch = 0
+		for font, word, width, space, stretch in self.boxes:
+			self.d.setfont(font)
+			v = baseline - self.d.baseline()
+			self.d.text((h, v), word)
+			h = h + width + space
+			if tot_extra > 0 and tot_stretch > 0:
+				extra = stretch * tot_extra / tot_stretch
+				h = h + extra
+				tot_extra = tot_extra - extra
+				tot_stretch = tot_stretch - stretch
+		#
+		# Prepare for next line.
+		#
+		self.v = baseline + self.max_descent
+		self.d.setfont(self.font)
+		self._reset()
+	#
+	# Add vertical space; first flush.
+	# Vertical space is expressed in fractions of the current
+	# font's line height.
+	#
+	def vspace(self, dy):
+		self.flush()
+		dy = int(float(dy) * float(self.d.lineheight()))
+		self.v = self.v + dy
+	#
+	# Set temporary (hanging) indent, for paragraph start.
+	# First flush.
+	#
+	def tempindent(self, space):
+		self.flush()
+		hang = int(float(self.font_space) * float(space))
+		self.hang_indent = hang
+		self.avail_width = self.avail_width - hang
+	#
+	# Add (permanent) left indentation.  First flush.
+	#
+	def addleftindent(self, space):
+		self.flush()
+		self.left = self.left \
+			+ int(float(self.font_space) * float(space))
+		self._reset()
+	#
+
+
+# Test procedure
+#
+def test():
+	import stdwin
+	from stdwinevents import *
+	try:
+		import mac
+		# Mac font assignments:
+		font1 = 'times', '', 12
+		font2 = 'times', 'b', 14
+	except NameError:
+		# X11R4 font assignments
+		font1 = '*times-medium-r-*-120-*'
+		font2 = '*times-bold-r-*-140-*'
+	words = \
+	    ['The','quick','brown','fox','jumps','over','the','lazy','dog.']
+	words = words * 2
+	stage = 0
+	stages = [(0,0,'ragged'), (1,0,'justified'), (0,1,'centered')]
+	justify, center, title = stages[stage]
+	stdwin.setdefwinsize(300,200)
+	w = stdwin.open(title)
+	winsize = w.getwinsize()
+	while 1:
+		type, window, detail = stdwin.getevent()
+		if type = WE_CLOSE:
+			break
+		elif type = WE_SIZE:
+			newsize = w.getwinsize()
+			if newsize <> winsize:
+				w.change((0,0), winsize)
+				winsize = newsize
+				w.change((0,0), winsize)
+		elif type = WE_MOUSE_DOWN:
+			stage = (stage + 1) % len(stages)
+			justify, center, title = stages[stage]
+			w.settitle(title)
+			w.change((0, 0), (1000, 1000))
+		elif type = WE_DRAW:
+			width, height = winsize
+			f = formatter().init(w.begindrawing(), 0, 0, width)
+			f.center = center
+			f.justify = justify
+			if not center:
+				f.tempindent(5)
+			for font in font1, font2, font1:
+				f.setfont(font)
+				for word in words:
+					space = 1 + (word[-1:] = '.')
+					f.addword(word, space)
+					if center and space > 1:
+						f.flush()
+			f.flush()
+			height = f.v
+			del f
+			w.setdocsize(0, height)