Exemplo n.º 1
0
    def __init__(self, parent, indents):
        wx.Dialog.__init__(self, parent, -1, "Adjust styles", style=wx.DEFAULT_DIALOG_STYLE)

        indents.sort(lambda i1, i2: -cmp(len(i1.lines), len(i2.lines)))

        vsizer = wx.BoxSizer(wx.VERTICAL)

        tmp = wx.StaticText(self, -1, "Input:")
        vsizer.Add(tmp)

        self.inputLb = wx.ListBox(self, -1, size=(400, 200))
        for it in indents:
            self.inputLb.Append("%d lines (indented %d characters)" % (len(it.lines), it.indent), it)

        vsizer.Add(self.inputLb, 0, wx.EXPAND)

        hsizer = wx.BoxSizer(wx.HORIZONTAL)

        hsizer.Add(wx.StaticText(self, -1, "Style:"), 0, wx.ALIGN_CENTER_VERTICAL)
        self.styleCombo = wx.ComboBox(self, -1, style=wx.CB_READONLY)

        self.styleCombo.Append("Scene / Action", SCENE_ACTION)
        for t in config.getTIs():
            self.styleCombo.Append(t.name, t.lt)

        util.setWH(self.styleCombo, w=150)

        hsizer.Add(self.styleCombo, 0, wx.LEFT, 10)

        vsizer.Add(hsizer, 0, wx.TOP | wx.BOTTOM, 10)

        vsizer.Add(wx.StaticText(self, -1, "Lines:"))

        self.linesEntry = wx.TextCtrl(self, -1, size=(400, 200), style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
        vsizer.Add(self.linesEntry, 0, wx.EXPAND)

        hsizer = wx.BoxSizer(wx.HORIZONTAL)

        hsizer.Add((1, 1), 1)

        cancelBtn = gutil.createStockButton(self, "Cancel")
        hsizer.Add(cancelBtn)

        okBtn = gutil.createStockButton(self, "OK")
        hsizer.Add(okBtn, 0, wx.LEFT, 10)

        vsizer.Add(hsizer, 0, wx.EXPAND | wx.TOP, 10)

        util.finishWindow(self, vsizer)

        wx.EVT_COMBOBOX(self, self.styleCombo.GetId(), self.OnStyleCombo)
        wx.EVT_LISTBOX(self, self.inputLb.GetId(), self.OnInputLb)

        wx.EVT_BUTTON(self, cancelBtn.GetId(), self.OnCancel)
        wx.EVT_BUTTON(self, okBtn.GetId(), self.OnOK)

        self.inputLb.SetSelection(0)
        self.OnInputLb()
Exemplo n.º 2
0
    def addTypeCombo(self, name, descr, parent, sizer):
        sizer.Add(wx.StaticText(parent, -1, descr + ":"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)

        combo = wx.ComboBox(parent, -1, style=wx.CB_READONLY)

        for t in config.getTIs():
            combo.Append(t.name, t.lt)

        sizer.Add(combo)

        wx.EVT_COMBOBOX(self, combo.GetId(), self.OnMisc)

        setattr(self, name + "Combo", combo)
Exemplo n.º 3
0
    def __init__(self, parent, id, cfg):
        wx.Panel.__init__(self, parent, id)
        self.cfg = cfg

        vsizer = wx.BoxSizer(wx.VERTICAL)

        hsizer = wx.BoxSizer(wx.HORIZONTAL)

        hsizer.Add(wx.StaticText(self, -1, "Element:"), 0,
                   wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)

        self.elementsCombo = wx.ComboBox(self, -1, style = wx.CB_READONLY)

        for t in config.getTIs():
            self.elementsCombo.Append(t.name, t.lt)

        hsizer.Add(self.elementsCombo, 0)

        vsizer.Add(hsizer, 0, wx.EXPAND)

        vsizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 10)

        gsizer = wx.FlexGridSizer(2, 2, 5, 0)

        self.addTypeCombo("newEnter", "Enter creates", self, gsizer)
        self.addTypeCombo("newTab", "Tab creates", self, gsizer)
        self.addTypeCombo("nextTab", "Tab switches to", self, gsizer)
        self.addTypeCombo("prevTab", "Shift+Tab switches to", self, gsizer)

        vsizer.Add(gsizer)

        util.finishWindow(self, vsizer, center = False)

        wx.EVT_COMBOBOX(self, self.elementsCombo.GetId(), self.OnElementCombo)

        self.elementsCombo.SetSelection(0)
        self.OnElementCombo()
Exemplo n.º 4
0
    def __init__(self, parent, indents):
        wx.Dialog.__init__(self,
                           parent,
                           -1,
                           "Adjust styles",
                           style=wx.DEFAULT_DIALOG_STYLE)

        indents.sort(lambda i1, i2: -cmp(len(i1.lines), len(i2.lines)))

        vsizer = wx.BoxSizer(wx.VERTICAL)

        tmp = wx.StaticText(self, -1, "Input:")
        vsizer.Add(tmp)

        self.inputLb = wx.ListBox(self, -1, size=(400, 200))
        for it in indents:
            self.inputLb.Append(
                "%d lines (indented %d characters)" %
                (len(it.lines), it.indent), it)

        vsizer.Add(self.inputLb, 0, wx.EXPAND)

        hsizer = wx.BoxSizer(wx.HORIZONTAL)

        hsizer.Add(wx.StaticText(self, -1, "Style:"), 0,
                   wx.ALIGN_CENTER_VERTICAL)
        self.styleCombo = wx.ComboBox(self, -1, style=wx.CB_READONLY)

        self.styleCombo.Append("Scene / Action", SCENE_ACTION)
        for t in config.getTIs():
            self.styleCombo.Append(t.name, t.lt)

        self.styleCombo.Append("Ignore", IGNORE)

        util.setWH(self.styleCombo, w=150)

        hsizer.Add(self.styleCombo, 0, wx.LEFT, 10)

        vsizer.Add(hsizer, 0, wx.TOP | wx.BOTTOM, 10)

        vsizer.Add(wx.StaticText(self, -1, "Lines:"))

        self.linesEntry = wx.TextCtrl(self,
                                      -1,
                                      size=(400, 200),
                                      style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
        vsizer.Add(self.linesEntry, 0, wx.EXPAND)

        hsizer = wx.BoxSizer(wx.HORIZONTAL)

        hsizer.Add((1, 1), 1)

        cancelBtn = gutil.createStockButton(self, "Cancel")
        hsizer.Add(cancelBtn)

        okBtn = gutil.createStockButton(self, "OK")
        hsizer.Add(okBtn, 0, wx.LEFT, 10)

        vsizer.Add(hsizer, 0, wx.EXPAND | wx.TOP, 10)

        util.finishWindow(self, vsizer)

        wx.EVT_COMBOBOX(self, self.styleCombo.GetId(), self.OnStyleCombo)
        wx.EVT_LISTBOX(self, self.inputLb.GetId(), self.OnInputLb)

        wx.EVT_BUTTON(self, cancelBtn.GetId(), self.OnCancel)
        wx.EVT_BUTTON(self, okBtn.GetId(), self.OnOK)

        self.inputLb.SetSelection(0)
        self.OnInputLb()
Exemplo n.º 5
0
    def generate(self):
        tf = pml.TextFormatter(self.sp.cfg.paperWidth, self.sp.cfg.paperHeight,
                               15.0, 12)

        ls = self.sp.lines

        total = len(ls)
        tf.addText("%5d Lines in Screenplay" % total)

        tf.addSpace(2.0)

        for t in config.getTIs():
            cnt = sum([1 for line in ls if line.lt == t.lt])
            tf.addText("        %13s  %4d (%d%%)" %
                       (t.name, cnt, util.pct(cnt, total)))

        tf.addSpace(4.0)

        intLines = sum([
            si.lines for si in self.sr.scenes
            if util.upper(si.name).startswith("INT.")
        ])
        extLines = sum([
            si.lines for si in self.sr.scenes
            if util.upper(si.name).startswith("EXT.")
        ])

        tf.addText("%d%% Interior / %d%% Exterior Scenes" %
                   (util.pct(intLines, intLines + extLines),
                    util.pct(extLines, intLines + extLines)))

        tf.addSpace(4.0)

        tf.addText("Scene Length in Lines: %d Max / %.2f Avg." %
                   (self.sr.longestScene, self.sr.avgScene))

        # lengths of action elements
        actions = []

        # length of current action element
        curLen = 0

        for ln in ls:
            if curLen > 0:
                if ln.lt == screenplay.ACTION:
                    curLen += 1

                    if ln.lb == screenplay.LB_LAST:
                        actions.append(curLen)
                        curLen = 0
                else:
                    actions.append(curLen)
                    curLen = 0
            else:
                if ln.lt == screenplay.ACTION:
                    curLen = 1

        if curLen > 0:
            actions.append(curLen)

        tf.addSpace(4.0)

        # avoid divide-by-zero
        if len(actions) > 0:
            maxA = max(actions)
            avgA = sum(actions) / float(len(actions))
        else:
            maxA = 0
            avgA = 0.0

        tf.addText("Action Length in Lines: %d Max / %.2f Avg." % (maxA, avgA))

        tf.addSpace(4.0)

        tf.addText("%d Speaking Characters" % len(self.cr.cinfo))

        return pdf.generate(tf.doc)
Exemplo n.º 6
0
    def __init__(self, parent, ctrl):
        wx.Dialog.__init__(self, parent, -1, "Find and Replace",
                           style=wx.DEFAULT_DIALOG_STYLE | wx.WANTS_CHARS)

        self.ctrl = ctrl

        self.searchLine = -1
        self.searchColumn = -1
        self.searchWidth = -1

        hsizer = wx.BoxSizer(wx.HORIZONTAL)

        vsizer = wx.BoxSizer(wx.VERTICAL)

        gsizer = wx.FlexGridSizer(2, 2, 5, 20)
        gsizer.AddGrowableCol(1)

        gsizer.Add(wx.StaticText(self, -1, "Text to Find"), 0,
                   wx.ALIGN_CENTER_VERTICAL)
        self.findEntry = wx.TextCtrl(self, -1, style=wx.TE_PROCESS_ENTER)
        gsizer.Add(self.findEntry, 0, wx.EXPAND)

        gsizer.Add(wx.StaticText(self, -1, "Replace With"), 0,
                   wx.ALIGN_CENTER_VERTICAL)
        self.replaceEntry = wx.TextCtrl(self, -1, style=wx.TE_PROCESS_ENTER)
        gsizer.Add(self.replaceEntry, 0, wx.EXPAND)

        vsizer.Add(gsizer, 0, wx.EXPAND | wx.BOTTOM, 5)

        hsizer2 = wx.BoxSizer(wx.HORIZONTAL)

        vsizer2 = wx.BoxSizer(wx.VERTICAL)

        # wxGTK adds way more space by default than wxMSW between the
        # items, have to adjust for that
        pad = 0
        if misc.isWindows:
            pad = 5

        self.matchWholeCb = wx.CheckBox(self, -1, "Match Whole Word")
        vsizer2.Add(self.matchWholeCb, 0, wx.TOP, pad)

        self.matchCaseCb = wx.CheckBox(self, -1, "Match Case")
        vsizer2.Add(self.matchCaseCb, 0, wx.TOP, pad)

        hsizer2.Add(vsizer2, 0, wx.EXPAND | wx.RIGHT, 10)

        self.direction = wx.RadioBox(self, -1, "Direction",
                                     choices=["Backward", "Forward"])
        self.direction.SetSelection(1)

        hsizer2.Add(self.direction, 1, 0)

        vsizer.Add(hsizer2, 0, wx.EXPAND | wx.BOTTOM, 10)

        self.extraLabel = wx.StaticText(self, -1, "Search In")
        vsizer.Add(self.extraLabel)

        self.elements = wx.CheckListBox(self, -1)

        # sucky wxMSW doesn't support client data for checklistbox items,
        # so we have to store it ourselves
        self.elementTypes = []

        for t in config.getTIs():
            self.elements.Append(t.name)
            self.elementTypes.append(t.lt)

        vsizer.Add(self.elements, 1, wx.EXPAND)

        hsizer.Add(vsizer, 1, wx.EXPAND)

        vsizer = wx.BoxSizer(wx.VERTICAL)

        find = wx.Button(self, -1, "&Find")
        vsizer.Add(find, 0, wx.EXPAND | wx.BOTTOM, 5)

        replaceAll = wx.Button(self, -1, " Replace All ")
        vsizer.Add(replaceAll, 0, wx.EXPAND | wx.BOTTOM, 5)

        replace = wx.Button(self, -1, "&Replace")
        vsizer.Add(replace, 0, wx.EXPAND | wx.BOTTOM, 5)

        self.moreButton = wx.Button(self, -1, "")
        vsizer.Add(self.moreButton, 0, wx.EXPAND | wx.BOTTOM, 5)

        hsizer.Add(vsizer, 0, wx.EXPAND | wx.LEFT, 30)

        wx.EVT_BUTTON(self, find.GetId(), self.OnFind)
        wx.EVT_BUTTON(self, replaceAll.GetId(), self.OnReplaceAll)
        wx.EVT_BUTTON(self, replace.GetId(), self.OnReplace)
        wx.EVT_BUTTON(self, self.moreButton.GetId(), self.OnMore)

        gutil.btnDblClick(find, self.OnFind)
        gutil.btnDblClick(replace, self.OnReplace)

        wx.EVT_TEXT(self, self.findEntry.GetId(), self.OnText)

        wx.EVT_TEXT_ENTER(self, self.findEntry.GetId(), self.OnFind)
        wx.EVT_TEXT_ENTER(self, self.replaceEntry.GetId(), self.OnFind)

        wx.EVT_CHAR(self, self.OnCharMisc)
        wx.EVT_CHAR(self.findEntry, self.OnCharEntry)
        wx.EVT_CHAR(self.replaceEntry, self.OnCharEntry)
        wx.EVT_CHAR(find, self.OnCharButton)
        wx.EVT_CHAR(replaceAll, self.OnCharButton)
        wx.EVT_CHAR(replace, self.OnCharButton)
        wx.EVT_CHAR(self.moreButton, self.OnCharButton)
        wx.EVT_CHAR(self.matchWholeCb, self.OnCharMisc)
        wx.EVT_CHAR(self.matchCaseCb, self.OnCharMisc)
        wx.EVT_CHAR(self.direction, self.OnCharMisc)
        wx.EVT_CHAR(self.elements, self.OnCharMisc)

        util.finishWindow(self, hsizer, center=False)

        self.loadState()
        self.findEntry.SetFocus()
Exemplo n.º 7
0
    def __init__(self, parent, ctrl):
        wx.Dialog.__init__(self,
                           parent,
                           -1,
                           "Find & Replace",
                           style=wx.DEFAULT_DIALOG_STYLE | wx.WANTS_CHARS)

        self.ctrl = ctrl

        self.searchLine = -1
        self.searchColumn = -1
        self.searchWidth = -1

        hsizer = wx.BoxSizer(wx.HORIZONTAL)

        vsizer = wx.BoxSizer(wx.VERTICAL)

        gsizer = wx.FlexGridSizer(2, 2, 5, 20)
        gsizer.AddGrowableCol(1)

        gsizer.Add(wx.StaticText(self, -1, "Find what:"), 0,
                   wx.ALIGN_CENTER_VERTICAL)
        self.findEntry = wx.TextCtrl(self, -1, style=wx.TE_PROCESS_ENTER)
        gsizer.Add(self.findEntry, 0, wx.EXPAND)

        gsizer.Add(wx.StaticText(self, -1, "Replace with:"), 0,
                   wx.ALIGN_CENTER_VERTICAL)
        self.replaceEntry = wx.TextCtrl(self, -1, style=wx.TE_PROCESS_ENTER)
        gsizer.Add(self.replaceEntry, 0, wx.EXPAND)

        vsizer.Add(gsizer, 0, wx.EXPAND | wx.BOTTOM, 10)

        hsizer2 = wx.BoxSizer(wx.HORIZONTAL)

        vsizer2 = wx.BoxSizer(wx.VERTICAL)

        # wxGTK adds way more space by default than wxMSW between the
        # items, have to adjust for that
        pad = 0
        if misc.isWindows:
            pad = 5

        self.matchWholeCb = wx.CheckBox(self, -1, "Match whole word only")
        vsizer2.Add(self.matchWholeCb, 0, wx.TOP, pad)

        self.matchCaseCb = wx.CheckBox(self, -1, "Match case")
        vsizer2.Add(self.matchCaseCb, 0, wx.TOP, pad)

        hsizer2.Add(vsizer2, 0, wx.EXPAND | wx.RIGHT, 10)

        self.direction = wx.RadioBox(self,
                                     -1,
                                     "Direction",
                                     choices=["Up", "Down"])
        self.direction.SetSelection(1)

        hsizer2.Add(self.direction, 1, 0)

        vsizer.Add(hsizer2, 0, wx.EXPAND | wx.BOTTOM, 10)

        self.extraLabel = wx.StaticText(self, -1, "Search in:")
        vsizer.Add(self.extraLabel)

        self.elements = wx.CheckListBox(self, -1)

        # sucky wxMSW doesn't support client data for checklistbox items,
        # so we have to store it ourselves
        self.elementTypes = []

        for t in config.getTIs():
            self.elements.Append(t.name)
            self.elementTypes.append(t.lt)

        vsizer.Add(self.elements, 1, wx.EXPAND)

        hsizer.Add(vsizer, 1, wx.EXPAND)

        vsizer = wx.BoxSizer(wx.VERTICAL)

        find = wx.Button(self, -1, "&Find next")
        vsizer.Add(find, 0, wx.EXPAND | wx.BOTTOM, 5)

        replace = wx.Button(self, -1, "&Replace")
        vsizer.Add(replace, 0, wx.EXPAND | wx.BOTTOM, 5)

        replaceAll = wx.Button(self, -1, " Replace all ")
        vsizer.Add(replaceAll, 0, wx.EXPAND | wx.BOTTOM, 5)

        self.moreButton = wx.Button(self, -1, "")
        vsizer.Add(self.moreButton, 0, wx.EXPAND | wx.BOTTOM, 5)

        hsizer.Add(vsizer, 0, wx.EXPAND | wx.LEFT, 30)

        wx.EVT_BUTTON(self, find.GetId(), self.OnFind)
        wx.EVT_BUTTON(self, replace.GetId(), self.OnReplace)
        wx.EVT_BUTTON(self, replaceAll.GetId(), self.OnReplaceAll)
        wx.EVT_BUTTON(self, self.moreButton.GetId(), self.OnMore)

        gutil.btnDblClick(find, self.OnFind)
        gutil.btnDblClick(replace, self.OnReplace)

        wx.EVT_TEXT(self, self.findEntry.GetId(), self.OnText)

        wx.EVT_TEXT_ENTER(self, self.findEntry.GetId(), self.OnFind)
        wx.EVT_TEXT_ENTER(self, self.replaceEntry.GetId(), self.OnFind)

        wx.EVT_CHAR(self, self.OnCharMisc)
        wx.EVT_CHAR(self.findEntry, self.OnCharEntry)
        wx.EVT_CHAR(self.replaceEntry, self.OnCharEntry)
        wx.EVT_CHAR(find, self.OnCharButton)
        wx.EVT_CHAR(replace, self.OnCharButton)
        wx.EVT_CHAR(replaceAll, self.OnCharButton)
        wx.EVT_CHAR(self.moreButton, self.OnCharButton)
        wx.EVT_CHAR(self.matchWholeCb, self.OnCharMisc)
        wx.EVT_CHAR(self.matchCaseCb, self.OnCharMisc)
        wx.EVT_CHAR(self.direction, self.OnCharMisc)
        wx.EVT_CHAR(self.elements, self.OnCharMisc)

        util.finishWindow(self, hsizer, center=False)

        self.loadState()
        self.findEntry.SetFocus()
Exemplo n.º 8
0
    def __init__(self, parent, id, cfg):
        wx.Panel.__init__(self, parent, id)
        self.cfg = cfg

        vsizer = wx.BoxSizer(wx.VERTICAL)

        hsizer = wx.BoxSizer(wx.HORIZONTAL)

        hsizer.Add(wx.StaticText(self, -1, "Element:"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)

        self.elementsCombo = wx.ComboBox(self, -1, style=wx.CB_READONLY)

        for t in config.getTIs():
            self.elementsCombo.Append(t.name, t.lt)

        hsizer.Add(self.elementsCombo, 0)

        vsizer.Add(hsizer, 0, wx.EXPAND)

        vsizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 10)

        hsizer = wx.BoxSizer(wx.HORIZONTAL)

        hsizer.Add(self.addTextStyles("Screen", "screen", self))
        hsizer.Add(self.addTextStyles("Print", "export", self), 0, wx.LEFT, 10)

        vsizer.Add(hsizer, 0, wx.BOTTOM, 10)

        gsizer = wx.FlexGridSizer(2, 2, 5, 0)

        gsizer.Add(wx.StaticText(self, -1, "Empty lines / 10 before:"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)

        tmp = wx.SpinCtrl(self, -1)
        tmp.SetRange(*self.cfg.getType(screenplay.ACTION).cvars.getMinMax("beforeSpacing"))
        wx.EVT_SPINCTRL(self, tmp.GetId(), self.OnMisc)
        wx.EVT_KILL_FOCUS(tmp, self.OnKillFocus)
        gsizer.Add(tmp)
        self.beforeSpacingEntry = tmp

        gsizer.Add(wx.StaticText(self, -1, "Empty lines / 10 between:"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)

        tmp = wx.SpinCtrl(self, -1)
        tmp.SetRange(*self.cfg.getType(screenplay.ACTION).cvars.getMinMax("intraSpacing"))
        wx.EVT_SPINCTRL(self, tmp.GetId(), self.OnMisc)
        wx.EVT_KILL_FOCUS(tmp, self.OnKillFocus)
        gsizer.Add(tmp)
        self.intraSpacingEntry = tmp

        vsizer.Add(gsizer, 0, wx.BOTTOM, 20)

        gsizer = wx.FlexGridSizer(2, 3, 5, 0)

        gsizer.Add(wx.StaticText(self, -1, "Indent:"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)

        self.indentEntry = wx.SpinCtrl(self, -1)
        self.indentEntry.SetRange(*self.cfg.getType(screenplay.ACTION).cvars.getMinMax("indent"))
        wx.EVT_SPINCTRL(self, self.indentEntry.GetId(), self.OnMisc)
        wx.EVT_KILL_FOCUS(self.indentEntry, self.OnKillFocus)
        gsizer.Add(self.indentEntry, 0)

        gsizer.Add(
            wx.StaticText(self, -1, "characters (10 characters" " = 1 inch)"), 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 10
        )

        gsizer.Add(wx.StaticText(self, -1, "Width:"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)

        self.widthEntry = wx.SpinCtrl(self, -1)
        self.widthEntry.SetRange(*self.cfg.getType(screenplay.ACTION).cvars.getMinMax("width"))
        wx.EVT_SPINCTRL(self, self.widthEntry.GetId(), self.OnMisc)
        wx.EVT_KILL_FOCUS(self.widthEntry, self.OnKillFocus)
        gsizer.Add(self.widthEntry, 0)

        gsizer.Add(
            wx.StaticText(self, -1, "characters (10 characters" " = 1 inch)"), 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 10
        )

        vsizer.Add(gsizer, 0, wx.BOTTOM, 20)

        util.finishWindow(self, vsizer, center=False)

        wx.EVT_COMBOBOX(self, self.elementsCombo.GetId(), self.OnElementCombo)

        self.elementsCombo.SetSelection(0)
        self.OnElementCombo()
Exemplo n.º 9
0
    def generate(self):
        tf = pml.TextFormatter(self.sp.cfg.paperWidth,
                               self.sp.cfg.paperHeight, 15.0, 12)

        ls = self.sp.lines

        total = len(ls)
        tf.addText("%5d Lines in Screenplay" % total)

        tf.addSpace(2.0)

        for t in config.getTIs():
            cnt = sum([1 for line in ls if line.lt == t.lt])
            tf.addText("        %13s  %4d (%d%%)" % (t.name, cnt,
                                                      util.pct(cnt, total)))

        tf.addSpace(4.0)

        intLines = sum([si.lines for si in self.sr.scenes if
                        util.upper(si.name).startswith("INT.")])
        extLines = sum([si.lines for si in self.sr.scenes if
                        util.upper(si.name).startswith("EXT.")])

        tf.addText("%d%% Interior / %d%% Exterior Scenes" % (
            util.pct(intLines, intLines + extLines),
            util.pct(extLines, intLines + extLines)))

        tf.addSpace(4.0)

        tf.addText("Scene Length in Lines: %d Max / %.2f Avg." % (
            self.sr.longestScene, self.sr.avgScene))

        # lengths of action elements
        actions = []

        # length of current action element
        curLen = 0

        for ln in ls:
            if curLen > 0:
                if ln.lt == screenplay.ACTION:
                    curLen += 1

                    if ln.lb == screenplay.LB_LAST:
                        actions.append(curLen)
                        curLen = 0
                else:
                    actions.append(curLen)
                    curLen = 0
            else:
                if ln.lt == screenplay.ACTION:
                    curLen = 1

        if curLen > 0:
            actions.append(curLen)

        tf.addSpace(4.0)

        # avoid divide-by-zero
        if len(actions) > 0:
            maxA = max(actions)
            avgA = sum(actions) / float(len(actions))
        else:
            maxA = 0
            avgA = 0.0

        tf.addText("Action Length in Lines: %d Max / %.2f Avg." % (
            maxA, avgA))

        tf.addSpace(4.0)

        tf.addText("%d Speaking Characters" % len(self.cr.cinfo))

        return pdf.generate(tf.doc)