def addElem(eleType, items): # if elem ends in a newline, last line is empty and useless; # get rid of it if not items[-1] and (len(items) > 1): items = items[:-1] for s in items[:-1]: lines.append( screenplay.Line(screenplay.LB_FORCED, eleType, util.cleanInput(s))) lines.append( screenplay.Line(screenplay.LB_LAST, eleType, util.cleanInput(items[-1])))
def addElem(eleType, eleText): lns = eleText.split("\n") # if elem ends in a newline, last line is empty and useless; # get rid of it if not lns[-1] and (len(lns) > 1): lns = lns[:-1] for s in lns[:-1]: lines.append( screenplay.Line(screenplay.LB_FORCED, eleType, util.cleanInput(s))) lines.append( screenplay.Line(screenplay.LB_LAST, eleType, util.cleanInput(lns[-1])))
def testPasteAfterForcedLineBreak(): sp = u.new() sp.cmd("addChar", char = "E") assert sp.lines[0].lt != scr.CHARACTER sp.cmd("insertForcedLineBreak") sp.paste([scr.Line(text = "Tsashkataar", lt = scr.CHARACTER)]) assert len(sp.lines) == 2 assert (sp.line == 1) and (sp.column == 11) assert sp.lines[0].text == "E" assert sp.lines[0].lt == scr.SCENE assert sp.lines[0].lb == scr.LB_FORCED assert sp.lines[1].text == "Tsashkataar" assert sp.lines[1].lt == scr.SCENE assert sp.lines[1].lb == scr.LB_LAST sp._validate()
def importTextFile(fileName, frame): # the 1 MB limit is arbitrary, we just want to avoid getting a # MemoryError exception for /dev/zero etc. data = util.loadFile(fileName, frame, 1000000) if data == None: return None if len(data) == 0: wx.MessageBox("File is empty.", "Error", wx.OK, frame) return None data = util.fixNL(data) lines = data.split("\n") tabWidth = 4 # key = indent level, value = Indent indDict = {} for i in range(len(lines)): s = util.toInputStr(lines[i].rstrip().expandtabs(tabWidth)) # don't count empty lines towards indentation statistics if s.strip() == "": lines[i] = "" continue cnt = util.countInitial(s, " ") ind = indDict.get(cnt) if not ind: ind = Indent(cnt) indDict[cnt] = ind tmp = s.upper() if util.multiFind(tmp, ["EXT.", "INT."]): ind.sceneStart += 1 if util.multiFind(tmp, ["CUT TO:", "DISSOLVE TO:"]): ind.trans += 1 if re.match(r"^ +\(.*\)$", tmp): ind.paren += 1 ind.lines.append(s.lstrip()) lines[i] = s if len(indDict) == 0: wx.MessageBox("File contains only empty lines.", "Error", wx.OK, frame) return None # scene/action indent setType(SCENE_ACTION, indDict, lambda v: v.sceneStart) # indent with most lines is dialogue in non-pure-action scripts setType(screenplay.DIALOGUE, indDict, lambda v: len(v.lines)) # remaining indent with lines is character most likely setType(screenplay.CHARACTER, indDict, lambda v: len(v.lines)) # transitions setType(screenplay.TRANSITION, indDict, lambda v: v.trans) # parentheticals setType(screenplay.PAREN, indDict, lambda v: v.paren) # some text files have this type of parens: # # JOE # (smiling and # hopping along) # # this handles them. parenIndent = findIndent(indDict, lambda v: v.lt == screenplay.PAREN) if parenIndent != -1: paren2Indent = findIndent( indDict, lambda v, var: (v.lt == -1) and (v.indent == var), parenIndent + 1) if paren2Indent != -1: indDict[paren2Indent].lt = screenplay.PAREN # set line type to ACTION for any indents not recognized for v in indDict.itervalues(): if v.lt == -1: v.lt = screenplay.ACTION dlg = ImportDlg(frame, indDict.values()) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return None dlg.Destroy() ret = [] for i in range(len(lines)): s = lines[i] cnt = util.countInitial(s, " ") s = s.lstrip() sUp = s.upper() if s: lt = indDict[cnt].lt if lt == IGNORE: continue if lt == SCENE_ACTION: if s.startswith("EXT.") or s.startswith("INT."): lt = screenplay.SCENE else: lt = screenplay.ACTION if ret and (ret[-1].lt != lt): ret[-1].lb = screenplay.LB_LAST if lt == screenplay.CHARACTER: if sUp.endswith("(CONT'D)"): s = sUp[:-8].rstrip() elif lt == screenplay.PAREN: if s == "(continuing)": s = "" if s: line = screenplay.Line(screenplay.LB_SPACE, lt, s) ret.append(line) elif ret: ret[-1].lb = screenplay.LB_LAST if len(ret) == 0: ret.append(screenplay.Line(screenplay.LB_LAST, screenplay.ACTION)) # make sure the last line ends an element ret[-1].lb = screenplay.LB_LAST return ret
def importFountain(fileName, frame): # regular expressions for fountain markdown. # https://github.com/vilcans/screenplain/blob/master/screenplain/richstring.py ire = re.compile( # one star r'\*' # anything but a space, then text r'([^\s].*?)' # finishing with one star r'\*' # must not be followed by star r'(?!\*)') bre = re.compile( # two stars r'\*\*' # must not be followed by space r'(?=\S)' # inside text r'(.+?[*_]*)' # finishing with two stars r'(?<=\S)\*\*') ure = re.compile( # underline r'_' # must not be followed by space r'(?=\S)' # inside text r'([^_]+)' # finishing with underline r'(?<=\S)_') boneyard_re = re.compile('/\\*.*?\\*/', flags=re.DOTALL) # random magicstring used to escape literal star '\*' literalstar = "Aq7RR" # returns s with markdown formatting removed. def unmarkdown(s): s = s.replace("\\*", literalstar) for style in (bre, ire, ure): s = style.sub(r'\1', s) return s.replace(literalstar, "*") data = util.loadFile(fileName, frame, 1000000) if data == None: return None if len(data) == 0: wx.MessageBox("File is empty.", "Error", wx.OK, frame) return None inf = [] inf.append(misc.CheckBoxItem("Import titles as action lines.")) inf.append(misc.CheckBoxItem("Remove unsupported formatting markup.")) inf.append(misc.CheckBoxItem("Import section/synopsis as notes.")) dlg = misc.CheckBoxDlg(frame, "Fountain import options", inf, "Import options:", False) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return None importTitles = inf[0].selected removeMarkdown = inf[1].selected importSectSyn = inf[2].selected # pre-process data - fix newlines, remove boneyard. data = util.fixNL(data) data = boneyard_re.sub('', data) prelines = data.split("\n") for i in xrange(len(prelines)): try: util.toLatin1(prelines[i]) except: prelines[i] = util.cleanInput( u"" + prelines[i].decode('UTF-8', "ignore")) lines = [] tabWidth = 4 lns = [] sceneStartsList = ("INT", "EXT", "EST", "INT./EXT", "INT/EXT", "I/E", "I./E") TWOSPACE = " " skipone = False # First check if title lines are present: c = 0 while c < len(prelines): if prelines[c] != "": c = c + 1 else: break # prelines[0:i] are the first bunch of lines, that could be titles. # Our check for title is simple: # - the line does not start with 'fade' # - the first line has a single ':' if c > 0: l = util.toInputStr(prelines[0].expandtabs(tabWidth).lstrip().lower()) if not l.startswith("fade") and l.count(":") == 1: # these are title lines. Now do what the user requested. if importTitles: # add TWOSPACE to all the title lines. for i in xrange(c): prelines[i] += TWOSPACE else: #remove these lines prelines = prelines[c + 1:] for l in prelines: if l != TWOSPACE: lines.append(util.toInputStr(l.expandtabs(tabWidth))) else: lines.append(TWOSPACE) linesLen = len(lines) def isPrevEmpty(): if lns and lns[-1].text == "": return True return False def isPrevType(ltype): return (lns and lns[-1].lt == ltype) # looks ahead to check if next line is not empty def isNextEmpty(i): return (i + 1 < len(lines) and lines[i + 1] == "") def getPrevType(): if lns: return lns[-1].lt else: return screenplay.ACTION def isParen(s): return (s.startswith('(') and s.endswith(')')) def isScene(s): if s.endswith(TWOSPACE): return False if s.startswith(".") and not s.startswith(".."): return True tmp = s.upper() if (re.match(r'^(INT|EXT|EST)[ .]', tmp) or re.match(r'^(INT\.?/EXT\.?)[ .]', tmp) or re.match(r'^I/E[ .]', tmp)): return True return False def isTransition(s): return ((s.isupper() and s.endswith("TO:")) or (s.startswith(">") and not s.endswith("<"))) def isCentered(s): return s.startswith(">") and s.endswith("<") def isPageBreak(s): return s.startswith('===') and s.lstrip('=') == '' def isNote(s): return s.startswith("[[") and s.endswith("]]") def isSection(s): return s.startswith("#") def isSynopsis(s): return s.startswith("=") and not s.startswith("==") # first pass - identify linetypes for i in range(linesLen): if skipone: skipone = False continue s = lines[i] sl = s.lstrip() # mark as ACTION by default. line = screenplay.Line(screenplay.LB_FORCED, screenplay.ACTION, s) # Start testing lines for element type. Go in order: # Scene Character, Paren, Dialog, Transition, Note. if s == "" or isCentered(s) or isPageBreak(s): # do nothing - import as action. pass elif s == TWOSPACE: line.lt = getPrevType() elif isScene(s): line.lt = screenplay.SCENE if sl.startswith('.'): line.text = sl[1:] else: line.text = sl elif isTransition(sl) and isPrevEmpty() and isNextEmpty(i): line.lt = screenplay.TRANSITION if line.text.startswith('>'): line.text = sl[1:].lstrip() elif s.isupper() and isPrevEmpty() and not isNextEmpty(i): line.lt = screenplay.CHARACTER if s.endswith(TWOSPACE): line.lt = screenplay.ACTION elif isParen(sl) and (isPrevType(screenplay.CHARACTER) or isPrevType(screenplay.DIALOGUE)): line.lt = screenplay.PAREN elif (isPrevType(screenplay.CHARACTER) or isPrevType(screenplay.DIALOGUE) or isPrevType(screenplay.PAREN)): line.lt = screenplay.DIALOGUE elif isNote(sl): line.lt = screenplay.NOTE line.text = sl.strip('[]') elif isSection(s) or isSynopsis(s): if not importSectSyn: if isNextEmpty(i): skipone = True continue line.lt = screenplay.NOTE line.text = sl.lstrip('=#') if line.text == TWOSPACE: pass elif line.lt != screenplay.ACTION: line.text = line.text.lstrip() else: tmp = line.text.rstrip() # we don't support center align, so simply add required indent. if isCentered(tmp): tmp = tmp[1:-1].strip() width = frame.panel.ctrl.sp.cfg.getType( screenplay.ACTION).width if len(tmp) < width: tmp = ' ' * ((width - len(tmp)) // 2) + tmp line.text = tmp if removeMarkdown: line.text = unmarkdown(line.text) if line.lt == screenplay.CHARACTER and line.text.endswith('^'): line.text = line.text[:-1] lns.append(line) ret = [] # second pass helper functions. def isLastLBForced(): return ret and ret[-1].lb == screenplay.LB_FORCED def makeLastLBLast(): if ret: ret[-1].lb = screenplay.LB_LAST def isRetPrevType(t): return ret and ret[-1].lt == t # second pass - remove unneeded empty lines, and fix the linebreaks. for ln in lns: if ln.text == '': if isLastLBForced(): makeLastLBLast() else: ret.append(ln) elif not isRetPrevType(ln.lt): makeLastLBLast() ret.append(ln) else: ret.append(ln) makeLastLBLast() return ret
def __init__(self, sp): Base.__init__(self, sp, CMD_MISC) self.linesBefore = [ screenplay.Line(ln.lb, ln.lt, ln.text) for ln in sp.lines ]
def testPaste(): sp = u.load() sp.paste([scr.Line(text="yo")]) assert sp.isModified()