| 
									
										
										
										
											1991-05-14 12:25:35 +00:00
										 |  |  | # 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. | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											1991-12-26 13:03:52 +00:00
										 |  |  | class formatter: | 
					
						
							| 
									
										
										
										
											1991-05-14 12:25:35 +00:00
										 |  |  | 	# | 
					
						
							|  |  |  | 	# 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 | 
					
						
							| 
									
										
										
										
											1991-08-16 13:24:20 +00:00
										 |  |  | 		self.setfont('')	# Default font | 
					
						
							| 
									
										
										
										
											1991-05-14 12:25:35 +00:00
										 |  |  | 		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. | 
					
						
							|  |  |  | 	# | 
					
						
							| 
									
										
										
										
											1991-08-16 13:24:20 +00:00
										 |  |  | 	def vspace(self, lines): | 
					
						
							|  |  |  | 		self.vspacepixels(int(lines * self.d.lineheight())) | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	# Add vertical space given in pixels. | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def vspacepixels(self, dv): | 
					
						
							| 
									
										
										
										
											1991-05-14 12:25:35 +00:00
										 |  |  | 		self.flush() | 
					
						
							| 
									
										
										
										
											1991-08-16 13:24:20 +00:00
										 |  |  | 		self.v = self.v + dv | 
					
						
							| 
									
										
										
										
											1991-05-14 12:25:35 +00:00
										 |  |  | 	# | 
					
						
							|  |  |  | 	# 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(): | 
					
						
							| 
									
										
										
										
											1991-08-16 13:24:20 +00:00
										 |  |  | 	import stdwin, stdwinq | 
					
						
							| 
									
										
										
										
											1991-05-14 12:25:35 +00:00
										 |  |  | 	from stdwinevents import * | 
					
						
							|  |  |  | 	try: | 
					
						
							|  |  |  | 		import mac | 
					
						
							|  |  |  | 		# Mac font assignments: | 
					
						
							|  |  |  | 		font1 = 'times', '', 12 | 
					
						
							|  |  |  | 		font2 = 'times', 'b', 14 | 
					
						
							| 
									
										
										
										
											1991-12-26 13:03:52 +00:00
										 |  |  | 	except ImportError: | 
					
						
							| 
									
										
										
										
											1991-05-14 12:25:35 +00:00
										 |  |  | 		# 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: | 
					
						
							| 
									
										
										
										
											1991-08-16 13:24:20 +00:00
										 |  |  | 		type, window, detail = stdwinq.getevent() | 
					
						
							| 
									
										
										
										
											1991-05-14 12:25:35 +00:00
										 |  |  | 		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) |