Last active
November 9, 2019 01:19
-
-
Save tin2tin/98e83093cdffbb1da71c455f2b564846 to your computer and use it in GitHub Desktop.
Fountain_in_Text_Editor - work in progress
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import bpy | |
import textwrap | |
import os | |
import sys | |
import fountain | |
from bpy.props import BoolProperty, PointerProperty, StringProperty | |
from pathlib import Path | |
class FOUNTAIN_PT_panel(bpy.types.Panel): | |
"""Preview fountain script as formatted screenplay""" | |
bl_label = "Fountain" | |
bl_space_type = 'TEXT_EDITOR' | |
bl_region_type = 'UI' | |
bl_category = "Text" | |
def draw(self, context): | |
layout = self.layout | |
layout.operator("scene.preview_fountain") | |
layout.operator("areatype.trimview") | |
repl = context.scene.text_replace | |
layout.prop(repl, "enabled") | |
#print(bpy.context.space_data.type) | |
class FOUNTAIN_OT_preview_fountain(bpy.types.Operator): | |
bl_idname = "scene.preview_fountain" | |
bl_label = "Preview Screenplay" | |
# @classmethod | |
# def poll(self, context): | |
# if bpy.context.space_data.type == 'TEXT_EDITOR': | |
# suffix = Path(bpy.context.space_data.text.filepath).suffix | |
# else: | |
# suffix = "" | |
# return suffix == ".fountain" | |
def execute(self, context): | |
if bpy.context.space_data.type == 'TEXT_EDITOR': | |
suffix = Path(bpy.context.space_data.text.filepath).suffix | |
else: | |
suffix = "" | |
if suffix != ".fountain": | |
return {"CANCELLED"} | |
# dir = os.path.dirname(bpy.data.filepath) | |
# if not dir in sys.path: | |
# sys.path.append(dir) | |
fountain_script = bpy.context.area.spaces.active.text.as_string()#context.scene.fountain.get_body() | |
F = fountain.Fountain(fountain_script) | |
# if 'title' in F.metadata: | |
# file_name = F.metadata['title'][0] | |
# else: | |
file_name = "Fountain" | |
#filename = "Preview_"+file_name[:-4]+".txt" | |
filename = "Preview"+".txt" | |
#print(filename) | |
if filename not in bpy.data.texts: | |
bpy.data.texts.new(filename) # New document in Text Editor | |
else: | |
bpy.data.texts[filename].clear() # Clear existing text | |
action_wrapper = textwrap.TextWrapper(width=60) | |
dialogue_wrapper = textwrap.TextWrapper(width=37) | |
for fc, f in enumerate(F.elements): | |
if f.element_type == 'Scene Heading': | |
bpy.data.texts[filename].write(f.element_text+chr(10)) | |
if f.element_type == 'Action': | |
action = f.element_text | |
action_list = action_wrapper.wrap(text=action) | |
for element in action_list: | |
bpy.data.texts[filename].write(element+chr(10)) | |
#bpy.data.texts[filename].write(chr(10)) | |
if f.element_type == 'Character': | |
bpy.data.texts[filename].write(chr(10)+(f.element_text).center(60)+chr(10)) | |
if f.element_type == 'Parenthetical': | |
bpy.data.texts[filename].write((f.element_text).center(60)+chr(10)) | |
if f.element_type == 'Dialogue': | |
dialogue = f.element_text | |
line_list = dialogue_wrapper.wrap(text=dialogue) | |
for element in line_list: | |
bpy.data.texts[filename].write((" "+element)+chr(10)) # 13 characters | |
elif f.element_type == 'Synopsis': | |
continue | |
elif f.element_type == 'Page Break': | |
continue | |
elif f.element_type == 'Boneyard': | |
continue | |
elif f.element_type == 'Comment': | |
continue | |
elif f.element_type == 'Section Heading': | |
continue | |
elif f.element_type == 'Transition': | |
bpy.data.texts[filename].write(f.element_text.rjust(60)+chr(10)) | |
elif f.element_type == 'Empty Line': | |
bpy.data.texts[filename].write(chr(10)+chr(10)) | |
return {"FINISHED"} | |
class AREATYPE_OT_trim(bpy.types.Operator): | |
bl_idname = "areatype.trimview" | |
bl_label = "Dual View" | |
original_area = None | |
def execute(self,context): | |
self.original_area = context.area | |
original = context.copy() | |
thisarea = context.area | |
otherarea = None | |
tgxvalue = thisarea.x + thisarea.width + 1 | |
thistype = context.area.type | |
arealist = list(context.screen.areas) | |
for area in context.screen.areas: | |
if area == thisarea: | |
continue | |
elif area.x == tgxvalue and area.y == thisarea.y: | |
otherarea = area | |
break | |
if otherarea: #leave trim-mode | |
# print("this x: "+str(thisarea.x)) #min_x = left x of left window | |
# print("this y: "+str(thisarea.y)) #min_y = mouse position, starting from window bottom y | |
# print("other x: "+str(otherarea.x)) #max_x = x between left and right window | |
# print("other y: "+str(otherarea.y)) #max_y = mouse position, starting from window bottom y | |
#join | |
#bpy.ops.screen.area_join(min_x=thisarea.x, min_y=thisarea.y, max_x=otherarea.x, max_y=otherarea.y) #broken | |
# normal settings | |
#bpy.ops.screen.screen_full_area() | |
#bpy.ops.screen.screen_full_area() | |
override = context.copy() | |
area = self.original_area | |
override['area'] = area | |
override['space_data'] = area.spaces.active | |
# for region in area.regions: | |
# if region.type == 'PREVIEW': | |
# break | |
# override['region'] = region | |
return {"FINISHED"} | |
else: # enter dual-mode | |
areax = None | |
#split | |
bpy.ops.screen.area_split(direction="VERTICAL") | |
#settings for preview 2. | |
# fit 1. preview to window | |
bpy.ops.screen.screen_full_area() | |
bpy.ops.screen.screen_full_area() | |
override = original | |
area = self.original_area | |
override['area'] = area | |
override['space_data'] = area.spaces.active | |
for area in context.screen.areas: | |
if area not in arealist: | |
areax = area | |
break | |
# for area in reversed(context.screen.areas): | |
# if area.type == "TEXT_EDITOR": | |
# suffix = Path(bpy.context.space_data.text.filepath).suffix | |
# if suffix == ".fountain": | |
# area.space_data.text = bpy.data.texts['Preview.txt'] | |
# break | |
## else: | |
## raise RuntimeError("Nothing found") | |
# bpy.context.space_data.text = bpy.data.texts['Preview.txt'] | |
#print(bpy.context.space_data.text.filepath) | |
if areax: | |
areax.type = thistype | |
return {"FINISHED"} | |
#bpy.context.space_data.text = bpy.data.texts['Preview.txt'] | |
return {"CANCELLED"} | |
handler = None | |
last = None | |
last_index = None | |
def get_space(context): | |
for area in context.screen.areas: | |
if area.type == "TEXT_EDITOR": | |
return area.spaces.active | |
def text_handler(spc): | |
global last, last_index | |
text = spc.text | |
if not text: | |
return | |
line = text.current_line.body | |
if last is None or last_index != text.current_line_index: | |
last = line | |
last_index = text.current_line_index | |
return | |
if line != last and len(line)>len(last): | |
bpy.ops.scene.preview_fountain() | |
last = line | |
def activate_handler(self, context): | |
global handler | |
spc = get_space(context) | |
if not spc: | |
return | |
enabled = context.scene.text_replace.enabled | |
if enabled: | |
handler = spc.draw_handler_add(text_handler, (spc,), "WINDOW", "POST_PIXEL") | |
print("handler activated", handler) | |
else: | |
if handler is not None: | |
spc.draw_handler_remove(handler, "WINDOW") | |
handler = None | |
print("handler deactivated") | |
class TextReplaceProperties(bpy.types.PropertyGroup): | |
enabled: BoolProperty(name="Live Preview", description="Enables live screenplay preview", update=activate_handler) | |
bpy.utils.register_class(TextReplaceProperties) | |
bpy.types.Scene.text_replace = PointerProperty(type=TextReplaceProperties) | |
def register(): | |
bpy.utils.register_class(FOUNTAIN_PT_panel) | |
bpy.utils.register_class(FOUNTAIN_OT_preview_fountain) | |
bpy.utils.register_class(AREATYPE_OT_trim) | |
def unregister(): | |
bpy.utils.unregister_class(FOUNTAIN_PT_panel) | |
bpy.utils.unregister_class(FOUNTAIN_OT_preview_fountain) | |
bpy.utils.unregister_class(AREATYPE_OT_trim) | |
if __name__ == "__main__": | |
register() | |
#unregister() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment