| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | # Text formatting abstractions | 
					
						
							| 
									
										
										
										
											1995-08-10 18:00:03 +00:00
										 |  |  | # Note -- this module is obsolete, it's too slow anyway | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import string | 
					
						
							|  |  |  | import Para | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # A formatter back-end object has one method that is called by the formatter: | 
					
						
							|  |  |  | # addpara(p), where p is a paragraph object.  For example: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Formatter back-end to do nothing at all with the paragraphs | 
					
						
							|  |  |  | class NullBackEnd: | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def __init__(self): | 
					
						
							|  |  |  | 		pass | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def addpara(self, p): | 
					
						
							|  |  |  | 		pass | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def bgn_anchor(self, id): | 
					
						
							|  |  |  | 		pass | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def end_anchor(self, id): | 
					
						
							|  |  |  | 		pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Formatter back-end to collect the paragraphs in a list | 
					
						
							|  |  |  | class SavingBackEnd(NullBackEnd): | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def __init__(self): | 
					
						
							|  |  |  | 		self.paralist = [] | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def addpara(self, p): | 
					
						
							|  |  |  | 		self.paralist.append(p) | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def hitcheck(self, h, v): | 
					
						
							|  |  |  | 		hits = [] | 
					
						
							|  |  |  | 		for p in self.paralist: | 
					
						
							|  |  |  | 			if p.top <= v <= p.bottom: | 
					
						
							|  |  |  | 				for id in p.hitcheck(h, v): | 
					
						
							|  |  |  | 					if id not in hits: | 
					
						
							|  |  |  | 						hits.append(id) | 
					
						
							|  |  |  | 		return hits | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def extract(self): | 
					
						
							|  |  |  | 		text = '' | 
					
						
							|  |  |  | 		for p in self.paralist: | 
					
						
							|  |  |  | 			text = text + (p.extract()) | 
					
						
							|  |  |  | 		return text | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def extractpart(self, long1, long2): | 
					
						
							|  |  |  | 		if long1 > long2: long1, long2 = long2, long1 | 
					
						
							|  |  |  | 		para1, pos1 = long1 | 
					
						
							|  |  |  | 		para2, pos2 = long2 | 
					
						
							|  |  |  | 		text = '' | 
					
						
							|  |  |  | 		while para1 < para2: | 
					
						
							|  |  |  | 			ptext = self.paralist[para1].extract() | 
					
						
							|  |  |  | 			text = text + ptext[pos1:] | 
					
						
							|  |  |  | 			pos1 = 0 | 
					
						
							|  |  |  | 			para1 = para1 + 1 | 
					
						
							|  |  |  | 		ptext = self.paralist[para2].extract() | 
					
						
							|  |  |  | 		return text + ptext[pos1:pos2] | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def whereis(self, d, h, v): | 
					
						
							|  |  |  | 		total = 0 | 
					
						
							|  |  |  | 		for i in range(len(self.paralist)): | 
					
						
							|  |  |  | 			p = self.paralist[i] | 
					
						
							|  |  |  | 			result = p.whereis(d, h, v) | 
					
						
							| 
									
										
										
										
											2000-12-12 23:11:42 +00:00
										 |  |  | 			if result is not None: | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 				return i, result | 
					
						
							|  |  |  | 		return None | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def roundtowords(self, long1, long2): | 
					
						
							|  |  |  | 		i, offset = long1 | 
					
						
							|  |  |  | 		text = self.paralist[i].extract() | 
					
						
							| 
									
										
										
										
											2000-12-12 23:11:42 +00:00
										 |  |  | 		while offset > 0 and text[offset-1] != ' ': offset = offset-1 | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 		long1 = i, offset | 
					
						
							|  |  |  | 		# | 
					
						
							|  |  |  | 		i, offset = long2 | 
					
						
							|  |  |  | 		text = self.paralist[i].extract() | 
					
						
							|  |  |  | 		n = len(text) | 
					
						
							| 
									
										
										
										
											2000-12-12 23:11:42 +00:00
										 |  |  | 		while offset < n-1 and text[offset] != ' ': offset = offset+1 | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 		long2 = i, offset | 
					
						
							|  |  |  | 		# | 
					
						
							|  |  |  | 		return long1, long2 | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def roundtoparagraphs(self, long1, long2): | 
					
						
							|  |  |  | 		long1 = long1[0], 0 | 
					
						
							|  |  |  | 		long2 = long2[0], len(self.paralist[long2[0]].extract()) | 
					
						
							|  |  |  | 		return long1, long2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Formatter back-end to send the text directly to the drawing object | 
					
						
							|  |  |  | class WritingBackEnd(NullBackEnd): | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def __init__(self, d, width): | 
					
						
							|  |  |  | 		self.d = d | 
					
						
							|  |  |  | 		self.width = width | 
					
						
							|  |  |  | 		self.lineno = 0 | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def addpara(self, p): | 
					
						
							|  |  |  | 		self.lineno = p.render(self.d, 0, self.lineno, self.width) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # A formatter receives a stream of formatting instructions and assembles | 
					
						
							|  |  |  | # these into a stream of paragraphs on to a back-end.  The assembly is | 
					
						
							|  |  |  | # parametrized by a text measurement object, which must match the output | 
					
						
							|  |  |  | # operations of the back-end.  The back-end is responsible for splitting | 
					
						
							|  |  |  | # paragraphs up in lines of a given maximum width.  (This is done because | 
					
						
							|  |  |  | # in a windowing environment, when the window size changes, there is no | 
					
						
							|  |  |  | # need to redo the assembly into paragraphs, but the splitting into lines | 
					
						
							|  |  |  | # must be done taking the new window size into account.) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Formatter base class.  Initialize it with a text measurement object, | 
					
						
							|  |  |  | # which is used for text measurements, and a back-end object, | 
					
						
							|  |  |  | # which receives the completed paragraphs.  The formatting methods are: | 
					
						
							|  |  |  | # setfont(font) | 
					
						
							|  |  |  | # setleftindent(nspaces) | 
					
						
							|  |  |  | # setjust(type) where type is 'l', 'c', 'r', or 'lr' | 
					
						
							|  |  |  | # flush() | 
					
						
							|  |  |  | # vspace(nlines) | 
					
						
							|  |  |  | # needvspace(nlines) | 
					
						
							|  |  |  | # addword(word, nspaces) | 
					
						
							|  |  |  | class BaseFormatter: | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def __init__(self, d, b): | 
					
						
							|  |  |  | 		# Drawing object used for text measurements | 
					
						
							|  |  |  | 		self.d = d | 
					
						
							|  |  |  | 		# | 
					
						
							|  |  |  | 		# BackEnd object receiving completed paragraphs | 
					
						
							|  |  |  | 		self.b = b | 
					
						
							|  |  |  | 		# | 
					
						
							|  |  |  | 		# Parameters of the formatting model | 
					
						
							|  |  |  | 		self.leftindent = 0 | 
					
						
							|  |  |  | 		self.just = 'l' | 
					
						
							|  |  |  | 		self.font = None | 
					
						
							|  |  |  | 		self.blanklines = 0 | 
					
						
							|  |  |  | 		# | 
					
						
							|  |  |  | 		# Parameters derived from the current font | 
					
						
							|  |  |  | 		self.space = d.textwidth(' ') | 
					
						
							|  |  |  | 		self.line = d.lineheight() | 
					
						
							|  |  |  | 		self.ascent = d.baseline() | 
					
						
							|  |  |  | 		self.descent = self.line - self.ascent | 
					
						
							|  |  |  | 		# | 
					
						
							|  |  |  | 		# Parameter derived from the default font | 
					
						
							|  |  |  | 		self.n_space = self.space | 
					
						
							|  |  |  | 		# | 
					
						
							|  |  |  | 		# Current paragraph being built | 
					
						
							|  |  |  | 		self.para = None | 
					
						
							|  |  |  | 		self.nospace = 1 | 
					
						
							|  |  |  | 		# | 
					
						
							|  |  |  | 		# Font to set on the next word | 
					
						
							|  |  |  | 		self.nextfont = None | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def newpara(self): | 
					
						
							|  |  |  | 		return Para.Para() | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def setfont(self, font): | 
					
						
							| 
									
										
										
										
											2000-12-12 23:11:42 +00:00
										 |  |  | 		if font is None: return | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 		self.font = self.nextfont = font | 
					
						
							|  |  |  | 		d = self.d | 
					
						
							|  |  |  | 		d.setfont(font) | 
					
						
							|  |  |  | 		self.space = d.textwidth(' ') | 
					
						
							|  |  |  | 		self.line = d.lineheight() | 
					
						
							|  |  |  | 		self.ascent = d.baseline() | 
					
						
							|  |  |  | 		self.descent = self.line - self.ascent | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def setleftindent(self, nspaces): | 
					
						
							|  |  |  | 		self.leftindent = int(self.n_space * nspaces) | 
					
						
							|  |  |  | 		if self.para: | 
					
						
							|  |  |  | 			hang = self.leftindent - self.para.indent_left | 
					
						
							|  |  |  | 			if hang > 0 and self.para.getlength() <= hang: | 
					
						
							|  |  |  | 				self.para.makehangingtag(hang) | 
					
						
							|  |  |  | 				self.nospace = 1 | 
					
						
							|  |  |  | 			else: | 
					
						
							|  |  |  | 				self.flush() | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def setrightindent(self, nspaces): | 
					
						
							|  |  |  | 		self.rightindent = int(self.n_space * nspaces) | 
					
						
							|  |  |  | 		if self.para: | 
					
						
							|  |  |  | 			self.para.indent_right = self.rightindent | 
					
						
							|  |  |  | 			self.flush() | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def setjust(self, just): | 
					
						
							|  |  |  | 		self.just = just | 
					
						
							|  |  |  | 		if self.para: | 
					
						
							|  |  |  | 			self.para.just = self.just | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def flush(self): | 
					
						
							|  |  |  | 		if self.para: | 
					
						
							|  |  |  | 			self.b.addpara(self.para) | 
					
						
							|  |  |  | 			self.para = None | 
					
						
							| 
									
										
										
										
											2000-12-12 23:11:42 +00:00
										 |  |  | 			if self.font is not None: | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 				self.d.setfont(self.font) | 
					
						
							|  |  |  | 		self.nospace = 1 | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def vspace(self, nlines): | 
					
						
							|  |  |  | 		self.flush() | 
					
						
							|  |  |  | 		if nlines > 0: | 
					
						
							|  |  |  | 			self.para = self.newpara() | 
					
						
							|  |  |  | 			tuple = None, '', 0, 0, 0, int(nlines*self.line), 0 | 
					
						
							|  |  |  | 			self.para.words.append(tuple) | 
					
						
							|  |  |  | 			self.flush() | 
					
						
							|  |  |  | 			self.blanklines = self.blanklines + nlines | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def needvspace(self, nlines): | 
					
						
							|  |  |  | 		self.flush() # Just to be sure | 
					
						
							|  |  |  | 		if nlines > self.blanklines: | 
					
						
							|  |  |  | 			self.vspace(nlines - self.blanklines) | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def addword(self, text, space): | 
					
						
							|  |  |  | 		if self.nospace and not text: | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		self.nospace = 0 | 
					
						
							|  |  |  | 		self.blanklines = 0 | 
					
						
							|  |  |  | 		if not self.para: | 
					
						
							|  |  |  | 			self.para = self.newpara() | 
					
						
							|  |  |  | 			self.para.indent_left = self.leftindent | 
					
						
							|  |  |  | 			self.para.just = self.just | 
					
						
							|  |  |  | 			self.nextfont = self.font | 
					
						
							|  |  |  | 		space = int(space * self.space) | 
					
						
							| 
									
										
										
										
											2000-02-27 15:34:29 +00:00
										 |  |  | 		self.para.words.append((self.nextfont, text, | 
					
						
							|  |  |  | 			self.d.textwidth(text), space, space, | 
					
						
							|  |  |  | 			self.ascent, self.descent)) | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 		self.nextfont = None | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def bgn_anchor(self, id): | 
					
						
							|  |  |  | 		if not self.para: | 
					
						
							|  |  |  | 			self.nospace = 0 | 
					
						
							|  |  |  | 			self.addword('', 0) | 
					
						
							|  |  |  | 		self.para.bgn_anchor(id) | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def end_anchor(self, id): | 
					
						
							|  |  |  | 		if not self.para: | 
					
						
							|  |  |  | 			self.nospace = 0 | 
					
						
							|  |  |  | 			self.addword('', 0) | 
					
						
							|  |  |  | 		self.para.end_anchor(id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Measuring object for measuring text as viewed on a tty | 
					
						
							|  |  |  | class NullMeasurer: | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def __init__(self): | 
					
						
							|  |  |  | 		pass | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def setfont(self, font): | 
					
						
							|  |  |  | 		pass | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def textwidth(self, text): | 
					
						
							|  |  |  | 		return len(text) | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def lineheight(self): | 
					
						
							|  |  |  | 		return 1 | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def baseline(self): | 
					
						
							|  |  |  | 		return 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Drawing object for writing plain ASCII text to a file | 
					
						
							|  |  |  | class FileWriter: | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def __init__(self, fp): | 
					
						
							|  |  |  | 		self.fp = fp | 
					
						
							|  |  |  | 		self.lineno, self.colno = 0, 0 | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def setfont(self, font): | 
					
						
							|  |  |  | 		pass | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def text(self, (h, v), str): | 
					
						
							|  |  |  | 		if not str: return | 
					
						
							|  |  |  | 		if '\n' in str: | 
					
						
							|  |  |  | 			raise ValueError, 'can\'t write \\n' | 
					
						
							|  |  |  | 		while self.lineno < v: | 
					
						
							|  |  |  | 			self.fp.write('\n') | 
					
						
							|  |  |  | 			self.colno, self.lineno = 0, self.lineno + 1 | 
					
						
							|  |  |  | 		while self.lineno > v: | 
					
						
							|  |  |  | 			# XXX This should never happen... | 
					
						
							|  |  |  | 			self.fp.write('\033[A') # ANSI up arrow | 
					
						
							|  |  |  | 			self.lineno = self.lineno - 1 | 
					
						
							|  |  |  | 		if self.colno < h: | 
					
						
							|  |  |  | 			self.fp.write(' ' * (h - self.colno)) | 
					
						
							|  |  |  | 		elif self.colno > h: | 
					
						
							|  |  |  | 			self.fp.write('\b' * (self.colno - h)) | 
					
						
							|  |  |  | 		self.colno = h | 
					
						
							|  |  |  | 		self.fp.write(str) | 
					
						
							|  |  |  | 		self.colno = h + len(str) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Formatting class to do nothing at all with the data | 
					
						
							|  |  |  | class NullFormatter(BaseFormatter): | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def __init__(self): | 
					
						
							|  |  |  | 		d = NullMeasurer() | 
					
						
							|  |  |  | 		b = NullBackEnd() | 
					
						
							|  |  |  | 		BaseFormatter.__init__(self, d, b) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Formatting class to write directly to a file | 
					
						
							|  |  |  | class WritingFormatter(BaseFormatter): | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def __init__(self, fp, width): | 
					
						
							|  |  |  | 		dm = NullMeasurer() | 
					
						
							|  |  |  | 		dw = FileWriter(fp) | 
					
						
							|  |  |  | 		b = WritingBackEnd(dw, width) | 
					
						
							|  |  |  | 		BaseFormatter.__init__(self, dm, b) | 
					
						
							|  |  |  | 		self.blanklines = 1 | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	# Suppress multiple blank lines | 
					
						
							|  |  |  | 	def needvspace(self, nlines): | 
					
						
							|  |  |  | 		BaseFormatter.needvspace(self, min(1, nlines)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # A "FunnyFormatter" writes ASCII text with a twist: *bold words*, | 
					
						
							|  |  |  | # _italic text_ and _underlined words_, and `quoted text'. | 
					
						
							|  |  |  | # It assumes that the fonts are 'r', 'i', 'b', 'u', 'q': (roman, | 
					
						
							|  |  |  | # italic, bold, underline, quote). | 
					
						
							|  |  |  | # Moreover, if the font is in upper case, the text is converted to | 
					
						
							|  |  |  | # UPPER CASE. | 
					
						
							|  |  |  | class FunnyFormatter(WritingFormatter): | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def flush(self): | 
					
						
							|  |  |  | 		if self.para: finalize(self.para) | 
					
						
							|  |  |  | 		WritingFormatter.flush(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Surrounds *bold words* and _italic text_ in a paragraph with | 
					
						
							|  |  |  | # appropriate markers, fixing the size (assuming these characters' | 
					
						
							|  |  |  | # width is 1). | 
					
						
							|  |  |  | openchar = \ | 
					
						
							|  |  |  |     {'b':'*', 'i':'_', 'u':'_', 'q':'`', 'B':'*', 'I':'_', 'U':'_', 'Q':'`'} | 
					
						
							|  |  |  | closechar = \ | 
					
						
							|  |  |  |     {'b':'*', 'i':'_', 'u':'_', 'q':'\'', 'B':'*', 'I':'_', 'U':'_', 'Q':'\''} | 
					
						
							|  |  |  | def finalize(para): | 
					
						
							|  |  |  | 	oldfont = curfont = 'r' | 
					
						
							| 
									
										
										
										
											1996-10-08 14:06:17 +00:00
										 |  |  | 	para.words.append(('r', '', 0, 0, 0, 0)) # temporary, deleted at end | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 	for i in range(len(para.words)): | 
					
						
							|  |  |  | 		fo, te, wi = para.words[i][:3] | 
					
						
							| 
									
										
										
										
											2000-12-12 23:11:42 +00:00
										 |  |  | 		if fo is not None: curfont = fo | 
					
						
							|  |  |  | 		if curfont != oldfont: | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 			if closechar.has_key(oldfont): | 
					
						
							|  |  |  | 				c = closechar[oldfont] | 
					
						
							|  |  |  | 				j = i-1 | 
					
						
							|  |  |  | 				while j > 0 and para.words[j][1] == '': j = j-1 | 
					
						
							|  |  |  | 				fo1, te1, wi1 = para.words[j][:3] | 
					
						
							|  |  |  | 				te1 = te1 + c | 
					
						
							|  |  |  | 				wi1 = wi1 + len(c) | 
					
						
							|  |  |  | 				para.words[j] = (fo1, te1, wi1) + \ | 
					
						
							|  |  |  | 					para.words[j][3:] | 
					
						
							|  |  |  | 			if openchar.has_key(curfont) and te: | 
					
						
							|  |  |  | 				c = openchar[curfont] | 
					
						
							|  |  |  | 				te = c + te | 
					
						
							|  |  |  | 				wi = len(c) + wi | 
					
						
							|  |  |  | 				para.words[i] = (fo, te, wi) + \ | 
					
						
							|  |  |  | 					para.words[i][3:] | 
					
						
							|  |  |  | 			if te: oldfont = curfont | 
					
						
							|  |  |  | 			else: oldfont = 'r' | 
					
						
							|  |  |  | 		if curfont in string.uppercase: | 
					
						
							|  |  |  | 			te = string.upper(te) | 
					
						
							|  |  |  | 			para.words[i] = (fo, te, wi) + para.words[i][3:] | 
					
						
							|  |  |  | 	del para.words[-1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Formatter back-end to draw the text in a window. | 
					
						
							|  |  |  | # This has an option to draw while the paragraphs are being added, | 
					
						
							|  |  |  | # to minimize the delay before the user sees anything. | 
					
						
							|  |  |  | # This manages the entire "document" of the window. | 
					
						
							|  |  |  | class StdwinBackEnd(SavingBackEnd): | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def __init__(self, window, drawnow): | 
					
						
							|  |  |  | 		self.window = window | 
					
						
							|  |  |  | 		self.drawnow = drawnow | 
					
						
							|  |  |  | 		self.width = window.getwinsize()[0] | 
					
						
							|  |  |  | 		self.selection = None | 
					
						
							|  |  |  | 		self.height = 0 | 
					
						
							|  |  |  | 		window.setorigin(0, 0) | 
					
						
							|  |  |  | 		window.setdocsize(0, 0) | 
					
						
							|  |  |  | 		self.d = window.begindrawing() | 
					
						
							|  |  |  | 		SavingBackEnd.__init__(self) | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def finish(self): | 
					
						
							|  |  |  | 		self.d.close() | 
					
						
							|  |  |  | 		self.d = None | 
					
						
							|  |  |  | 		self.window.setdocsize(0, self.height) | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def addpara(self, p): | 
					
						
							|  |  |  | 		self.paralist.append(p) | 
					
						
							|  |  |  | 		if self.drawnow: | 
					
						
							|  |  |  | 			self.height = \ | 
					
						
							|  |  |  | 				p.render(self.d, 0, self.height, self.width) | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			p.layout(self.width) | 
					
						
							|  |  |  | 			p.left = 0 | 
					
						
							|  |  |  | 			p.top = self.height | 
					
						
							|  |  |  | 			p.right = self.width | 
					
						
							|  |  |  | 			p.bottom = self.height + p.height | 
					
						
							|  |  |  | 			self.height = p.bottom | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def resize(self): | 
					
						
							|  |  |  | 		self.window.change((0, 0), (self.width, self.height)) | 
					
						
							|  |  |  | 		self.width = self.window.getwinsize()[0] | 
					
						
							|  |  |  | 		self.height = 0 | 
					
						
							|  |  |  | 		for p in self.paralist: | 
					
						
							|  |  |  | 			p.layout(self.width) | 
					
						
							|  |  |  | 			p.left = 0 | 
					
						
							|  |  |  | 			p.top = self.height | 
					
						
							|  |  |  | 			p.right = self.width | 
					
						
							|  |  |  | 			p.bottom = self.height + p.height | 
					
						
							|  |  |  | 			self.height = p.bottom | 
					
						
							|  |  |  | 		self.window.change((0, 0), (self.width, self.height)) | 
					
						
							|  |  |  | 		self.window.setdocsize(0, self.height) | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def redraw(self, area): | 
					
						
							|  |  |  | 		d = self.window.begindrawing() | 
					
						
							|  |  |  | 		(left, top), (right, bottom) = area | 
					
						
							|  |  |  | 		d.erase(area) | 
					
						
							|  |  |  | 		d.cliprect(area) | 
					
						
							|  |  |  | 		for p in self.paralist: | 
					
						
							|  |  |  | 			if top < p.bottom and p.top < bottom: | 
					
						
							|  |  |  | 				v = p.render(d, p.left, p.top, p.right) | 
					
						
							|  |  |  | 		if self.selection: | 
					
						
							|  |  |  | 			self.invert(d, self.selection) | 
					
						
							|  |  |  | 		d.close() | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def setselection(self, new): | 
					
						
							|  |  |  | 		if new: | 
					
						
							|  |  |  | 			long1, long2 = new | 
					
						
							|  |  |  | 			pos1 = long1[:3] | 
					
						
							|  |  |  | 			pos2 = long2[:3] | 
					
						
							|  |  |  | 			new = pos1, pos2 | 
					
						
							| 
									
										
										
										
											2000-12-12 23:11:42 +00:00
										 |  |  | 		if new != self.selection: | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 			d = self.window.begindrawing() | 
					
						
							|  |  |  | 			if self.selection: | 
					
						
							|  |  |  | 				self.invert(d, self.selection) | 
					
						
							|  |  |  | 			if new: | 
					
						
							|  |  |  | 				self.invert(d, new) | 
					
						
							|  |  |  | 			d.close() | 
					
						
							|  |  |  | 			self.selection = new | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def getselection(self): | 
					
						
							|  |  |  | 		return self.selection | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def extractselection(self): | 
					
						
							|  |  |  | 		if self.selection: | 
					
						
							|  |  |  | 			a, b = self.selection | 
					
						
							|  |  |  | 			return self.extractpart(a, b) | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			return None | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def invert(self, d, region): | 
					
						
							|  |  |  | 		long1, long2 = region | 
					
						
							|  |  |  | 		if long1 > long2: long1, long2 = long2, long1 | 
					
						
							|  |  |  | 		para1, pos1 = long1 | 
					
						
							|  |  |  | 		para2, pos2 = long2 | 
					
						
							|  |  |  | 		while para1 < para2: | 
					
						
							|  |  |  | 			self.paralist[para1].invert(d, pos1, None) | 
					
						
							|  |  |  | 			pos1 = None | 
					
						
							|  |  |  | 			para1 = para1 + 1 | 
					
						
							|  |  |  | 		self.paralist[para2].invert(d, pos1, pos2) | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def search(self, prog): | 
					
						
							| 
									
										
										
										
											1997-10-22 21:00:49 +00:00
										 |  |  | 		import re, string | 
					
						
							| 
									
										
										
										
											2000-12-12 23:11:42 +00:00
										 |  |  | 		if type(prog) is type(''): | 
					
						
							| 
									
										
										
										
											1997-10-22 21:00:49 +00:00
										 |  |  | 			prog = re.compile(string.lower(prog)) | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 		if self.selection: | 
					
						
							|  |  |  | 			iold = self.selection[0][0] | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			iold = -1 | 
					
						
							|  |  |  | 		hit = None | 
					
						
							|  |  |  | 		for i in range(len(self.paralist)): | 
					
						
							|  |  |  | 			if i == iold or i < iold and hit: | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			p = self.paralist[i] | 
					
						
							|  |  |  | 			text = string.lower(p.extract()) | 
					
						
							| 
									
										
										
										
											1997-10-22 21:00:49 +00:00
										 |  |  | 			match = prog.search(text) | 
					
						
							|  |  |  | 			if match: | 
					
						
							|  |  |  | 				a, b = match.group(0) | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 				long1 = i, a | 
					
						
							|  |  |  | 				long2 = i, b | 
					
						
							|  |  |  | 				hit = long1, long2 | 
					
						
							|  |  |  | 				if i > iold: | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 		if hit: | 
					
						
							|  |  |  | 			self.setselection(hit) | 
					
						
							|  |  |  | 			i = hit[0][0] | 
					
						
							|  |  |  | 			p = self.paralist[i] | 
					
						
							|  |  |  | 			self.window.show((p.left, p.top), (p.right, p.bottom)) | 
					
						
							|  |  |  | 			return 1 | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			return 0 | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def showanchor(self, id): | 
					
						
							|  |  |  | 		for i in range(len(self.paralist)): | 
					
						
							|  |  |  | 			p = self.paralist[i] | 
					
						
							|  |  |  | 			if p.hasanchor(id): | 
					
						
							|  |  |  | 				long1 = i, 0 | 
					
						
							|  |  |  | 				long2 = i, len(p.extract()) | 
					
						
							|  |  |  | 				hit = long1, long2 | 
					
						
							|  |  |  | 				self.setselection(hit) | 
					
						
							| 
									
										
										
										
											2000-02-27 15:34:29 +00:00
										 |  |  | 				self.window.show( | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 					(p.left, p.top), (p.right, p.bottom)) | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # GL extensions | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class GLFontCache: | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def __init__(self): | 
					
						
							|  |  |  | 		self.reset() | 
					
						
							|  |  |  | 		self.setfont('') | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def reset(self): | 
					
						
							|  |  |  | 		self.fontkey = None | 
					
						
							|  |  |  | 		self.fonthandle = None | 
					
						
							|  |  |  | 		self.fontinfo = None | 
					
						
							|  |  |  | 		self.fontcache = {} | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def close(self): | 
					
						
							|  |  |  | 		self.reset() | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def setfont(self, fontkey): | 
					
						
							|  |  |  | 		if fontkey == '': | 
					
						
							|  |  |  | 			fontkey = 'Times-Roman 12' | 
					
						
							|  |  |  | 		elif ' ' not in fontkey: | 
					
						
							|  |  |  | 			fontkey = fontkey + ' 12' | 
					
						
							|  |  |  | 		if fontkey == self.fontkey: | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		if self.fontcache.has_key(fontkey): | 
					
						
							|  |  |  | 			handle = self.fontcache[fontkey] | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			import string | 
					
						
							|  |  |  | 			i = string.index(fontkey, ' ') | 
					
						
							|  |  |  | 			name, sizestr = fontkey[:i], fontkey[i:] | 
					
						
							|  |  |  | 			size = eval(sizestr) | 
					
						
							|  |  |  | 			key1 = name + ' 1' | 
					
						
							|  |  |  | 			key = name + ' ' + `size` | 
					
						
							|  |  |  | 			# NB key may differ from fontkey! | 
					
						
							|  |  |  | 			if self.fontcache.has_key(key): | 
					
						
							|  |  |  | 				handle = self.fontcache[key] | 
					
						
							|  |  |  | 			else: | 
					
						
							|  |  |  | 				if self.fontcache.has_key(key1): | 
					
						
							|  |  |  | 					handle = self.fontcache[key1] | 
					
						
							|  |  |  | 				else: | 
					
						
							|  |  |  | 					import fm | 
					
						
							|  |  |  | 					handle = fm.findfont(name) | 
					
						
							|  |  |  | 					self.fontcache[key1] = handle | 
					
						
							|  |  |  | 				handle = handle.scalefont(size) | 
					
						
							|  |  |  | 				self.fontcache[fontkey] = \ | 
					
						
							|  |  |  | 					self.fontcache[key] = handle | 
					
						
							|  |  |  | 		self.fontkey = fontkey | 
					
						
							| 
									
										
										
										
											2000-12-12 23:11:42 +00:00
										 |  |  | 		if self.fonthandle != handle: | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 			self.fonthandle = handle | 
					
						
							|  |  |  | 			self.fontinfo = handle.getfontinfo() | 
					
						
							|  |  |  | 			handle.setfont() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class GLMeasurer(GLFontCache): | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def textwidth(self, text): | 
					
						
							|  |  |  | 		return self.fonthandle.getstrwidth(text) | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def baseline(self): | 
					
						
							|  |  |  | 		return self.fontinfo[6] - self.fontinfo[3] | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def lineheight(self): | 
					
						
							|  |  |  | 		return self.fontinfo[6] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class GLWriter(GLFontCache): | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	# NOTES: | 
					
						
							|  |  |  | 	# (1) Use gl.ortho2 to use X pixel coordinates! | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def text(self, (h, v), text): | 
					
						
							|  |  |  | 		import gl, fm | 
					
						
							|  |  |  | 		gl.cmov2i(h, v + self.fontinfo[6] - self.fontinfo[3]) | 
					
						
							|  |  |  | 		fm.prstr(text) | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def setfont(self, fontkey): | 
					
						
							|  |  |  | 		oldhandle = self.fonthandle | 
					
						
							|  |  |  | 		GLFontCache.setfont(fontkey) | 
					
						
							| 
									
										
										
										
											2000-12-12 23:11:42 +00:00
										 |  |  | 		if self.fonthandle != oldhandle: | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 			handle.setfont() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class GLMeasurerWriter(GLMeasurer, GLWriter): | 
					
						
							|  |  |  | 	pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class GLBackEnd(SavingBackEnd): | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def __init__(self, wid): | 
					
						
							|  |  |  | 		import gl | 
					
						
							|  |  |  | 		gl.winset(wid) | 
					
						
							|  |  |  | 		self.wid = wid | 
					
						
							|  |  |  | 		self.width = gl.getsize()[1] | 
					
						
							|  |  |  | 		self.height = 0 | 
					
						
							|  |  |  | 		self.d = GLMeasurerWriter() | 
					
						
							|  |  |  | 		SavingBackEnd.__init__(self) | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def finish(self): | 
					
						
							|  |  |  | 		pass | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def addpara(self, p): | 
					
						
							|  |  |  | 		self.paralist.append(p) | 
					
						
							|  |  |  | 		self.height = p.render(self.d, 0, self.height, self.width) | 
					
						
							|  |  |  | 	# | 
					
						
							|  |  |  | 	def redraw(self): | 
					
						
							|  |  |  | 		import gl | 
					
						
							|  |  |  | 		gl.winset(self.wid) | 
					
						
							|  |  |  | 		width = gl.getsize()[1] | 
					
						
							| 
									
										
										
										
											2000-12-12 23:11:42 +00:00
										 |  |  | 		if width != self.width: | 
					
						
							| 
									
										
										
										
											1995-02-27 13:16:55 +00:00
										 |  |  | 			setdocsize = 1 | 
					
						
							|  |  |  | 			self.width = width | 
					
						
							|  |  |  | 			for p in self.paralist: | 
					
						
							|  |  |  | 				p.top = p.bottom = None | 
					
						
							|  |  |  | 		d = self.d | 
					
						
							|  |  |  | 		v = 0 | 
					
						
							|  |  |  | 		for p in self.paralist: | 
					
						
							|  |  |  | 			v = p.render(d, 0, v, width) |