summaryrefslogtreecommitdiffstats
path: root/Lib/stdwin
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/stdwin')
-rwxr-xr-xLib/stdwin/formatter.py204
1 files changed, 204 insertions, 0 deletions
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)