mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			208 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			208 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# 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('')	# Default 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, lines):
 | 
						|
		self.vspacepixels(int(lines * self.d.lineheight()))
 | 
						|
	#
 | 
						|
	# Add vertical space given in pixels.
 | 
						|
	#
 | 
						|
	def vspacepixels(self, dv):
 | 
						|
		self.flush()
 | 
						|
		self.v = self.v + dv
 | 
						|
	#
 | 
						|
	# 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, stdwinq
 | 
						|
	from stdwinevents import *
 | 
						|
	try:
 | 
						|
		import mac
 | 
						|
		# Mac font assignments:
 | 
						|
		font1 = 'times', '', 12
 | 
						|
		font2 = 'times', 'b', 14
 | 
						|
	except ImportError:
 | 
						|
		# 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 = stdwinq.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)
 |